From 8142925c7e5cb6ec21ca7046b37140a735735b51 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Nov 2023 13:37:13 +0100 Subject: [PATCH] elf: hook up saving object files in an archive --- src/link/Elf.zig | 7 ++++++- src/link/Elf/Archive.zig | 4 ++++ src/link/Elf/Object.zig | 30 ++++++++++++++++++++++++++++++ src/link/Elf/ZigObject.zig | 27 ++++++++------------------- src/link/Elf/file.zig | 20 +++++++++++++------- 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 6d73e9bf2c..786f5eacac 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -288,7 +288,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option const index = @as(File.Index, @intCast(try self.files.addOne(allocator))); self.files.set(index, .{ .zig_object = .{ .index = index, - .path = options.module.?.main_mod.root_src_path, + .path = try std.fmt.allocPrint(self.base.allocator, "{s}.o", .{std.fs.path.stem( + options.module.?.main_mod.root_src_path, + )}), } }); self.zig_object_index = index; try self.zigObjectPtr().?.init(self); @@ -1553,6 +1555,9 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void 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); diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index c106e2f1ec..ee35f0fece 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -142,6 +142,7 @@ const SYMDEFNAME = genSpecialMemberName("__.SYMDEF"); const SYMDEFSORTEDNAME = genSpecialMemberName("__.SYMDEF SORTED"); const strtab_delimiter = '\n'; +pub const max_member_name_len = 15; pub const ar_hdr = extern struct { /// Member file name, sometimes / terminated. @@ -248,6 +249,9 @@ pub const ArSymtab = struct { if (elf_file.zigObjectPtr()) |zig_object| { offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off); } + for (elf_file.objects.items) |index| { + offsets.putAssumeCapacityNoClobber(index, elf_file.file(index).?.object.output_ar_state.file_off); + } // Number of symbols try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 673bf08247..e710a81ad3 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -654,6 +654,36 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { } } +pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { + const gpa = elf_file.base.allocator; + const start = self.first_global orelse self.symtab.items.len; + + try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.symtab.items.len - start); + + for (self.symtab.items[start..]) |sym| { + if (sym.st_shndx == elf.SHN_UNDEF) continue; + const off = try ar_symtab.strtab.insert(gpa, self.getString(sym.st_name)); + ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); + } +} + +pub fn updateArSize(self: *Object) void { + self.output_ar_state.size = self.data.len; +} + +pub fn writeAr(self: Object, 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(self.data.len), + }); + try writer.writeAll(mem.asBytes(&hdr)); + try writer.writeAll(self.data); +} + pub fn locals(self: Object) []const Symbol.Index { const end = self.first_global orelse self.symbols.items.len; return self.symbols.items[0..end]; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index a6ac6cfc11..dd2126a99e 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -3,7 +3,6 @@ //! and any relocations that may have been emitted. //! Think about this as fake in-memory Object file for the Zig module. -/// Path is owned by Module and lives as long as *Module. path: []const u8, index: File.Index, @@ -78,7 +77,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { try self.atoms.append(gpa, 0); // null input section try self.strtab.buffer.append(gpa, 0); - const name_off = try self.strtab.insert(gpa, std.fs.path.stem(self.path)); + const name_off = try self.strtab.insert(gpa, self.path); const symbol_index = try elf_file.addSymbol(); try self.local_symbols.append(gpa, symbol_index); const symbol_ptr = elf_file.symbol(symbol_index); @@ -98,6 +97,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { } pub fn deinit(self: *ZigObject, allocator: Allocator) void { + allocator.free(self.path); self.local_esyms.deinit(allocator); self.global_esyms.deinit(allocator); self.strtab.deinit(allocator); @@ -512,25 +512,13 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: * const global = elf_file.symbol(global_index); const file_ptr = global.file(elf_file).?; assert(file_ptr.index() == self.index); - if (global.type(elf_file) == elf.SHN_UNDEF) continue; + if (global.outputShndx() == null) continue; const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); } } -pub fn updateArStrtab( - self: *ZigObject, - allocator: Allocator, - ar_strtab: *Archive.ArStrtab, -) error{OutOfMemory}!void { - const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)}); - defer allocator.free(name); - if (name.len <= 15) return; - const name_off = try ar_strtab.insert(allocator, name); - self.output_ar_state.name_off = name_off; -} - pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { var end_pos: u64 = elf_file.shdr_table_offset.?; for (elf_file.shdrs.items) |shdr| { @@ -549,11 +537,12 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { const amt = try elf_file.base.file.?.preadAll(contents, 0); if (amt != self.output_ar_state.size) return error.InputOutput; - const name = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(self.path)}); - defer gpa.free(name); - + const name = self.path; const hdr = Archive.setArHdr(.{ - .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, + .name = if (name.len <= Archive.max_member_name_len) + .{ .name = name } + else + .{ .name_off = self.output_ar_state.name_off }, .size = @intCast(size), }); try writer.writeAll(mem.asBytes(&hdr)); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index ed15ff93f3..b5c8b2bd19 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -199,23 +199,30 @@ pub const File = union(enum) { pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { return switch (file) { .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), - .object => @panic("TODO"), + .object => |x| x.updateArSymtab(ar_symtab, elf_file), inline else => unreachable, }; } pub fn updateArStrtab(file: File, allocator: Allocator, ar_strtab: *Archive.ArStrtab) !void { - return switch (file) { - .zig_object => |x| x.updateArStrtab(allocator, ar_strtab), - .object => @panic("TODO"), + const path = switch (file) { + .zig_object => |x| x.path, + .object => |x| x.path, inline else => unreachable, }; + const state = switch (file) { + .zig_object => |x| &x.output_ar_state, + .object => |x| &x.output_ar_state, + inline else => unreachable, + }; + if (path.len <= Archive.max_member_name_len) return; + state.name_off = try ar_strtab.insert(allocator, path); } pub fn updateArSize(file: File, elf_file: *Elf) void { return switch (file) { .zig_object => |x| x.updateArSize(elf_file), - .object => @panic("TODO"), + .object => |x| x.updateArSize(), inline else => unreachable, }; } @@ -223,8 +230,7 @@ pub const File = union(enum) { pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void { return switch (file) { .zig_object => |x| x.writeAr(elf_file, writer), - // .object => |x| x.writeAr(elf_file, writer), - .object => @panic("TODO"), + .object => |x| x.writeAr(writer), inline else => unreachable, }; }