elf: copy out committed ZigObject to a buffer when creating static lib
This commit is contained in:
@@ -1344,23 +1344,25 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
|
||||
try zig_object.addAtomsToRelaSections(self);
|
||||
try self.updateSectionSizesObject();
|
||||
|
||||
try self.allocateAllocSectionsObject();
|
||||
try self.allocateNonAllocSections();
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("{}", .{self.dumpState()});
|
||||
log.debug("{}", .{self.dumpState()});
|
||||
}
|
||||
|
||||
try self.writeSyntheticSectionsObject();
|
||||
try self.writeShdrTable();
|
||||
try self.writeElfHeader();
|
||||
|
||||
// TODO we can avoid reading in the file contents we just wrote if we give the linker
|
||||
// ability to write directly to a buffer.
|
||||
try zig_object.readFileContents(self);
|
||||
}
|
||||
|
||||
var files = std.ArrayList(File.Index).init(gpa);
|
||||
defer files.deinit();
|
||||
try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
|
||||
// Note to self: we currently must have ZigObject written out first as we write the object
|
||||
// file into the same file descriptor and then re-read its contents.
|
||||
// TODO implement writing ZigObject to a buffer instead of file.
|
||||
if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
|
||||
for (self.objects.items) |index| files.appendAssumeCapacity(index);
|
||||
|
||||
@@ -1381,7 +1383,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
|
||||
for (files.items) |index| {
|
||||
const file_ptr = self.file(index).?;
|
||||
try file_ptr.updateArStrtab(gpa, &ar_strtab);
|
||||
file_ptr.updateArSize(self);
|
||||
file_ptr.updateArSize();
|
||||
}
|
||||
|
||||
// Update file offsets of contributing objects.
|
||||
@@ -1433,7 +1435,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
|
||||
// Write object files
|
||||
for (files.items) |index| {
|
||||
if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
|
||||
try self.file(index).?.writeAr(self, buffer.writer());
|
||||
try self.file(index).?.writeAr(buffer.writer());
|
||||
}
|
||||
|
||||
assert(buffer.items.len == total_size);
|
||||
@@ -2970,6 +2972,7 @@ fn writeShdrTable(self: *Elf) !void {
|
||||
defer gpa.free(buf);
|
||||
|
||||
for (buf, 0..) |*shdr, i| {
|
||||
assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
|
||||
shdr.* = shdrTo32(self.shdrs.items[i]);
|
||||
if (foreign_endian) {
|
||||
mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
|
||||
@@ -2982,6 +2985,7 @@ fn writeShdrTable(self: *Elf) !void {
|
||||
defer gpa.free(buf);
|
||||
|
||||
for (buf, 0..) |*shdr, i| {
|
||||
assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
|
||||
shdr.* = self.shdrs.items[i];
|
||||
if (foreign_endian) {
|
||||
mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//! and any relocations that may have been emitted.
|
||||
//! Think about this as fake in-memory Object file for the Zig module.
|
||||
|
||||
data: std.ArrayListUnmanaged(u8) = .{},
|
||||
path: []const u8,
|
||||
index: File.Index,
|
||||
|
||||
@@ -101,6 +102,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ZigObject, allocator: Allocator) void {
|
||||
self.data.deinit(allocator);
|
||||
allocator.free(self.path);
|
||||
self.local_esyms.deinit(allocator);
|
||||
self.global_esyms.deinit(allocator);
|
||||
@@ -441,6 +443,27 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
|
||||
/// We need this so that we can write to an archive.
|
||||
/// TODO implement writing ZigObject data directly to a buffer instead.
|
||||
pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const shsize: u64 = switch (elf_file.ptr_width) {
|
||||
.p32 => @sizeOf(elf.Elf32_Shdr),
|
||||
.p64 => @sizeOf(elf.Elf64_Shdr),
|
||||
};
|
||||
var end_pos: u64 = elf_file.shdr_table_offset.? + elf_file.shdrs.items.len * shsize;
|
||||
for (elf_file.shdrs.items) |shdr| {
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
|
||||
}
|
||||
const size = std.math.cast(usize, end_pos) orelse return error.Overflow;
|
||||
try self.data.resize(gpa, size);
|
||||
|
||||
const amt = try elf_file.base.file.?.preadAll(self.data.items, 0);
|
||||
if (amt != size) return error.InputOutput;
|
||||
}
|
||||
|
||||
pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
@@ -457,34 +480,21 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void {
|
||||
var end_pos: u64 = elf_file.shdr_table_offset.?;
|
||||
for (elf_file.shdrs.items) |shdr| {
|
||||
end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
|
||||
}
|
||||
self.output_ar_state.size = end_pos;
|
||||
pub fn updateArSize(self: *ZigObject) void {
|
||||
self.output_ar_state.size = self.data.items.len;
|
||||
}
|
||||
|
||||
pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
|
||||
const contents = try gpa.alloc(u8, size);
|
||||
defer gpa.free(contents);
|
||||
|
||||
const amt = try elf_file.base.file.?.preadAll(contents, 0);
|
||||
if (amt != self.output_ar_state.size) return error.InputOutput;
|
||||
|
||||
pub fn writeAr(self: ZigObject, writer: anytype) !void {
|
||||
const name = self.path;
|
||||
const hdr = Archive.setArHdr(.{
|
||||
.name = if (name.len <= Archive.max_member_name_len)
|
||||
.{ .name = name }
|
||||
else
|
||||
.{ .name_off = self.output_ar_state.name_off },
|
||||
.size = @intCast(size),
|
||||
.size = @intCast(self.data.items.len),
|
||||
});
|
||||
try writer.writeAll(mem.asBytes(&hdr));
|
||||
try writer.writeAll(contents);
|
||||
try writer.writeAll(self.data.items);
|
||||
}
|
||||
|
||||
pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {
|
||||
|
||||
@@ -162,17 +162,17 @@ pub const File = union(enum) {
|
||||
state.name_off = try ar_strtab.insert(allocator, path);
|
||||
}
|
||||
|
||||
pub fn updateArSize(file: File, elf_file: *Elf) void {
|
||||
pub fn updateArSize(file: File) void {
|
||||
return switch (file) {
|
||||
.zig_object => |x| x.updateArSize(elf_file),
|
||||
.zig_object => |x| x.updateArSize(),
|
||||
.object => |x| x.updateArSize(),
|
||||
inline else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {
|
||||
pub fn writeAr(file: File, writer: anytype) !void {
|
||||
return switch (file) {
|
||||
.zig_object => |x| x.writeAr(elf_file, writer),
|
||||
.zig_object => |x| x.writeAr(writer),
|
||||
.object => |x| x.writeAr(writer),
|
||||
inline else => unreachable,
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ pub fn testAll(b: *Build) *Step {
|
||||
|
||||
// Exercise linker in ar mode
|
||||
elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
|
||||
elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target }));
|
||||
|
||||
// Exercise linker with self-hosted backend (no LLVM)
|
||||
elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
|
||||
@@ -743,6 +744,42 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
|
||||
return test_step;
|
||||
}
|
||||
|
||||
fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
|
||||
const test_step = addTestStep(b, "emit-static-lib-zig", opts);
|
||||
|
||||
const obj1 = addObject(b, "obj1", opts);
|
||||
addZigSourceBytes(obj1,
|
||||
\\export var foo: i32 = 42;
|
||||
\\export var bar: i32 = 2;
|
||||
);
|
||||
|
||||
const lib = addStaticLibrary(b, "lib", opts);
|
||||
addZigSourceBytes(lib,
|
||||
\\extern var foo: i32;
|
||||
\\extern var bar: i32;
|
||||
\\export fn fooBar() i32 {
|
||||
\\ return foo + bar;
|
||||
\\}
|
||||
);
|
||||
lib.addObject(obj1);
|
||||
|
||||
const exe = addExecutable(b, "test", opts);
|
||||
addZigSourceBytes(exe,
|
||||
\\const std = @import("std");
|
||||
\\extern fn fooBar() i32;
|
||||
\\pub fn main() void {
|
||||
\\ std.debug.print("{d}", .{fooBar()});
|
||||
\\}
|
||||
);
|
||||
exe.linkLibrary(lib);
|
||||
|
||||
const run = addRunArtifact(exe);
|
||||
run.expectStdErrEqual("44");
|
||||
test_step.dependOn(&run.step);
|
||||
|
||||
return test_step;
|
||||
}
|
||||
|
||||
fn testEmptyObject(b: *Build, opts: Options) *Step {
|
||||
const test_step = addTestStep(b, "empty-object", opts);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user