| author | |
| committer | |
| log | d05db526168351ae9250657725cc126625262fa6 |
| tree | 37495ecbf8a21f639111eb77cea0dd8c0e20926d |
| parent | ab423bd63cd0c61e7299dc0b73cba4d83dffdd38 |
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 |
| 1344 | 1344 | try zig_object.addAtomsToRelaSections(self); |
| 1345 | 1345 | try self.updateSectionSizesObject(); |
| 1346 | 1346 | |
| 1347 | try self.allocateAllocSectionsObject(); | |
| 1347 | 1348 | try self.allocateNonAllocSections(); |
| 1348 | 1349 | |
| 1349 | 1350 | if (build_options.enable_logging) { |
| 1350 | state_log.debug("{}", .{self.dumpState()}); | |
| 1351 | log.debug("{}", .{self.dumpState()}); | |
| 1351 | 1352 | } |
| 1352 | 1353 | |
| 1353 | 1354 | try self.writeSyntheticSectionsObject(); |
| 1354 | 1355 | try self.writeShdrTable(); |
| 1355 | 1356 | 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); | |
| 1356 | 1361 | } |
| 1357 | 1362 | |
| 1358 | 1363 | var files = std.ArrayList(File.Index).init(gpa); |
| 1359 | 1364 | defer files.deinit(); |
| 1360 | 1365 | 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. | |
| 1364 | 1366 | if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index); |
| 1365 | 1367 | for (self.objects.items) |index| files.appendAssumeCapacity(index); |
| 1366 | 1368 | |
| ... | ... | @@ -1381,7 +1383,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const |
| 1381 | 1383 | for (files.items) |index| { |
| 1382 | 1384 | const file_ptr = self.file(index).?; |
| 1383 | 1385 | try file_ptr.updateArStrtab(gpa, &ar_strtab); |
| 1384 | file_ptr.updateArSize(self); | |
| 1386 | file_ptr.updateArSize(); | |
| 1385 | 1387 | } |
| 1386 | 1388 | |
| 1387 | 1389 | // Update file offsets of contributing objects. |
| ... | ... | @@ -1433,7 +1435,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const |
| 1433 | 1435 | // Write object files |
| 1434 | 1436 | for (files.items) |index| { |
| 1435 | 1437 | 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()); | |
| 1437 | 1439 | } |
| 1438 | 1440 | |
| 1439 | 1441 | assert(buffer.items.len == total_size); |
| ... | ... | @@ -2970,6 +2972,7 @@ fn writeShdrTable(self: *Elf) !void { |
| 2970 | 2972 | defer gpa.free(buf); |
| 2971 | 2973 | |
| 2972 | 2974 | for (buf, 0..) |*shdr, i| { |
| 2975 | assert(self.shdrs.items[i].sh_offset != math.maxInt(u64)); | |
| 2973 | 2976 | shdr.* = shdrTo32(self.shdrs.items[i]); |
| 2974 | 2977 | if (foreign_endian) { |
| 2975 | 2978 | mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); |
| ... | ... | @@ -2982,6 +2985,7 @@ fn writeShdrTable(self: *Elf) !void { |
| 2982 | 2985 | defer gpa.free(buf); |
| 2983 | 2986 | |
| 2984 | 2987 | for (buf, 0..) |*shdr, i| { |
| 2988 | assert(self.shdrs.items[i].sh_offset != math.maxInt(u64)); | |
| 2985 | 2989 | shdr.* = self.shdrs.items[i]; |
| 2986 | 2990 | if (foreign_endian) { |
| 2987 | 2991 | mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); |
src/link/Elf/ZigObject.zig+28-18| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | //! and any relocations that may have been emitted. |
| 4 | 4 | //! Think about this as fake in-memory Object file for the Zig module. |
| 5 | 5 | |
| 6 | data: std.ArrayListUnmanaged(u8) = .{}, | |
| 6 | 7 | path: []const u8, |
| 7 | 8 | index: File.Index, |
| 8 | 9 | |
| ... | ... | @@ -101,6 +102,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { |
| 101 | 102 | } |
| 102 | 103 | |
| 103 | 104 | pub fn deinit(self: *ZigObject, allocator: Allocator) void { |
| 105 | self.data.deinit(allocator); | |
| 104 | 106 | allocator.free(self.path); |
| 105 | 107 | self.local_esyms.deinit(allocator); |
| 106 | 108 | self.global_esyms.deinit(allocator); |
| ... | ... | @@ -441,6 +443,27 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { |
| 441 | 443 | } |
| 442 | 444 | } |
| 443 | 445 | |
| 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. | |
| 449 | pub 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 | ||
| 444 | 467 | pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { |
| 445 | 468 | const gpa = elf_file.base.allocator; |
| 446 | 469 | |
| ... | ... | @@ -457,34 +480,21 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: * |
| 457 | 480 | } |
| 458 | 481 | } |
| 459 | 482 | |
| 460 | pub 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; | |
| 483 | pub fn updateArSize(self: *ZigObject) void { | |
| 484 | self.output_ar_state.size = self.data.items.len; | |
| 466 | 485 | } |
| 467 | 486 | |
| 468 | pub 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 | ||
| 487 | pub fn writeAr(self: ZigObject, writer: anytype) !void { | |
| 478 | 488 | const name = self.path; |
| 479 | 489 | const hdr = Archive.setArHdr(.{ |
| 480 | 490 | .name = if (name.len <= Archive.max_member_name_len) |
| 481 | 491 | .{ .name = name } |
| 482 | 492 | else |
| 483 | 493 | .{ .name_off = self.output_ar_state.name_off }, |
| 484 | .size = @intCast(size), | |
| 494 | .size = @intCast(self.data.items.len), | |
| 485 | 495 | }); |
| 486 | 496 | try writer.writeAll(mem.asBytes(&hdr)); |
| 487 | try writer.writeAll(contents); | |
| 497 | try writer.writeAll(self.data.items); | |
| 488 | 498 | } |
| 489 | 499 | |
| 490 | 500 | pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void { |
src/link/Elf/file.zig+4-4| ... | ... | @@ -162,17 +162,17 @@ pub const File = union(enum) { |
| 162 | 162 | state.name_off = try ar_strtab.insert(allocator, path); |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | pub fn updateArSize(file: File, elf_file: *Elf) void { | |
| 165 | pub fn updateArSize(file: File) void { | |
| 166 | 166 | return switch (file) { |
| 167 | .zig_object => |x| x.updateArSize(elf_file), | |
| 167 | .zig_object => |x| x.updateArSize(), | |
| 168 | 168 | .object => |x| x.updateArSize(), |
| 169 | 169 | inline else => unreachable, |
| 170 | 170 | }; |
| 171 | 171 | } |
| 172 | 172 | |
| 173 | pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void { | |
| 173 | pub fn writeAr(file: File, writer: anytype) !void { | |
| 174 | 174 | return switch (file) { |
| 175 | .zig_object => |x| x.writeAr(elf_file, writer), | |
| 175 | .zig_object => |x| x.writeAr(writer), | |
| 176 | 176 | .object => |x| x.writeAr(writer), |
| 177 | 177 | inline else => unreachable, |
| 178 | 178 | }; |
test/link/elf.zig+37| ... | ... | @@ -29,6 +29,7 @@ pub fn testAll(b: *Build) *Step { |
| 29 | 29 | |
| 30 | 30 | // Exercise linker in ar mode |
| 31 | 31 | elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target })); |
| 32 | elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target })); | |
| 32 | 33 | |
| 33 | 34 | // Exercise linker with self-hosted backend (no LLVM) |
| 34 | 35 | elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); |
| ... | ... | @@ -743,6 +744,42 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step { |
| 743 | 744 | return test_step; |
| 744 | 745 | } |
| 745 | 746 | |
| 747 | fn 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 | ||
| 746 | 783 | fn testEmptyObject(b: *Build, opts: Options) *Step { |
| 747 | 784 | const test_step = addTestStep(b, "empty-object", opts); |
| 748 | 785 |