Commit d05db52616
Changed files (4)
src
link
test
link
src/link/Elf/file.zig
@@ -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,
};
src/link/Elf/ZigObject.zig
@@ -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 {
src/link/Elf.zig
@@ -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);
test/link/elf.zig
@@ -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);