authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-05 17:29:26+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-05 17:29:26+01:00
logd05db526168351ae9250657725cc126625262fa6
tree37495ecbf8a21f639111eb77cea0dd8c0e20926d
parentab423bd63cd0c61e7299dc0b73cba4d83dffdd38

elf: copy out committed ZigObject to a buffer when creating static lib


4 files changed, 79 insertions(+), 28 deletions(-)

src/link/Elf.zig+10-6
......@@ -1344,23 +1344,25 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
13441344 try zig_object.addAtomsToRelaSections(self);
13451345 try self.updateSectionSizesObject();
13461346
1347 try self.allocateAllocSectionsObject();
13471348 try self.allocateNonAllocSections();
13481349
13491350 if (build_options.enable_logging) {
1350 state_log.debug("{}", .{self.dumpState()});
1351 log.debug("{}", .{self.dumpState()});
13511352 }
13521353
13531354 try self.writeSyntheticSectionsObject();
13541355 try self.writeShdrTable();
13551356 try self.writeElfHeader();
1357
1358 // TODO we can avoid reading in the file contents we just wrote if we give the linker
1359 // ability to write directly to a buffer.
1360 try zig_object.readFileContents(self);
13561361 }
13571362
13581363 var files = std.ArrayList(File.Index).init(gpa);
13591364 defer files.deinit();
13601365 try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
1361 // Note to self: we currently must have ZigObject written out first as we write the object
1362 // file into the same file descriptor and then re-read its contents.
1363 // TODO implement writing ZigObject to a buffer instead of file.
13641366 if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
13651367 for (self.objects.items) |index| files.appendAssumeCapacity(index);
13661368
......@@ -1381,7 +1383,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
13811383 for (files.items) |index| {
13821384 const file_ptr = self.file(index).?;
13831385 try file_ptr.updateArStrtab(gpa, &ar_strtab);
1384 file_ptr.updateArSize(self);
1386 file_ptr.updateArSize();
13851387 }
13861388
13871389 // Update file offsets of contributing objects.
......@@ -1433,7 +1435,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
14331435 // Write object files
14341436 for (files.items) |index| {
14351437 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1436 try self.file(index).?.writeAr(self, buffer.writer());
1438 try self.file(index).?.writeAr(buffer.writer());
14371439 }
14381440
14391441 assert(buffer.items.len == total_size);
......@@ -2970,6 +2972,7 @@ fn writeShdrTable(self: *Elf) !void {
29702972 defer gpa.free(buf);
29712973
29722974 for (buf, 0..) |*shdr, i| {
2975 assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
29732976 shdr.* = shdrTo32(self.shdrs.items[i]);
29742977 if (foreign_endian) {
29752978 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
......@@ -2982,6 +2985,7 @@ fn writeShdrTable(self: *Elf) !void {
29822985 defer gpa.free(buf);
29832986
29842987 for (buf, 0..) |*shdr, i| {
2988 assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
29852989 shdr.* = self.shdrs.items[i];
29862990 if (foreign_endian) {
29872991 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
src/link/Elf/ZigObject.zig+28-18
......@@ -3,6 +3,7 @@
33//! and any relocations that may have been emitted.
44//! Think about this as fake in-memory Object file for the Zig module.
55
6data: std.ArrayListUnmanaged(u8) = .{},
67path: []const u8,
78index: File.Index,
89
......@@ -101,6 +102,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
101102}
102103
103104pub fn deinit(self: *ZigObject, allocator: Allocator) void {
105 self.data.deinit(allocator);
104106 allocator.free(self.path);
105107 self.local_esyms.deinit(allocator);
106108 self.global_esyms.deinit(allocator);
......@@ -441,6 +443,27 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
441443 }
442444}
443445
446/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
447/// We need this so that we can write to an archive.
448/// TODO implement writing ZigObject data directly to a buffer instead.
449pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void {
450 const gpa = elf_file.base.allocator;
451 const shsize: u64 = switch (elf_file.ptr_width) {
452 .p32 => @sizeOf(elf.Elf32_Shdr),
453 .p64 => @sizeOf(elf.Elf64_Shdr),
454 };
455 var end_pos: u64 = elf_file.shdr_table_offset.? + elf_file.shdrs.items.len * shsize;
456 for (elf_file.shdrs.items) |shdr| {
457 if (shdr.sh_type == elf.SHT_NOBITS) continue;
458 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
459 }
460 const size = std.math.cast(usize, end_pos) orelse return error.Overflow;
461 try self.data.resize(gpa, size);
462
463 const amt = try elf_file.base.file.?.preadAll(self.data.items, 0);
464 if (amt != size) return error.InputOutput;
465}
466
444467pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
445468 const gpa = elf_file.base.allocator;
446469
......@@ -457,34 +480,21 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *
457480 }
458481}
459482
460pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void {
461 var end_pos: u64 = elf_file.shdr_table_offset.?;
462 for (elf_file.shdrs.items) |shdr| {
463 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
464 }
465 self.output_ar_state.size = end_pos;
483pub fn updateArSize(self: *ZigObject) void {
484 self.output_ar_state.size = self.data.items.len;
466485}
467486
468pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void {
469 const gpa = elf_file.base.allocator;
470
471 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
472 const contents = try gpa.alloc(u8, size);
473 defer gpa.free(contents);
474
475 const amt = try elf_file.base.file.?.preadAll(contents, 0);
476 if (amt != self.output_ar_state.size) return error.InputOutput;
477
487pub fn writeAr(self: ZigObject, writer: anytype) !void {
478488 const name = self.path;
479489 const hdr = Archive.setArHdr(.{
480490 .name = if (name.len <= Archive.max_member_name_len)
481491 .{ .name = name }
482492 else
483493 .{ .name_off = self.output_ar_state.name_off },
484 .size = @intCast(size),
494 .size = @intCast(self.data.items.len),
485495 });
486496 try writer.writeAll(mem.asBytes(&hdr));
487 try writer.writeAll(contents);
497 try writer.writeAll(self.data.items);
488498}
489499
490500pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {
src/link/Elf/file.zig+4-4
......@@ -162,17 +162,17 @@ pub const File = union(enum) {
162162 state.name_off = try ar_strtab.insert(allocator, path);
163163 }
164164
165 pub fn updateArSize(file: File, elf_file: *Elf) void {
165 pub fn updateArSize(file: File) void {
166166 return switch (file) {
167 .zig_object => |x| x.updateArSize(elf_file),
167 .zig_object => |x| x.updateArSize(),
168168 .object => |x| x.updateArSize(),
169169 inline else => unreachable,
170170 };
171171 }
172172
173 pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {
173 pub fn writeAr(file: File, writer: anytype) !void {
174174 return switch (file) {
175 .zig_object => |x| x.writeAr(elf_file, writer),
175 .zig_object => |x| x.writeAr(writer),
176176 .object => |x| x.writeAr(writer),
177177 inline else => unreachable,
178178 };
test/link/elf.zig+37
......@@ -29,6 +29,7 @@ pub fn testAll(b: *Build) *Step {
2929
3030 // Exercise linker in ar mode
3131 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
32 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target }));
3233
3334 // Exercise linker with self-hosted backend (no LLVM)
3435 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
......@@ -743,6 +744,42 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
743744 return test_step;
744745}
745746
747fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
748 const test_step = addTestStep(b, "emit-static-lib-zig", opts);
749
750 const obj1 = addObject(b, "obj1", opts);
751 addZigSourceBytes(obj1,
752 \\export var foo: i32 = 42;
753 \\export var bar: i32 = 2;
754 );
755
756 const lib = addStaticLibrary(b, "lib", opts);
757 addZigSourceBytes(lib,
758 \\extern var foo: i32;
759 \\extern var bar: i32;
760 \\export fn fooBar() i32 {
761 \\ return foo + bar;
762 \\}
763 );
764 lib.addObject(obj1);
765
766 const exe = addExecutable(b, "test", opts);
767 addZigSourceBytes(exe,
768 \\const std = @import("std");
769 \\extern fn fooBar() i32;
770 \\pub fn main() void {
771 \\ std.debug.print("{d}", .{fooBar()});
772 \\}
773 );
774 exe.linkLibrary(lib);
775
776 const run = addRunArtifact(exe);
777 run.expectStdErrEqual("44");
778 test_step.dependOn(&run.step);
779
780 return test_step;
781}
782
746783fn testEmptyObject(b: *Build, opts: Options) *Step {
747784 const test_step = addTestStep(b, "empty-object", opts);
748785