authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-04 20:58:15+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-04 20:58:15+01:00
logf24ceec35a6fd1e5e6a671461b78919b5f588a32
tree00e4242cf5dcdae789e0d8f1de77303f4a2e6e23
parent98dc28bbe223cb7183aabe7ed7a847c67c1a4df9
parent7a186d9eb6a84fb22bdb53b9c81a70169e9fa65f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17844 from ziglang/elf-object

elf: handle emitting relocatables and static libraries - humble beginnings

26 files changed, 1913 insertions(+), 1010 deletions(-)

CMakeLists.txt+1-1
......@@ -624,7 +624,7 @@ set(ZIG_STAGE2_SOURCES
624624 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
625625 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
626626 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
627 "${CMAKE_SOURCE_DIR}/src/link/strtab.zig"
627 "${CMAKE_SOURCE_DIR}/src/link/StringTable.zig"
628628 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
629629 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
630630 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
src/Compilation.zig+59-11
......@@ -810,16 +810,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
810810 return error.ExportTableAndImportTableConflict;
811811 }
812812
813 // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt.
814 // Once they are capable this condition could be removed. When removing this condition,
815 // also test the use case of `build-obj -fcompiler-rt` with the native backends
816 // and make sure the compiler-rt symbols are emitted.
817 const is_p9 = options.target.os.tag == .plan9;
818 const is_spv = options.target.cpu.arch.isSpirV();
819 const capable_of_building_compiler_rt = build_options.have_llvm and !is_p9 and !is_spv;
820 const capable_of_building_zig_libc = build_options.have_llvm and !is_p9 and !is_spv;
821 const capable_of_building_ssp = build_options.have_llvm and !is_p9 and !is_spv;
822
823813 const comp: *Compilation = comp: {
824814 // For allocations that have the same lifetime as Compilation. This arena is used only during this
825815 // initialization and then is freed in deinit().
......@@ -1094,6 +1084,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
10941084 if (stack_check and !target_util.supportsStackProbing(options.target))
10951085 return error.StackCheckUnsupportedByTarget;
10961086
1087 const capable_of_building_ssp = canBuildLibSsp(options.target, use_llvm);
1088
10971089 const stack_protector: u32 = options.want_stack_protector orelse b: {
10981090 if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0);
10991091
......@@ -1754,6 +1746,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17541746
17551747 const target = comp.getTarget();
17561748
1749 const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.bin_file.options.use_llvm);
1750 const capable_of_building_zig_libc = canBuildZigLibC(target, comp.bin_file.options.use_llvm);
1751
17571752 // Add a `CObject` for each `c_source_files`.
17581753 try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);
17591754 for (options.c_source_files) |c_source_file| {
......@@ -6240,9 +6235,62 @@ pub fn dump_argv(argv: []const []const u8) void {
62406235 nosuspend stderr.print("{s}\n", .{argv[argv.len - 1]}) catch {};
62416236}
62426237
6238fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool {
6239 switch (target.os.tag) {
6240 .plan9 => return false,
6241 else => {},
6242 }
6243 switch (target.cpu.arch) {
6244 .spirv32, .spirv64 => return false,
6245 else => {},
6246 }
6247 return switch (zigBackend(target, use_llvm)) {
6248 .stage2_llvm => true,
6249 .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm,
6250 else => build_options.have_llvm,
6251 };
6252}
6253
6254fn canBuildLibSsp(target: std.Target, use_llvm: bool) bool {
6255 switch (target.os.tag) {
6256 .plan9 => return false,
6257 else => {},
6258 }
6259 switch (target.cpu.arch) {
6260 .spirv32, .spirv64 => return false,
6261 else => {},
6262 }
6263 return switch (zigBackend(target, use_llvm)) {
6264 .stage2_llvm => true,
6265 else => build_options.have_llvm,
6266 };
6267}
6268
6269/// Not to be confused with canBuildLibC, which builds musl, glibc, and similar.
6270/// This one builds lib/c.zig.
6271fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
6272 switch (target.os.tag) {
6273 .plan9 => return false,
6274 else => {},
6275 }
6276 switch (target.cpu.arch) {
6277 .spirv32, .spirv64 => return false,
6278 else => {},
6279 }
6280 return switch (zigBackend(target, use_llvm)) {
6281 .stage2_llvm => true,
6282 .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm,
6283 else => build_options.have_llvm,
6284 };
6285}
6286
62436287pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
6244 if (comp.bin_file.options.use_llvm) return .stage2_llvm;
62456288 const target = comp.bin_file.options.target;
6289 return zigBackend(target, comp.bin_file.options.use_llvm);
6290}
6291
6292fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend {
6293 if (use_llvm) return .stage2_llvm;
62466294 if (target.ofmt == .c) return .stage2_c;
62476295 return switch (target.cpu.arch) {
62486296 .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
src/arch/x86_64/CodeGen.zig+2-3
......@@ -10796,7 +10796,7 @@ fn genCall(self: *Self, info: union(enum) {
1079610796 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1079710797 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
1079810798 const sym = elf_file.symbol(sym_index);
10799 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
10799 sym.flags.needs_zig_got = true;
1080010800 if (self.bin_file.options.pic) {
1080110801 const callee_reg: Register = switch (resolved_cc) {
1080210802 .SysV => callee: {
......@@ -13682,8 +13682,7 @@ fn genLazySymbolRef(
1368213682 const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
1368313683 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
1368413684 const sym = elf_file.symbol(sym_index);
13685 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
13686
13685 sym.flags.needs_zig_got = true;
1368713686 if (self.bin_file.options.pic) {
1368813687 switch (tag) {
1368913688 .lea, .call => try self.genSetReg(reg, Type.usize, .{
src/arch/x86_64/Emit.zig+30-13
......@@ -85,10 +85,19 @@ pub fn emitMir(emit: *Emit) Error!void {
8585 @tagName(emit.lower.bin_file.tag),
8686 }),
8787 .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
88 const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) {
89 .Exe => false,
90 .Obj => true,
91 .Lib => emit.lower.bin_file.options.link_mode == .Static,
92 };
8893 const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
89 const sym = elf_file.symbol(elf_file.zigObjectPtr().?.symbol(data.sym_index));
94 const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index);
95 const sym = elf_file.symbol(sym_index);
96 if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
97 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
98 }
9099 if (emit.lower.bin_file.options.pic) {
91 const r_type: u32 = if (sym.flags.has_zig_got)
100 const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
92101 link.File.Elf.R_X86_64_ZIG_GOTPCREL
93102 else if (sym.flags.needs_got)
94103 std.elf.R_X86_64_GOTPCREL
......@@ -100,17 +109,25 @@ pub fn emitMir(emit: *Emit) Error!void {
100109 .r_addend = -4,
101110 });
102111 } else {
103 const r_type: u32 = if (sym.flags.has_zig_got)
104 link.File.Elf.R_X86_64_ZIG_GOT32
105 else if (sym.flags.needs_got)
106 std.elf.R_X86_64_GOT32
107 else
108 std.elf.R_X86_64_32;
109 try atom.addReloc(elf_file, .{
110 .r_offset = end_offset - 4,
111 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
112 .r_addend = 0,
113 });
112 if (lowered_inst.encoding.mnemonic == .call and sym.flags.needs_zig_got and is_obj_or_static_lib) {
113 try atom.addReloc(elf_file, .{
114 .r_offset = end_offset - 4,
115 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_PC32,
116 .r_addend = -4,
117 });
118 } else {
119 const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
120 link.File.Elf.R_X86_64_ZIG_GOT32
121 else if (sym.flags.needs_got)
122 std.elf.R_X86_64_GOT32
123 else
124 std.elf.R_X86_64_32;
125 try atom.addReloc(elf_file, .{
126 .r_offset = end_offset - 4,
127 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
128 .r_addend = 0,
129 });
130 }
114131 }
115132 } else unreachable,
116133 .linker_got,
src/arch/x86_64/Lower.zig+29-5
......@@ -319,6 +319,19 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
319319}
320320
321321fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
322 const needsZigGot = struct {
323 fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool {
324 const elf_file = ctx.cast(link.File.Elf).?;
325 const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
326 return elf_file.symbol(sym_index).flags.needs_zig_got;
327 }
328 }.needsZigGot;
329
330 const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) {
331 .Exe => false,
332 .Obj => true,
333 .Lib => lower.bin_file.options.link_mode == .Static,
334 };
322335 var emit_prefix = prefix;
323336 var emit_mnemonic = mnemonic;
324337 var emit_ops_storage: [4]Operand = undefined;
......@@ -334,19 +347,30 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
334347 assert(mem_op.sib.scale_index.scale == 0);
335348 _ = lower.reloc(.{ .linker_reloc = sym });
336349 break :op if (lower.bin_file.options.pic) switch (mnemonic) {
337 .mov, .lea => .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
350 .lea => {
351 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
352 },
353 .mov => {
354 if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea;
355 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
356 },
338357 else => unreachable,
339358 } else switch (mnemonic) {
340 .call => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
359 .call => break :op if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) .{
360 .imm = Immediate.s(0),
361 } else .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
341362 .base = .{ .reg = .ds },
342363 }) },
343364 .lea => {
344365 emit_mnemonic = .mov;
345366 break :op .{ .imm = Immediate.s(0) };
346367 },
347 .mov => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
348 .base = .{ .reg = .ds },
349 }) },
368 .mov => {
369 if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea;
370 break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
371 .base = .{ .reg = .ds },
372 }) };
373 },
350374 else => unreachable,
351375 };
352376 },
src/codegen.zig+1-1
......@@ -912,7 +912,7 @@ fn genDeclRef(
912912 }
913913 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
914914 const sym = elf_file.symbol(sym_index);
915 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
915 sym.flags.needs_zig_got = true;
916916 return GenResult.mcv(.{ .load_symbol = sym.esym_index });
917917 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
918918 if (is_extern) {
src/link/Coff.zig+8-8
......@@ -33,10 +33,10 @@ need_got_table: std.AutoHashMapUnmanaged(u32, void) = .{},
3333locals_free_list: std.ArrayListUnmanaged(u32) = .{},
3434globals_free_list: std.ArrayListUnmanaged(u32) = .{},
3535
36strtab: StringTable(.strtab) = .{},
36strtab: StringTable = .{},
3737strtab_offset: ?u32 = null,
3838
39temp_strtab: StringTable(.temp_strtab) = .{},
39temp_strtab: StringTable = .{},
4040
4141got_table: TableSection(SymbolWithLoc) = .{},
4242
......@@ -419,7 +419,7 @@ fn populateMissingMetadata(self: *Coff) !void {
419419 }
420420
421421 if (self.strtab_offset == null) {
422 const file_size = @as(u32, @intCast(self.strtab.len()));
422 const file_size = @as(u32, @intCast(self.strtab.buffer.items.len));
423423 self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here
424424 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size });
425425 }
......@@ -2143,7 +2143,7 @@ fn writeStrtab(self: *Coff) !void {
21432143 if (self.strtab_offset == null) return;
21442144
21452145 const allocated_size = self.allocatedSize(self.strtab_offset.?);
2146 const needed_size = @as(u32, @intCast(self.strtab.len()));
2146 const needed_size = @as(u32, @intCast(self.strtab.buffer.items.len));
21472147
21482148 if (needed_size > allocated_size) {
21492149 self.strtab_offset = null;
......@@ -2155,10 +2155,10 @@ fn writeStrtab(self: *Coff) !void {
21552155 var buffer = std.ArrayList(u8).init(self.base.allocator);
21562156 defer buffer.deinit();
21572157 try buffer.ensureTotalCapacityPrecise(needed_size);
2158 buffer.appendSliceAssumeCapacity(self.strtab.items());
2158 buffer.appendSliceAssumeCapacity(self.strtab.buffer.items);
21592159 // Here, we do a trick in that we do not commit the size of the strtab to strtab buffer, instead
21602160 // we write the length of the strtab to a temporary buffer that goes to file.
2161 mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(self.strtab.len())), .little);
2161 mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(self.strtab.buffer.items.len)), .little);
21622162
21632163 try self.base.file.?.pwriteAll(buffer.items, self.strtab_offset.?);
21642164}
......@@ -2326,7 +2326,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
23262326 const end = start + padToIdeal(size);
23272327
23282328 if (self.strtab_offset) |off| {
2329 const tight_size = @as(u32, @intCast(self.strtab.len()));
2329 const tight_size = @as(u32, @intCast(self.strtab.buffer.items.len));
23302330 const increased_size = padToIdeal(tight_size);
23312331 const test_end = off + increased_size;
23322332 if (end > off and start < test_end) {
......@@ -2667,7 +2667,7 @@ const InternPool = @import("../InternPool.zig");
26672667const Object = @import("Coff/Object.zig");
26682668const Relocation = @import("Coff/Relocation.zig");
26692669const TableSection = @import("table_section.zig").TableSection;
2670const StringTable = @import("strtab.zig").StringTable;
2670const StringTable = @import("StringTable.zig");
26712671const Type = @import("../type.zig").Type;
26722672const TypedValue = @import("../TypedValue.zig");
26732673
src/link/Dwarf.zig+2-2
......@@ -23,7 +23,7 @@ abbrev_table_offset: ?u64 = null,
2323
2424/// TODO replace with InternPool
2525/// Table of debug symbol names.
26strtab: StringTable(.strtab) = .{},
26strtab: StringTable = .{},
2727
2828/// Quick lookup array of all defined source files referenced by at least one Decl.
2929/// They will end up in the DWARF debug_line header as two lists:
......@@ -2760,6 +2760,6 @@ const LinkFn = File.LinkFn;
27602760const LinkerLoad = @import("../codegen.zig").LinkerLoad;
27612761const Module = @import("../Module.zig");
27622762const InternPool = @import("../InternPool.zig");
2763const StringTable = @import("strtab.zig").StringTable;
2763const StringTable = @import("StringTable.zig");
27642764const Type = @import("../type.zig").Type;
27652765const Value = @import("../value.zig").Value;
src/link/Elf.zig+647-346
......@@ -66,13 +66,15 @@ page_size: u32,
6666default_sym_version: elf.Elf64_Versym,
6767
6868/// .shstrtab buffer
69shstrtab: StringTable(.strtab) = .{},
69shstrtab: std.ArrayListUnmanaged(u8) = .{},
70/// .symtab buffer
71symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
7072/// .strtab buffer
71strtab: StringTable(.strtab) = .{},
73strtab: std.ArrayListUnmanaged(u8) = .{},
7274/// Dynamic symbol table. Only populated and emitted when linking dynamically.
7375dynsym: DynsymSection = .{},
7476/// .dynstrtab buffer
75dynstrtab: StringTable(.dynstrtab) = .{},
77dynstrtab: std.ArrayListUnmanaged(u8) = .{},
7678/// Version symbol table. Only populated and emitted when linking dynamically.
7779versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
7880/// .verneed section
......@@ -97,13 +99,17 @@ plt_got: PltGotSection = .{},
9799copy_rel: CopyRelSection = .{},
98100/// .rela.plt section
99101rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
100/// .zig.got section
102/// .got.zig section
101103zig_got: ZigGotSection = .{},
102104
103/// Tracked section headers with incremental updates to Zig object
105/// Tracked section headers with incremental updates to Zig object.
106/// .rela.* sections are only used when emitting a relocatable object file.
104107zig_text_section_index: ?u16 = null,
105zig_rodata_section_index: ?u16 = null,
108zig_text_rela_section_index: ?u16 = null,
109zig_data_rel_ro_section_index: ?u16 = null,
110zig_data_rel_ro_rela_section_index: ?u16 = null,
106111zig_data_section_index: ?u16 = null,
112zig_data_rela_section_index: ?u16 = null,
107113zig_bss_section_index: ?u16 = null,
108114zig_got_section_index: ?u16 = null,
109115
......@@ -156,9 +162,10 @@ start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
156162/// An array of symbols parsed across all input files.
157163symbols: std.ArrayListUnmanaged(Symbol) = .{},
158164symbols_extra: std.ArrayListUnmanaged(u32) = .{},
159resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
160165symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
161166
167resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
168
162169has_text_reloc: bool = false,
163170num_ifunc_dynrelocs: usize = 0,
164171
......@@ -175,6 +182,10 @@ comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
175182comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
176183comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
177184
185/// Global string table used to provide quick access to global symbol resolvers
186/// such as `resolver` and `comdat_groups_table`.
187strings: StringTable = .{},
188
178189/// When allocating, the ideal_capacity is calculated by
179190/// actual_capacity + (actual_capacity / ideal_factor)
180191const ideal_factor = 3;
......@@ -227,13 +238,15 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
227238 // Append null file at index 0
228239 try self.files.append(allocator, .null);
229240 // Append null byte to string tables
230 try self.shstrtab.buffer.append(allocator, 0);
231 try self.strtab.buffer.append(allocator, 0);
241 try self.shstrtab.append(allocator, 0);
242 try self.strtab.append(allocator, 0);
232243 // There must always be a null shdr in index 0
233244 _ = try self.addSection(.{ .name = "" });
245 // Append null symbol in output symtab
246 try self.symtab.append(allocator, null_sym);
234247
235248 if (!is_obj_or_ar) {
236 try self.dynstrtab.buffer.append(allocator, 0);
249 try self.dynstrtab.append(allocator, 0);
237250
238251 // Initialize PT_PHDR program header
239252 const p_align: u16 = switch (self.ptr_width) {
......@@ -347,6 +360,7 @@ pub fn deinit(self: *Elf) void {
347360 }
348361 self.output_sections.deinit(gpa);
349362 self.shstrtab.deinit(gpa);
363 self.symtab.deinit(gpa);
350364 self.strtab.deinit(gpa);
351365 self.symbols.deinit(gpa);
352366 self.symbols_extra.deinit(gpa);
......@@ -364,6 +378,7 @@ pub fn deinit(self: *Elf) void {
364378 self.comdat_groups.deinit(gpa);
365379 self.comdat_groups_owners.deinit(gpa);
366380 self.comdat_groups_table.deinit(gpa);
381 self.strings.deinit(gpa);
367382
368383 self.got.deinit(gpa);
369384 self.plt.deinit(gpa);
......@@ -473,262 +488,302 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
473488 return start;
474489}
475490
476const AllocateSegmentOpts = struct {
477 addr: u64,
478 memsz: u64,
479 filesz: u64,
480 alignment: u64,
481 flags: u32 = elf.PF_R,
482};
483
484pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {
485 const off = self.findFreeSpace(opts.filesz, opts.alignment);
486 const index = try self.addPhdr(.{
487 .type = elf.PT_LOAD,
488 .offset = off,
489 .filesz = opts.filesz,
490 .addr = opts.addr,
491 .memsz = opts.memsz,
492 .@"align" = opts.alignment,
493 .flags = opts.flags,
494 });
495 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
496 index,
497 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',
498 if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',
499 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
500 off,
501 off + opts.filesz,
502 opts.addr,
503 opts.addr + opts.memsz,
504 });
505 return index;
506}
507
508const AllocateAllocSectionOpts = struct {
509 name: [:0]const u8,
510 phdr_index: u16,
511 alignment: u64 = 1,
512 flags: u64 = elf.SHF_ALLOC,
513 type: u32 = elf.SHT_PROGBITS,
514};
515
516pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{OutOfMemory}!u16 {
517 const gpa = self.base.allocator;
518 const phdr = &self.phdrs.items[opts.phdr_index];
519 const index = try self.addSection(.{
520 .name = opts.name,
521 .type = opts.type,
522 .flags = opts.flags,
523 .addralign = opts.alignment,
524 .offset = std.math.maxInt(u64),
525 });
526 const shdr = &self.shdrs.items[index];
527 try self.phdr_to_shdr_table.putNoClobber(gpa, index, opts.phdr_index);
528 log.debug("allocating '{s}' in phdr({d}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
529 opts.name,
530 opts.phdr_index,
531 phdr.p_offset,
532 phdr.p_offset + phdr.p_filesz,
533 phdr.p_vaddr,
534 phdr.p_vaddr + phdr.p_memsz,
535 });
536 shdr.sh_addr = phdr.p_vaddr;
537 shdr.sh_offset = phdr.p_offset;
538 shdr.sh_size = phdr.p_memsz;
539 return index;
540}
541
542const AllocateNonAllocSectionOpts = struct {
543 name: [:0]const u8,
544 size: u64,
545 alignment: u16 = 1,
546 flags: u32 = 0,
547 type: u32 = elf.SHT_PROGBITS,
548 link: u32 = 0,
549 info: u32 = 0,
550 entsize: u64 = 0,
551};
552
553fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{OutOfMemory}!u16 {
554 const index = try self.addSection(.{
555 .name = opts.name,
556 .type = opts.type,
557 .flags = opts.flags,
558 .link = opts.link,
559 .info = opts.info,
560 .addralign = opts.alignment,
561 .entsize = opts.entsize,
562 .offset = std.math.maxInt(u64),
563 });
564 const shdr = &self.shdrs.items[index];
565 const off = self.findFreeSpace(opts.size, opts.alignment);
566 log.debug("allocating '{s}' from 0x{x} to 0x{x} ", .{ opts.name, off, off + opts.size });
567 shdr.sh_offset = off;
568 shdr.sh_size = opts.size;
569 return index;
570}
571
572491/// TODO move to ZigObject
573492pub fn initMetadata(self: *Elf) !void {
574493 const gpa = self.base.allocator;
575494 const ptr_size = self.ptrWidthBytes();
576495 const ptr_bit_width = self.base.options.target.ptrBitWidth();
577496 const is_linux = self.base.options.target.os.tag == .linux;
497 const zig_object = self.zigObjectPtr().?;
498
499 const fillSection = struct {
500 fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void {
501 if (elf_file.isRelocatable()) {
502 const off = elf_file.findFreeSpace(size, shdr.sh_addralign);
503 shdr.sh_offset = off;
504 shdr.sh_size = size;
505 } else {
506 const phdr = elf_file.phdrs.items[phndx.?];
507 shdr.sh_addr = phdr.p_vaddr;
508 shdr.sh_offset = phdr.p_offset;
509 shdr.sh_size = phdr.p_memsz;
510 }
511 }
512 }.fillSection;
578513
579514 comptime assert(number_of_zig_segments == 5);
580515
581 if (self.phdr_zig_load_re_index == null) {
582 self.phdr_zig_load_re_index = try self.allocateSegment(.{
583 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
584 .memsz = self.base.options.program_code_size_hint,
585 .filesz = self.base.options.program_code_size_hint,
586 .alignment = self.page_size,
587 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
588 });
589 }
516 if (!self.isRelocatable()) {
517 if (self.phdr_zig_load_re_index == null) {
518 const filesz = self.base.options.program_code_size_hint;
519 const off = self.findFreeSpace(filesz, self.page_size);
520 self.phdr_zig_load_re_index = try self.addPhdr(.{
521 .type = elf.PT_LOAD,
522 .offset = off,
523 .filesz = filesz,
524 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
525 .memsz = filesz,
526 .@"align" = self.page_size,
527 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
528 });
529 }
590530
591 if (self.phdr_zig_got_index == null) {
592 // We really only need ptr alignment but since we are using PROGBITS, linux requires
593 // page align.
594 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
595 self.phdr_zig_got_index = try self.allocateSegment(.{
596 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
597 .memsz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
598 .filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
599 .alignment = alignment,
600 .flags = elf.PF_R | elf.PF_W,
601 });
602 }
531 if (self.phdr_zig_got_index == null) {
532 // We really only need ptr alignment but since we are using PROGBITS, linux requires
533 // page align.
534 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
535 const filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
536 const off = self.findFreeSpace(filesz, alignment);
537 self.phdr_zig_got_index = try self.addPhdr(.{
538 .type = elf.PT_LOAD,
539 .offset = off,
540 .filesz = filesz,
541 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
542 .memsz = filesz,
543 .@"align" = alignment,
544 .flags = elf.PF_R | elf.PF_W,
545 });
546 }
603547
604 if (self.phdr_zig_load_ro_index == null) {
605 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
606 self.phdr_zig_load_ro_index = try self.allocateSegment(.{
607 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
608 .memsz = 1024,
609 .filesz = 1024,
610 .alignment = alignment,
611 .flags = elf.PF_R | elf.PF_W,
612 });
613 }
548 if (self.phdr_zig_load_ro_index == null) {
549 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
550 const filesz: u64 = 1024;
551 const off = self.findFreeSpace(filesz, alignment);
552 self.phdr_zig_load_ro_index = try self.addPhdr(.{
553 .type = elf.PT_LOAD,
554 .offset = off,
555 .filesz = filesz,
556 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
557 .memsz = filesz,
558 .@"align" = alignment,
559 .flags = elf.PF_R | elf.PF_W,
560 });
561 }
614562
615 if (self.phdr_zig_load_rw_index == null) {
616 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
617 self.phdr_zig_load_rw_index = try self.allocateSegment(.{
618 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
619 .memsz = 1024,
620 .filesz = 1024,
621 .alignment = alignment,
622 .flags = elf.PF_R | elf.PF_W,
623 });
624 }
563 if (self.phdr_zig_load_rw_index == null) {
564 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
565 const filesz: u64 = 1024;
566 const off = self.findFreeSpace(filesz, alignment);
567 self.phdr_zig_load_rw_index = try self.addPhdr(.{
568 .type = elf.PT_LOAD,
569 .offset = off,
570 .filesz = filesz,
571 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
572 .memsz = filesz,
573 .@"align" = alignment,
574 .flags = elf.PF_R | elf.PF_W,
575 });
576 }
625577
626 if (self.phdr_zig_load_zerofill_index == null) {
627 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
628 self.phdr_zig_load_zerofill_index = try self.addPhdr(.{
629 .type = elf.PT_LOAD,
630 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
631 .memsz = 1024,
632 .@"align" = alignment,
633 .flags = elf.PF_R | elf.PF_W,
634 });
578 if (self.phdr_zig_load_zerofill_index == null) {
579 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
580 self.phdr_zig_load_zerofill_index = try self.addPhdr(.{
581 .type = elf.PT_LOAD,
582 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
583 .memsz = 1024,
584 .@"align" = alignment,
585 .flags = elf.PF_R | elf.PF_W,
586 });
587 }
635588 }
636589
637590 if (self.zig_text_section_index == null) {
638 self.zig_text_section_index = try self.allocateAllocSection(.{
639 .name = ".zig.text",
640 .phdr_index = self.phdr_zig_load_re_index.?,
591 self.zig_text_section_index = try self.addSection(.{
592 .name = ".text.zig",
593 .type = elf.SHT_PROGBITS,
641594 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
595 .addralign = 1,
596 .offset = std.math.maxInt(u64),
642597 });
598 const shdr = &self.shdrs.items[self.zig_text_section_index.?];
599 fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index);
600 if (self.isRelocatable()) {
601 try zig_object.addSectionSymbol(self.zig_text_section_index.?, self);
602 self.zig_text_rela_section_index = try self.addRelaShdr(
603 ".rela.text.zig",
604 self.zig_text_section_index.?,
605 );
606 } else {
607 try self.phdr_to_shdr_table.putNoClobber(
608 gpa,
609 self.zig_text_section_index.?,
610 self.phdr_zig_load_re_index.?,
611 );
612 }
643613 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{});
644614 }
645615
646 if (self.zig_got_section_index == null) {
647 self.zig_got_section_index = try self.allocateAllocSection(.{
648 .name = ".zig.got",
649 .phdr_index = self.phdr_zig_got_index.?,
650 .alignment = ptr_size,
616 if (self.zig_got_section_index == null and !self.isRelocatable()) {
617 self.zig_got_section_index = try self.addSection(.{
618 .name = ".got.zig",
619 .type = elf.SHT_PROGBITS,
620 .addralign = ptr_size,
651621 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
622 .offset = std.math.maxInt(u64),
652623 });
653 }
654
655 if (self.zig_rodata_section_index == null) {
656 self.zig_rodata_section_index = try self.allocateAllocSection(.{
657 .name = ".zig.rodata",
658 .phdr_index = self.phdr_zig_load_ro_index.?,
624 const shdr = &self.shdrs.items[self.zig_got_section_index.?];
625 const phndx = self.phdr_zig_got_index.?;
626 const phdr = self.phdrs.items[phndx];
627 shdr.sh_addr = phdr.p_vaddr;
628 shdr.sh_offset = phdr.p_offset;
629 shdr.sh_size = phdr.p_memsz;
630 try self.phdr_to_shdr_table.putNoClobber(
631 gpa,
632 self.zig_got_section_index.?,
633 self.phdr_zig_got_index.?,
634 );
635 }
636
637 if (self.zig_data_rel_ro_section_index == null) {
638 self.zig_data_rel_ro_section_index = try self.addSection(.{
639 .name = ".data.rel.ro.zig",
640 .type = elf.SHT_PROGBITS,
641 .addralign = 1,
659642 .flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro
643 .offset = std.math.maxInt(u64),
660644 });
661 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_rodata_section_index.?, .{});
645 const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?];
646 fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index);
647 if (self.isRelocatable()) {
648 try zig_object.addSectionSymbol(self.zig_data_rel_ro_section_index.?, self);
649 self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr(
650 ".rela.data.rel.ro.zig",
651 self.zig_data_rel_ro_section_index.?,
652 );
653 } else {
654 try self.phdr_to_shdr_table.putNoClobber(
655 gpa,
656 self.zig_data_rel_ro_section_index.?,
657 self.phdr_zig_load_ro_index.?,
658 );
659 }
660 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{});
662661 }
663662
664663 if (self.zig_data_section_index == null) {
665 self.zig_data_section_index = try self.allocateAllocSection(.{
666 .name = ".zig.data",
667 .phdr_index = self.phdr_zig_load_rw_index.?,
668 .alignment = ptr_size,
664 self.zig_data_section_index = try self.addSection(.{
665 .name = ".data.zig",
666 .type = elf.SHT_PROGBITS,
667 .addralign = ptr_size,
669668 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
669 .offset = std.math.maxInt(u64),
670670 });
671 const shdr = &self.shdrs.items[self.zig_data_section_index.?];
672 fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index);
673 if (self.isRelocatable()) {
674 try zig_object.addSectionSymbol(self.zig_data_section_index.?, self);
675 self.zig_data_rela_section_index = try self.addRelaShdr(
676 ".rela.data.zig",
677 self.zig_data_section_index.?,
678 );
679 } else {
680 try self.phdr_to_shdr_table.putNoClobber(
681 gpa,
682 self.zig_data_section_index.?,
683 self.phdr_zig_load_rw_index.?,
684 );
685 }
671686 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_section_index.?, .{});
672687 }
673688
674689 if (self.zig_bss_section_index == null) {
675 self.zig_bss_section_index = try self.allocateAllocSection(.{
676 .name = ".zig.bss",
677 .phdr_index = self.phdr_zig_load_zerofill_index.?,
678 .alignment = ptr_size,
679 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
690 self.zig_bss_section_index = try self.addSection(.{
691 .name = ".bss.zig",
680692 .type = elf.SHT_NOBITS,
693 .addralign = ptr_size,
694 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
695 .offset = 0,
681696 });
697 const shdr = &self.shdrs.items[self.zig_bss_section_index.?];
698 if (self.phdr_zig_load_zerofill_index) |phndx| {
699 const phdr = self.phdrs.items[phndx];
700 shdr.sh_addr = phdr.p_vaddr;
701 shdr.sh_size = phdr.p_memsz;
702 try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_bss_section_index.?, phndx);
703 } else {
704 try zig_object.addSectionSymbol(self.zig_bss_section_index.?, self);
705 shdr.sh_size = 1024;
706 }
682707 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{});
683708 }
684709
685 const zig_object = self.zigObjectPtr().?;
686710 if (zig_object.dwarf) |*dw| {
687711 if (self.debug_str_section_index == null) {
688712 assert(dw.strtab.buffer.items.len == 0);
689713 try dw.strtab.buffer.append(gpa, 0);
690 self.debug_str_section_index = try self.allocateNonAllocSection(.{
714 self.debug_str_section_index = try self.addSection(.{
691715 .name = ".debug_str",
692 .size = @intCast(dw.strtab.buffer.items.len),
693716 .flags = elf.SHF_MERGE | elf.SHF_STRINGS,
694717 .entsize = 1,
718 .type = elf.SHT_PROGBITS,
719 .addralign = 1,
720 .offset = std.math.maxInt(u64),
695721 });
722 const shdr = &self.shdrs.items[self.debug_str_section_index.?];
723 const size = @as(u64, @intCast(dw.strtab.buffer.items.len));
724 const off = self.findFreeSpace(size, 1);
725 shdr.sh_offset = off;
726 shdr.sh_size = size;
696727 zig_object.debug_strtab_dirty = true;
697728 }
698729
699730 if (self.debug_info_section_index == null) {
700 self.debug_info_section_index = try self.allocateNonAllocSection(.{
731 self.debug_info_section_index = try self.addSection(.{
701732 .name = ".debug_info",
702 .size = 200,
703 .alignment = 1,
733 .type = elf.SHT_PROGBITS,
734 .addralign = 1,
735 .offset = std.math.maxInt(u64),
704736 });
737 const shdr = &self.shdrs.items[self.debug_info_section_index.?];
738 const size: u64 = 200;
739 const off = self.findFreeSpace(size, 1);
740 shdr.sh_offset = off;
741 shdr.sh_size = size;
705742 zig_object.debug_info_header_dirty = true;
706743 }
707744
708745 if (self.debug_abbrev_section_index == null) {
709 self.debug_abbrev_section_index = try self.allocateNonAllocSection(.{
746 self.debug_abbrev_section_index = try self.addSection(.{
710747 .name = ".debug_abbrev",
711 .size = 128,
712 .alignment = 1,
748 .type = elf.SHT_PROGBITS,
749 .addralign = 1,
750 .offset = std.math.maxInt(u64),
713751 });
752 const shdr = &self.shdrs.items[self.debug_abbrev_section_index.?];
753 const size: u64 = 128;
754 const off = self.findFreeSpace(size, 1);
755 shdr.sh_offset = off;
756 shdr.sh_size = size;
714757 zig_object.debug_abbrev_section_dirty = true;
715758 }
716759
717760 if (self.debug_aranges_section_index == null) {
718 self.debug_aranges_section_index = try self.allocateNonAllocSection(.{
761 self.debug_aranges_section_index = try self.addSection(.{
719762 .name = ".debug_aranges",
720 .size = 160,
721 .alignment = 16,
763 .type = elf.SHT_PROGBITS,
764 .addralign = 16,
765 .offset = std.math.maxInt(u64),
722766 });
767 const shdr = &self.shdrs.items[self.debug_aranges_section_index.?];
768 const size: u64 = 160;
769 const off = self.findFreeSpace(size, 16);
770 shdr.sh_offset = off;
771 shdr.sh_size = size;
723772 zig_object.debug_aranges_section_dirty = true;
724773 }
725774
726775 if (self.debug_line_section_index == null) {
727 self.debug_line_section_index = try self.allocateNonAllocSection(.{
776 self.debug_line_section_index = try self.addSection(.{
728777 .name = ".debug_line",
729 .size = 250,
730 .alignment = 1,
778 .type = elf.SHT_PROGBITS,
779 .addralign = 1,
780 .offset = std.math.maxInt(u64),
731781 });
782 const shdr = &self.shdrs.items[self.debug_line_section_index.?];
783 const size: u64 = 250;
784 const off = self.findFreeSpace(size, 1);
785 shdr.sh_offset = off;
786 shdr.sh_size = size;
732787 zig_object.debug_line_header_dirty = true;
733788 }
734789 }
......@@ -736,18 +791,18 @@ pub fn initMetadata(self: *Elf) !void {
736791
737792pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
738793 const shdr = &self.shdrs.items[shdr_index];
739 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
740 const phdr = &self.phdrs.items[phdr_index];
794 const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null;
741795 const is_zerofill = shdr.sh_type == elf.SHT_NOBITS;
742796
743797 if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) {
744798 const existing_size = shdr.sh_size;
745799 shdr.sh_size = 0;
746800 // Must move the entire section.
747 const new_offset = self.findFreeSpace(needed_size, self.page_size);
801 const alignment = if (maybe_phdr) |phdr| phdr.p_align else shdr.sh_addralign;
802 const new_offset = self.findFreeSpace(needed_size, alignment);
748803
749804 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
750 self.shstrtab.getAssumeExists(shdr.sh_name),
805 self.getShString(shdr.sh_name),
751806 new_offset,
752807 new_offset + existing_size,
753808 });
......@@ -757,25 +812,27 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
757812 if (amt != existing_size) return error.InputOutput;
758813
759814 shdr.sh_offset = new_offset;
760 phdr.p_offset = new_offset;
815 if (maybe_phdr) |phdr| phdr.p_offset = new_offset;
761816 }
762817
763818 shdr.sh_size = needed_size;
764819 if (!is_zerofill) {
765 phdr.p_filesz = needed_size;
820 if (maybe_phdr) |phdr| phdr.p_filesz = needed_size;
766821 }
767822
768 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
769 if (needed_size > mem_capacity) {
770 var err = try self.addErrorWithNotes(2);
771 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
772 phdr_index,
773 });
774 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
775 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
776 }
823 if (maybe_phdr) |phdr| {
824 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
825 if (needed_size > mem_capacity) {
826 var err = try self.addErrorWithNotes(2);
827 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
828 self.phdr_to_shdr_table.get(shdr_index).?,
829 });
830 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
831 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
832 }
777833
778 phdr.p_memsz = needed_size;
834 phdr.p_memsz = needed_size;
835 }
779836
780837 self.markDirty(shdr_index);
781838}
......@@ -796,7 +853,7 @@ pub fn growNonAllocSection(
796853 const new_offset = self.findFreeSpace(needed_size, min_alignment);
797854
798855 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
799 self.shstrtab.getAssumeExists(shdr.sh_name),
856 self.getShString(shdr.sh_name),
800857 new_offset,
801858 new_offset + existing_size,
802859 });
......@@ -847,10 +904,6 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link
847904 if (use_lld) {
848905 return self.linkWithLLD(comp, prog_node);
849906 }
850 if (self.base.options.output_mode == .Lib and self.isStatic()) {
851 // TODO writing static library files
852 return error.TODOImplementWritingLibFiles;
853 }
854907 try self.flushModule(comp, prog_node);
855908}
856909
......@@ -886,7 +939,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
886939 } else null;
887940 const gc_sections = self.base.options.gc_sections orelse false;
888941
889 if (self.base.options.output_mode == .Obj and self.zig_object_index == null) {
942 if (self.isRelocatable() and self.zig_object_index == null) {
943 if (self.isStaticLib()) {
944 var err = try self.addErrorWithNotes(0);
945 try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{});
946 return;
947 }
890948 // TODO this will become -r route I guess. For now, just copy the object file.
891949 assert(self.base.file == null); // TODO uncomment once we implement -r
892950 const the_object_path = blk: {
......@@ -1159,6 +1217,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11591217 Compilation.dump_argv(argv.items);
11601218 }
11611219
1220 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1221
11621222 // Here we will parse input positional and library files (if referenced).
11631223 // This will roughly match in any linker backend we support.
11641224 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
......@@ -1226,6 +1286,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12261286 try positionals.append(.{ .path = ssp.full_object_path });
12271287 }
12281288
1289 if (self.isStaticLib()) return self.flushStaticLib(comp, positionals.items);
1290
12291291 for (positionals.items) |obj| {
12301292 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
12311293 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
......@@ -1331,8 +1393,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13311393 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
13321394 }
13331395
1334 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1335
13361396 // Dedup shared objects
13371397 {
13381398 var seen_dsos = std.StringHashMap(void).init(gpa);
......@@ -1353,7 +1413,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13531413
13541414 // If we haven't already, create a linker-generated input file comprising of
13551415 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
1356 if (self.linker_defined_index == null) {
1416 if (self.linker_defined_index == null and !self.isRelocatable()) {
13571417 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
13581418 self.files.set(index, .{ .linker_defined = .{ .index = index } });
13591419 self.linker_defined_index = index;
......@@ -1366,6 +1426,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13661426 // symbol for potential resolution at load-time.
13671427 self.resolveSymbols();
13681428 self.markEhFrameAtomsDead();
1429
1430 if (self.isObject()) return self.flushObject(comp);
1431
13691432 try self.convertCommonSymbols();
13701433 self.markImportsExports();
13711434
......@@ -1449,14 +1512,150 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
14491512 try self.writeAtoms();
14501513 try self.writeSyntheticSections();
14511514
1452 if (self.entry_index == null and self.base.options.effectiveOutputMode() == .Exe) {
1515 if (self.entry_index == null and self.isExe()) {
14531516 log.debug("flushing. no_entry_point_found = true", .{});
14541517 self.error_flags.no_entry_point_found = true;
14551518 } else {
14561519 log.debug("flushing. no_entry_point_found = false", .{});
14571520 self.error_flags.no_entry_point_found = false;
1458 try self.writeHeader();
1521 try self.writeElfHeader();
1522 }
1523}
1524
1525pub fn flushStaticLib(
1526 self: *Elf,
1527 comp: *Compilation,
1528 positionals: []const Compilation.LinkObject,
1529) link.File.FlushError!void {
1530 _ = comp;
1531 if (positionals.len > 0) {
1532 var err = try self.addErrorWithNotes(1);
1533 try err.addMsg(self, "fatal linker error: too many input positionals", .{});
1534 try err.addNote(self, "TODO implement linking objects into an static library", .{});
1535 return;
1536 }
1537 const gpa = self.base.allocator;
1538
1539 // First, we flush relocatable object file generated with our backends.
1540 if (self.zigObjectPtr()) |zig_object| {
1541 zig_object.resolveSymbols(self);
1542 zig_object.claimUnresolvedObject(self);
1543
1544 try self.initSymtab();
1545 try self.initShStrtab();
1546 try self.sortShdrs();
1547 zig_object.updateRelaSectionSizes(self);
1548 try self.updateSymtabSize();
1549 self.updateShStrtabSize();
1550
1551 try self.allocateNonAllocSections();
1552
1553 try self.writeShdrTable();
1554 try zig_object.writeRelaSections(self);
1555 try self.writeSymtab();
1556 try self.writeShStrtab();
1557 try self.writeElfHeader();
1558 }
1559
1560 // TODO parse positionals that we want to make part of the archive
1561
1562 // TODO update ar symtab from parsed positionals
1563
1564 var ar_symtab: Archive.ArSymtab = .{};
1565 defer ar_symtab.deinit(gpa);
1566
1567 if (self.zigObjectPtr()) |zig_object| {
1568 try zig_object.updateArSymtab(&ar_symtab, self);
1569 }
1570
1571 ar_symtab.sort();
1572
1573 // Save object paths in filenames strtab.
1574 var ar_strtab: Archive.ArStrtab = .{};
1575 defer ar_strtab.deinit(gpa);
1576
1577 if (self.zigObjectPtr()) |zig_object| {
1578 try zig_object.updateArStrtab(gpa, &ar_strtab);
1579 zig_object.updateArSize(self);
1580 }
1581
1582 // Update file offsets of contributing objects.
1583 const total_size: usize = blk: {
1584 var pos: usize = Archive.SARMAG;
1585 pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64);
1586
1587 if (ar_strtab.size() > 0) {
1588 pos = mem.alignForward(usize, pos, 2);
1589 pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size();
1590 }
1591
1592 if (self.zigObjectPtr()) |zig_object| {
1593 pos = mem.alignForward(usize, pos, 2);
1594 zig_object.output_ar_state.file_off = pos;
1595 pos += @sizeOf(Archive.ar_hdr) + (math.cast(usize, zig_object.output_ar_state.size) orelse return error.Overflow);
1596 }
1597
1598 break :blk pos;
1599 };
1600
1601 if (build_options.enable_logging) {
1602 state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)});
1603 state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
1604 }
1605
1606 var buffer = std.ArrayList(u8).init(gpa);
1607 defer buffer.deinit();
1608 try buffer.ensureTotalCapacityPrecise(total_size);
1609
1610 // Write magic
1611 try buffer.writer().writeAll(Archive.ARMAG);
1612
1613 // Write symtab
1614 try ar_symtab.write(.p64, self, buffer.writer());
1615
1616 // Write strtab
1617 if (ar_strtab.size() > 0) {
1618 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1619 try ar_strtab.write(buffer.writer());
1620 }
1621
1622 // Write object files
1623 if (self.zigObjectPtr()) |zig_object| {
1624 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1625 try zig_object.writeAr(self, buffer.writer());
1626 }
1627
1628 assert(buffer.items.len == total_size);
1629
1630 try self.base.file.?.setEndPos(total_size);
1631 try self.base.file.?.pwriteAll(buffer.items, 0);
1632}
1633
1634pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void {
1635 _ = comp;
1636
1637 if (self.objects.items.len > 0) {
1638 var err = try self.addErrorWithNotes(1);
1639 try err.addMsg(self, "fatal linker error: too many input positionals", .{});
1640 try err.addNote(self, "TODO implement '-r' option", .{});
1641 return;
1642 }
1643
1644 self.claimUnresolvedObject();
1645
1646 try self.initSections();
1647 try self.sortShdrs();
1648 try self.updateSectionSizes();
1649
1650 try self.allocateNonAllocSections();
1651
1652 if (build_options.enable_logging) {
1653 state_log.debug("{}", .{self.dumpState()});
14591654 }
1655
1656 try self.writeShdrTable();
1657 try self.writeSyntheticSections();
1658 try self.writeElfHeader();
14601659}
14611660
14621661const ParseError = error{
......@@ -1696,7 +1895,7 @@ fn accessLibPath(
16961895/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
16971896fn resolveSymbols(self: *Elf) void {
16981897 // Resolve symbols in the ZigObject. For now, we assume that it's always live.
1699 if (self.zigObjectPtr()) |zig_object| zig_object.resolveSymbols(self);
1898 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
17001899 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
17011900 for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
17021901 for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
......@@ -1705,7 +1904,7 @@ fn resolveSymbols(self: *Elf) void {
17051904 self.markLive();
17061905
17071906 // Reset state of all globals after marking live objects.
1708 if (self.zigObjectPtr()) |zig_object| zig_object.resetGlobals(self);
1907 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resetGlobals(self);
17091908 for (self.objects.items) |index| self.file(index).?.resetGlobals(self);
17101909 for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self);
17111910
......@@ -1767,7 +1966,7 @@ fn resolveSymbols(self: *Elf) void {
17671966/// This routine will prune unneeded objects extracted from archives and
17681967/// unneeded shared objects.
17691968fn markLive(self: *Elf) void {
1770 if (self.zigObjectPtr()) |zig_object| zig_object.markLive(self);
1969 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().markLive(self);
17711970 for (self.objects.items) |index| {
17721971 const file_ptr = self.file(index).?;
17731972 if (file_ptr.isAlive()) file_ptr.markLive(self);
......@@ -1845,6 +2044,12 @@ fn claimUnresolved(self: *Elf) void {
18452044 }
18462045}
18472046
2047fn claimUnresolvedObject(self: *Elf) void {
2048 if (self.zigObjectPtr()) |zig_object| {
2049 zig_object.claimUnresolvedObject(self);
2050 }
2051}
2052
18482053/// In scanRelocs we will go over all live atoms and scan their relocs.
18492054/// This will help us work out what synthetics to emit, GOT indirection, etc.
18502055/// This is also the point where we will report undefined symbols for any
......@@ -1873,7 +2078,7 @@ fn scanRelocs(self: *Elf) !void {
18732078
18742079 for (self.symbols.items, 0..) |*sym, i| {
18752080 const index = @as(u32, @intCast(i));
1876 if (!sym.isLocal() and !sym.flags.has_dynamic) {
2081 if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
18772082 log.debug("'{s}' is non-local", .{sym.name(self)});
18782083 try self.dynsym.addSymbol(index, self);
18792084 }
......@@ -2704,7 +2909,7 @@ fn writePhdrTable(self: *Elf) !void {
27042909 }
27052910}
27062911
2707fn writeHeader(self: *Elf) !void {
2912fn writeElfHeader(self: *Elf) !void {
27082913 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
27092914
27102915 var index: usize = 0;
......@@ -2735,7 +2940,7 @@ fn writeHeader(self: *Elf) !void {
27352940
27362941 assert(index == 16);
27372942
2738 const elf_type: elf.ET = switch (self.base.options.effectiveOutputMode()) {
2943 const elf_type: elf.ET = switch (self.base.options.output_mode) {
27392944 .Exe => if (self.base.options.pie) .DYN else .EXEC,
27402945 .Obj => .REL,
27412946 .Lib => switch (self.base.options.link_mode) {
......@@ -2755,7 +2960,7 @@ fn writeHeader(self: *Elf) !void {
27552960 index += 4;
27562961
27572962 const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0;
2758 const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset;
2963 const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0;
27592964 switch (self.ptr_width) {
27602965 .p32 => {
27612966 mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian);
......@@ -3054,10 +3259,6 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
30543259}
30553260
30563261fn initSections(self: *Elf) !void {
3057 const small_ptr = switch (self.ptr_width) {
3058 .p32 => true,
3059 .p64 => false,
3060 };
30613262 const ptr_size = self.ptrWidthBytes();
30623263
30633264 for (self.objects.items) |index| {
......@@ -3247,6 +3448,15 @@ fn initSections(self: *Elf) !void {
32473448 }
32483449 }
32493450
3451 try self.initSymtab();
3452 try self.initShStrtab();
3453}
3454
3455fn initSymtab(self: *Elf) !void {
3456 const small_ptr = switch (self.ptr_width) {
3457 .p32 => true,
3458 .p64 => false,
3459 };
32503460 if (self.symtab_section_index == null) {
32513461 self.symtab_section_index = try self.addSection(.{
32523462 .name = ".symtab",
......@@ -3265,6 +3475,9 @@ fn initSections(self: *Elf) !void {
32653475 .offset = std.math.maxInt(u64),
32663476 });
32673477 }
3478}
3479
3480fn initShStrtab(self: *Elf) !void {
32683481 if (self.shstrtab_section_index == null) {
32693482 self.shstrtab_section_index = try self.addSection(.{
32703483 .name = ".shstrtab",
......@@ -3358,7 +3571,7 @@ fn sortInitFini(self: *Elf) !void {
33583571 elf.SHT_FINI_ARRAY,
33593572 => is_init_fini = true,
33603573 else => {
3361 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
3574 const name = self.getShString(shdr.sh_name);
33623575 is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null;
33633576 },
33643577 }
......@@ -3520,7 +3733,7 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
35203733
35213734fn shdrRank(self: *Elf, shndx: u16) u8 {
35223735 const shdr = self.shdrs.items[shndx];
3523 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
3736 const name = self.getShString(shdr.sh_name);
35243737 const flags = shdr.sh_flags;
35253738
35263739 switch (shdr.sh_type) {
......@@ -3620,9 +3833,12 @@ fn sortShdrs(self: *Elf) !void {
36203833 &self.versym_section_index,
36213834 &self.verneed_section_index,
36223835 &self.zig_text_section_index,
3836 &self.zig_text_rela_section_index,
36233837 &self.zig_got_section_index,
3624 &self.zig_rodata_section_index,
3838 &self.zig_data_rel_ro_section_index,
3839 &self.zig_data_rel_ro_rela_section_index,
36253840 &self.zig_data_section_index,
3841 &self.zig_data_rela_section_index,
36263842 &self.zig_bss_section_index,
36273843 &self.debug_str_section_index,
36283844 &self.debug_info_section_index,
......@@ -3681,6 +3897,31 @@ fn sortShdrs(self: *Elf) !void {
36813897 shdr.sh_info = self.plt_section_index.?;
36823898 }
36833899
3900 for (&[_]?u16{
3901 self.zig_text_rela_section_index,
3902 self.zig_data_rel_ro_rela_section_index,
3903 self.zig_data_rela_section_index,
3904 }) |maybe_index| {
3905 const index = maybe_index orelse continue;
3906 const shdr = &self.shdrs.items[index];
3907 shdr.sh_link = self.symtab_section_index.?;
3908 shdr.sh_info = backlinks[shdr.sh_info];
3909 }
3910
3911 {
3912 var last_atom_and_free_list_table = try self.last_atom_and_free_list_table.clone(gpa);
3913 defer last_atom_and_free_list_table.deinit(gpa);
3914
3915 self.last_atom_and_free_list_table.clearRetainingCapacity();
3916
3917 var it = last_atom_and_free_list_table.iterator();
3918 while (it.next()) |entry| {
3919 const shndx = entry.key_ptr.*;
3920 const meta = entry.value_ptr.*;
3921 self.last_atom_and_free_list_table.putAssumeCapacityNoClobber(backlinks[shndx], meta);
3922 }
3923 }
3924
36843925 {
36853926 var phdr_to_shdr_table = try self.phdr_to_shdr_table.clone(gpa);
36863927 defer phdr_to_shdr_table.deinit(gpa);
......@@ -3698,23 +3939,19 @@ fn sortShdrs(self: *Elf) !void {
36983939 if (self.zigObjectPtr()) |zig_object| {
36993940 for (zig_object.atoms.items) |atom_index| {
37003941 const atom_ptr = self.atom(atom_index) orelse continue;
3701 if (!atom_ptr.flags.alive) continue;
3702 const out_shndx = atom_ptr.outputShndx() orelse continue;
3703 atom_ptr.output_section_index = backlinks[out_shndx];
3942 atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index];
37043943 }
37053944
37063945 for (zig_object.locals()) |local_index| {
37073946 const local = self.symbol(local_index);
3708 const atom_ptr = local.atom(self) orelse continue;
3709 if (!atom_ptr.flags.alive) continue;
3710 const out_shndx = local.outputShndx() orelse continue;
3711 local.output_section_index = backlinks[out_shndx];
3947 local.output_section_index = backlinks[local.output_section_index];
37123948 }
37133949
37143950 for (zig_object.globals()) |global_index| {
37153951 const global = self.symbol(global_index);
37163952 const atom_ptr = global.atom(self) orelse continue;
37173953 if (!atom_ptr.flags.alive) continue;
3954 // TODO claim unresolved for objects
37183955 if (global.file(self).?.index() != zig_object.index) continue;
37193956 const out_shndx = global.outputShndx() orelse continue;
37203957 global.output_section_index = backlinks[out_shndx];
......@@ -3737,6 +3974,10 @@ fn updateSectionSizes(self: *Elf) !void {
37373974 }
37383975 }
37393976
3977 if (self.zigObjectPtr()) |zig_object| {
3978 zig_object.updateRelaSectionSizes(self);
3979 }
3980
37403981 if (self.eh_frame_section_index) |index| {
37413982 self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
37423983 }
......@@ -3801,7 +4042,7 @@ fn updateSectionSizes(self: *Elf) !void {
38014042 }
38024043
38034044 if (self.dynstrtab_section_index) |index| {
3804 self.shdrs.items[index].sh_size = self.dynstrtab.buffer.items.len;
4045 self.shdrs.items[index].sh_size = self.dynstrtab.items.len;
38054046 }
38064047
38074048 if (self.versym_section_index) |index| {
......@@ -3812,30 +4053,13 @@ fn updateSectionSizes(self: *Elf) !void {
38124053 self.shdrs.items[index].sh_size = self.verneed.size();
38134054 }
38144055
3815 if (self.symtab_section_index != null) {
3816 try self.updateSymtabSize();
3817 }
3818
3819 if (self.strtab_section_index) |index| {
3820 // TODO I don't really this here but we need it to add symbol names from GOT and other synthetic
3821 // sections into .strtab for easier debugging.
3822 if (self.zig_got_section_index) |_| {
3823 try self.zig_got.updateStrtab(self);
3824 }
3825 if (self.got_section_index) |_| {
3826 try self.got.updateStrtab(self);
3827 }
3828 if (self.plt_section_index) |_| {
3829 try self.plt.updateStrtab(self);
3830 }
3831 if (self.plt_got_section_index) |_| {
3832 try self.plt_got.updateStrtab(self);
3833 }
3834 self.shdrs.items[index].sh_size = self.strtab.buffer.items.len;
3835 }
4056 try self.updateSymtabSize();
4057 self.updateShStrtabSize();
4058}
38364059
4060fn updateShStrtabSize(self: *Elf) void {
38374061 if (self.shstrtab_section_index) |index| {
3838 self.shdrs.items[index].sh_size = self.shstrtab.buffer.items.len;
4062 self.shdrs.items[index].sh_size = self.shstrtab.items.len;
38394063 }
38404064}
38414065
......@@ -4074,7 +4298,7 @@ fn allocateNonAllocSections(self: *Elf) !void {
40744298
40754299 if (self.isDebugSection(@intCast(shndx))) {
40764300 log.debug("moving {s} from 0x{x} to 0x{x}", .{
4077 self.shstrtab.getAssumeExists(shdr.sh_name),
4301 self.getShString(shdr.sh_name),
40784302 shdr.sh_offset,
40794303 new_offset,
40804304 });
......@@ -4187,7 +4411,7 @@ fn writeAtoms(self: *Elf) !void {
41874411
41884412 const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
41894413
4190 log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)});
4414 log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
41914415
41924416 // TODO really, really handle debug section separately
41934417 const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
......@@ -4256,65 +4480,70 @@ fn updateSymtabSize(self: *Elf) !void {
42564480 var sizes = SymtabSize{};
42574481
42584482 if (self.zigObjectPtr()) |zig_object| {
4259 zig_object.updateSymtabSize(self);
4260 sizes.nlocals += zig_object.output_symtab_size.nlocals;
4261 sizes.nglobals += zig_object.output_symtab_size.nglobals;
4483 zig_object.asFile().updateSymtabSize(self);
4484 sizes.add(zig_object.output_symtab_size);
42624485 }
42634486
42644487 for (self.objects.items) |index| {
4265 const object = self.file(index).?.object;
4266 object.updateSymtabSize(self);
4267 sizes.nlocals += object.output_symtab_size.nlocals;
4268 sizes.nglobals += object.output_symtab_size.nglobals;
4488 const file_ptr = self.file(index).?;
4489 file_ptr.updateSymtabSize(self);
4490 sizes.add(file_ptr.object.output_symtab_size);
42694491 }
42704492
42714493 for (self.shared_objects.items) |index| {
4272 const shared_object = self.file(index).?.shared_object;
4273 shared_object.updateSymtabSize(self);
4274 sizes.nglobals += shared_object.output_symtab_size.nglobals;
4494 const file_ptr = self.file(index).?;
4495 file_ptr.updateSymtabSize(self);
4496 sizes.add(file_ptr.shared_object.output_symtab_size);
42754497 }
42764498
42774499 if (self.zig_got_section_index) |_| {
42784500 self.zig_got.updateSymtabSize(self);
4279 sizes.nlocals += self.zig_got.output_symtab_size.nlocals;
4501 sizes.add(self.zig_got.output_symtab_size);
42804502 }
42814503
42824504 if (self.got_section_index) |_| {
42834505 self.got.updateSymtabSize(self);
4284 sizes.nlocals += self.got.output_symtab_size.nlocals;
4506 sizes.add(self.got.output_symtab_size);
42854507 }
42864508
42874509 if (self.plt_section_index) |_| {
42884510 self.plt.updateSymtabSize(self);
4289 sizes.nlocals += self.plt.output_symtab_size.nlocals;
4511 sizes.add(self.plt.output_symtab_size);
42904512 }
42914513
42924514 if (self.plt_got_section_index) |_| {
42934515 self.plt_got.updateSymtabSize(self);
4294 sizes.nlocals += self.plt_got.output_symtab_size.nlocals;
4516 sizes.add(self.plt_got.output_symtab_size);
42954517 }
42964518
42974519 if (self.linker_defined_index) |index| {
4298 const linker_defined = self.file(index).?.linker_defined;
4299 linker_defined.updateSymtabSize(self);
4300 sizes.nlocals += linker_defined.output_symtab_size.nlocals;
4520 const file_ptr = self.file(index).?;
4521 file_ptr.updateSymtabSize(self);
4522 sizes.add(file_ptr.linker_defined.output_symtab_size);
43014523 }
43024524
4303 const shdr = &self.shdrs.items[self.symtab_section_index.?];
4304 shdr.sh_info = sizes.nlocals + 1;
4305 shdr.sh_link = self.strtab_section_index.?;
4525 const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?];
4526 symtab_shdr.sh_info = sizes.nlocals + 1;
4527 symtab_shdr.sh_link = self.strtab_section_index.?;
43064528
43074529 const sym_size: u64 = switch (self.ptr_width) {
43084530 .p32 => @sizeOf(elf.Elf32_Sym),
43094531 .p64 => @sizeOf(elf.Elf64_Sym),
43104532 };
43114533 const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size;
4312 shdr.sh_size = needed_size;
4534 symtab_shdr.sh_size = needed_size;
4535
4536 const strtab = &self.shdrs.items[self.strtab_section_index.?];
4537 strtab.sh_size = sizes.strsize + 1;
43134538}
43144539
43154540fn writeSyntheticSections(self: *Elf) !void {
43164541 const gpa = self.base.allocator;
43174542
4543 if (self.zigObjectPtr()) |zig_object| {
4544 try zig_object.writeRelaSections(self);
4545 }
4546
43184547 if (self.interp_section_index) |shndx| {
43194548 const shdr = self.shdrs.items[shndx];
43204549 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
......@@ -4370,7 +4599,7 @@ fn writeSyntheticSections(self: *Elf) !void {
43704599
43714600 if (self.dynstrtab_section_index) |shndx| {
43724601 const shdr = self.shdrs.items[shndx];
4373 try self.base.file.?.pwriteAll(self.dynstrtab.buffer.items, shdr.sh_offset);
4602 try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset);
43744603 }
43754604
43764605 if (self.eh_frame_section_index) |shndx| {
......@@ -4438,94 +4667,97 @@ fn writeSyntheticSections(self: *Elf) !void {
44384667 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset);
44394668 }
44404669
4441 if (self.shstrtab_section_index) |index| {
4442 const shdr = self.shdrs.items[index];
4443 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shdr.sh_offset);
4444 }
4670 try self.writeSymtab();
4671 try self.writeShStrtab();
4672}
44454673
4446 if (self.strtab_section_index) |index| {
4674fn writeShStrtab(self: *Elf) !void {
4675 if (self.shstrtab_section_index) |index| {
44474676 const shdr = self.shdrs.items[index];
4448 try self.base.file.?.pwriteAll(self.strtab.buffer.items, shdr.sh_offset);
4449 }
4450
4451 if (self.symtab_section_index) |_| {
4452 try self.writeSymtab();
4677 try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset);
44534678 }
44544679}
44554680
44564681fn writeSymtab(self: *Elf) !void {
44574682 const gpa = self.base.allocator;
4458 const shdr = &self.shdrs.items[self.symtab_section_index.?];
4683 const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];
4684 const strtab_shdr = self.shdrs.items[self.strtab_section_index.?];
44594685 const sym_size: u64 = switch (self.ptr_width) {
44604686 .p32 => @sizeOf(elf.Elf32_Sym),
44614687 .p64 => @sizeOf(elf.Elf64_Sym),
44624688 };
4463 const nsyms = math.cast(usize, @divExact(shdr.sh_size, sym_size)) orelse return error.Overflow;
4689 const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow;
44644690
4465 log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset });
4691 log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset });
44664692
4467 const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms);
4468 defer gpa.free(symtab);
4469 symtab[0] = null_sym;
4693 try self.symtab.resize(gpa, nsyms);
4694 const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow;
4695 try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size);
44704696
4471 var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{
4697 const Ctx = struct {
4698 ilocal: usize,
4699 iglobal: usize,
4700
4701 fn incr(this: *@This(), ss: SymtabSize) void {
4702 this.ilocal += ss.nlocals;
4703 this.iglobal += ss.nglobals;
4704 }
4705 };
4706 var ctx: Ctx = .{
44724707 .ilocal = 1,
4473 .iglobal = shdr.sh_info,
4474 .symtab = symtab,
4708 .iglobal = symtab_shdr.sh_info,
44754709 };
44764710
44774711 if (self.zigObjectPtr()) |zig_object| {
4478 zig_object.writeSymtab(self, ctx);
4479 ctx.ilocal += zig_object.output_symtab_size.nlocals;
4480 ctx.iglobal += zig_object.output_symtab_size.nglobals;
4712 zig_object.asFile().writeSymtab(self, ctx);
4713 ctx.incr(zig_object.output_symtab_size);
44814714 }
44824715
44834716 for (self.objects.items) |index| {
4484 const object = self.file(index).?.object;
4485 object.writeSymtab(self, ctx);
4486 ctx.ilocal += object.output_symtab_size.nlocals;
4487 ctx.iglobal += object.output_symtab_size.nglobals;
4717 const file_ptr = self.file(index).?;
4718 file_ptr.writeSymtab(self, ctx);
4719 ctx.incr(file_ptr.object.output_symtab_size);
44884720 }
44894721
44904722 for (self.shared_objects.items) |index| {
4491 const shared_object = self.file(index).?.shared_object;
4492 shared_object.writeSymtab(self, ctx);
4493 ctx.iglobal += shared_object.output_symtab_size.nglobals;
4723 const file_ptr = self.file(index).?;
4724 file_ptr.writeSymtab(self, ctx);
4725 ctx.incr(file_ptr.shared_object.output_symtab_size);
44944726 }
44954727
44964728 if (self.zig_got_section_index) |_| {
4497 try self.zig_got.writeSymtab(self, ctx);
4498 ctx.ilocal += self.zig_got.output_symtab_size.nlocals;
4729 self.zig_got.writeSymtab(self, ctx);
4730 ctx.incr(self.zig_got.output_symtab_size);
44994731 }
45004732
45014733 if (self.got_section_index) |_| {
4502 try self.got.writeSymtab(self, ctx);
4503 ctx.ilocal += self.got.output_symtab_size.nlocals;
4734 self.got.writeSymtab(self, ctx);
4735 ctx.incr(self.got.output_symtab_size);
45044736 }
45054737
45064738 if (self.plt_section_index) |_| {
4507 try self.plt.writeSymtab(self, ctx);
4508 ctx.ilocal += self.plt.output_symtab_size.nlocals;
4739 self.plt.writeSymtab(self, ctx);
4740 ctx.incr(self.plt.output_symtab_size);
45094741 }
45104742
45114743 if (self.plt_got_section_index) |_| {
4512 try self.plt_got.writeSymtab(self, ctx);
4513 ctx.ilocal += self.plt_got.output_symtab_size.nlocals;
4744 self.plt_got.writeSymtab(self, ctx);
4745 ctx.incr(self.plt_got.output_symtab_size);
45144746 }
45154747
45164748 if (self.linker_defined_index) |index| {
4517 const linker_defined = self.file(index).?.linker_defined;
4518 linker_defined.writeSymtab(self, ctx);
4519 ctx.ilocal += linker_defined.output_symtab_size.nlocals;
4749 const file_ptr = self.file(index).?;
4750 file_ptr.writeSymtab(self, ctx);
4751 ctx.incr(file_ptr.linker_defined.output_symtab_size);
45204752 }
45214753
45224754 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
45234755 switch (self.ptr_width) {
45244756 .p32 => {
4525 const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len);
4757 const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len);
45264758 defer gpa.free(buf);
45274759
4528 for (buf, symtab) |*out, sym| {
4760 for (buf, self.symtab.items) |*out, sym| {
45294761 out.* = .{
45304762 .st_name = sym.st_name,
45314763 .st_info = sym.st_info,
......@@ -4536,15 +4768,17 @@ fn writeSymtab(self: *Elf) !void {
45364768 };
45374769 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
45384770 }
4539 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);
4771 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);
45404772 },
45414773 .p64 => {
45424774 if (foreign_endian) {
4543 for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
4775 for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
45444776 }
4545 try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset);
4777 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset);
45464778 },
45474779 }
4780
4781 try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset);
45484782}
45494783
45504784/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
......@@ -4848,18 +5082,30 @@ pub fn isStatic(self: Elf) bool {
48485082 return self.base.options.link_mode == .Static;
48495083}
48505084
5085pub fn isObject(self: Elf) bool {
5086 return self.base.options.output_mode == .Obj;
5087}
5088
48515089pub fn isExe(self: Elf) bool {
4852 return self.base.options.effectiveOutputMode() == .Exe;
5090 return self.base.options.output_mode == .Exe;
5091}
5092
5093pub fn isStaticLib(self: Elf) bool {
5094 return self.base.options.output_mode == .Lib and self.isStatic();
5095}
5096
5097pub fn isRelocatable(self: Elf) bool {
5098 return self.isObject() or self.isStaticLib();
48535099}
48545100
48555101pub fn isDynLib(self: Elf) bool {
4856 return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic;
5102 return self.base.options.output_mode == .Lib and !self.isStatic();
48575103}
48585104
48595105pub fn isZigSection(self: Elf, shndx: u16) bool {
48605106 inline for (&[_]?u16{
48615107 self.zig_text_section_index,
4862 self.zig_rodata_section_index,
5108 self.zig_data_rel_ro_section_index,
48635109 self.zig_data_section_index,
48645110 self.zig_bss_section_index,
48655111 self.zig_got_section_index,
......@@ -4909,6 +5155,26 @@ fn addPhdr(self: *Elf, opts: struct {
49095155 return index;
49105156}
49115157
5158fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
5159 const entsize: u64 = switch (self.ptr_width) {
5160 .p32 => @sizeOf(elf.Elf32_Rela),
5161 .p64 => @sizeOf(elf.Elf64_Rela),
5162 };
5163 const addralign: u64 = switch (self.ptr_width) {
5164 .p32 => @alignOf(elf.Elf32_Rela),
5165 .p64 => @alignOf(elf.Elf64_Rela),
5166 };
5167 return self.addSection(.{
5168 .name = name,
5169 .type = elf.SHT_RELA,
5170 .flags = elf.SHF_INFO_LINK,
5171 .entsize = entsize,
5172 .info = shndx,
5173 .addralign = addralign,
5174 .offset = std.math.maxInt(u64),
5175 });
5176}
5177
49125178pub const AddSectionOpts = struct {
49135179 name: [:0]const u8,
49145180 type: u32 = elf.SHT_NULL,
......@@ -4925,7 +5191,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 {
49255191 const index = @as(u16, @intCast(self.shdrs.items.len));
49265192 const shdr = try self.shdrs.addOne(gpa);
49275193 shdr.* = .{
4928 .sh_name = try self.shstrtab.insert(gpa, opts.name),
5194 .sh_name = try self.insertShString(opts.name),
49295195 .sh_type = opts.type,
49305196 .sh_flags = opts.flags,
49315197 .sh_addr = 0,
......@@ -4941,7 +5207,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 {
49415207
49425208pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
49435209 for (self.shdrs.items, 0..) |*shdr, i| {
4944 const this_name = self.shstrtab.getAssumeExists(shdr.sh_name);
5210 const this_name = self.getShString(shdr.sh_name);
49455211 if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i));
49465212 } else return null;
49475213}
......@@ -5114,13 +5380,15 @@ const GetOrPutGlobalResult = struct {
51145380 index: Symbol.Index,
51155381};
51165382
5117pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult {
5383pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult {
51185384 const gpa = self.base.allocator;
5385 const name_off = try self.strings.insert(gpa, name);
51195386 const gop = try self.resolver.getOrPut(gpa, name_off);
51205387 if (!gop.found_existing) {
51215388 const index = try self.addSymbol();
51225389 const global = self.symbol(index);
51235390 global.name_offset = name_off;
5391 global.flags.global = true;
51245392 gop.value_ptr.* = index;
51255393 }
51265394 return .{
......@@ -5130,7 +5398,7 @@ pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult {
51305398}
51315399
51325400pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index {
5133 const name_off = self.strtab.getOffset(name) orelse return null;
5401 const name_off = self.strings.getOffset(name) orelse return null;
51345402 return self.resolver.get(name_off);
51355403}
51365404
......@@ -5148,8 +5416,9 @@ const GetOrCreateComdatGroupOwnerResult = struct {
51485416 index: ComdatGroupOwner.Index,
51495417};
51505418
5151pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult {
5419pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateComdatGroupOwnerResult {
51525420 const gpa = self.base.allocator;
5421 const off = try self.strings.insert(gpa, name);
51535422 const gop = try self.comdat_groups_table.getOrPut(gpa, off);
51545423 if (!gop.found_existing) {
51555424 const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len));
......@@ -5239,6 +5508,30 @@ fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMem
52395508 return .{ .index = index };
52405509}
52415510
5511pub fn getShString(self: Elf, off: u32) [:0]const u8 {
5512 assert(off < self.shstrtab.items.len);
5513 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
5514}
5515
5516pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {
5517 const off = @as(u32, @intCast(self.shstrtab.items.len));
5518 try self.shstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1);
5519 self.shstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable;
5520 return off;
5521}
5522
5523pub fn getDynString(self: Elf, off: u32) [:0]const u8 {
5524 assert(off < self.dynstrtab.items.len);
5525 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.dynstrtab.items.ptr + off)), 0);
5526}
5527
5528pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
5529 const off = @as(u32, @intCast(self.dynstrtab.items.len));
5530 try self.dynstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1);
5531 self.dynstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable;
5532 return off;
5533}
5534
52425535fn reportUndefined(self: *Elf, undefs: anytype) !void {
52435536 const gpa = self.base.allocator;
52445537 const max_notes = 4;
......@@ -5340,8 +5633,8 @@ fn formatShdr(
53405633 _ = unused_fmt_string;
53415634 const shdr = ctx.shdr;
53425635 try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{
5343 ctx.elf_file.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,
5344 shdr.sh_addr, shdr.sh_addralign,
5636 ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,
5637 shdr.sh_addr, shdr.sh_addralign,
53455638 shdr.sh_size,
53465639 });
53475640}
......@@ -5444,6 +5737,7 @@ fn fmtDumpState(
54445737 }
54455738 try writer.print("{}\n", .{self.got.fmt(self)});
54465739 try writer.print("{}\n", .{self.zig_got.fmt(self)});
5740
54475741 try writer.writeAll("Output shdrs\n");
54485742 for (self.shdrs.items, 0..) |shdr, shndx| {
54495743 try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{
......@@ -5516,6 +5810,13 @@ pub const ComdatGroup = struct {
55165810pub const SymtabSize = struct {
55175811 nlocals: u32 = 0,
55185812 nglobals: u32 = 0,
5813 strsize: u32 = 0,
5814
5815 fn add(ss: *SymtabSize, other: SymtabSize) void {
5816 ss.nlocals += other.nlocals;
5817 ss.nglobals += other.nglobals;
5818 ss.strsize += other.strsize;
5819 }
55195820};
55205821
55215822pub const null_sym = elf.Elf64_Sym{
......@@ -5621,7 +5922,7 @@ const PltSection = synthetic_sections.PltSection;
56215922const PltGotSection = synthetic_sections.PltGotSection;
56225923const SharedObject = @import("Elf/SharedObject.zig");
56235924const Symbol = @import("Elf/Symbol.zig");
5624const StringTable = @import("strtab.zig").StringTable;
5925const StringTable = @import("StringTable.zig");
56255926const TypedValue = @import("../TypedValue.zig");
56265927const VerneedSection = synthetic_sections.VerneedSection;
56275928const ZigGotSection = synthetic_sections.ZigGotSection;
src/link/Elf/Archive.zig+272-63
......@@ -4,20 +4,150 @@ data: []const u8,
44objects: std.ArrayListUnmanaged(Object) = .{},
55strtab: []const u8 = &[0]u8{},
66
7pub fn isArchive(path: []const u8) !bool {
8 const file = try std.fs.cwd().openFile(path, .{});
9 defer file.close();
10 const reader = file.reader();
11 const magic = reader.readBytesNoEof(SARMAG) catch return false;
12 if (!mem.eql(u8, &magic, ARMAG)) return false;
13 return true;
14}
15
16pub fn deinit(self: *Archive, allocator: Allocator) void {
17 allocator.free(self.path);
18 allocator.free(self.data);
19 self.objects.deinit(allocator);
20}
21
22pub fn parse(self: *Archive, elf_file: *Elf) !void {
23 const gpa = elf_file.base.allocator;
24
25 var stream = std.io.fixedBufferStream(self.data);
26 const reader = stream.reader();
27 _ = try reader.readBytesNoEof(SARMAG);
28
29 while (true) {
30 if (stream.pos >= self.data.len) break;
31
32 if (stream.pos % 2 != 0) {
33 stream.pos += 1;
34 }
35 const hdr = try reader.readStruct(ar_hdr);
36
37 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
38 // TODO convert into an error
39 log.debug(
40 "{s}: invalid header delimiter: expected '{s}', found '{s}'",
41 .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) },
42 );
43 return;
44 }
45
46 const size = try hdr.size();
47 defer {
48 _ = stream.seekBy(size) catch {};
49 }
50
51 if (hdr.isSymtab()) continue;
52 if (hdr.isStrtab()) {
53 self.strtab = self.data[stream.pos..][0..size];
54 continue;
55 }
56
57 const name = ar_hdr.getValue(&hdr.ar_name);
58
59 if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
60
61 const object_name = blk: {
62 if (name[0] == '/') {
63 const off = try std.fmt.parseInt(u32, name[1..], 10);
64 const object_name = self.getString(off);
65 break :blk try gpa.dupe(u8, object_name[0 .. object_name.len - 1]); // To account for trailing '/'
66 }
67 break :blk try gpa.dupe(u8, name);
68 };
69
70 const object = Object{
71 .archive = try gpa.dupe(u8, self.path),
72 .path = object_name,
73 .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
74 .index = undefined,
75 .alive = false,
76 };
77
78 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });
79
80 try self.objects.append(gpa, object);
81 }
82}
83
84fn getString(self: Archive, off: u32) []const u8 {
85 assert(off < self.strtab.len);
86 return mem.sliceTo(@as([*:strtab_delimiter]const u8, @ptrCast(self.strtab.ptr + off)), 0);
87}
88
89pub fn setArHdr(opts: struct {
90 name: union(enum) {
91 symtab: void,
92 strtab: void,
93 name: []const u8,
94 name_off: u32,
95 },
96 size: u32,
97}) ar_hdr {
98 var hdr: ar_hdr = .{
99 .ar_name = undefined,
100 .ar_date = undefined,
101 .ar_uid = undefined,
102 .ar_gid = undefined,
103 .ar_mode = undefined,
104 .ar_size = undefined,
105 .ar_fmag = undefined,
106 };
107 @memset(mem.asBytes(&hdr), 0x20);
108 @memcpy(&hdr.ar_fmag, Archive.ARFMAG);
109
110 {
111 var stream = std.io.fixedBufferStream(&hdr.ar_name);
112 const writer = stream.writer();
113 switch (opts.name) {
114 .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable,
115 .strtab => writer.print("//", .{}) catch unreachable,
116 .name => |x| writer.print("{s}", .{x}) catch unreachable,
117 .name_off => |x| writer.print("/{d}", .{x}) catch unreachable,
118 }
119 }
120 {
121 var stream = std.io.fixedBufferStream(&hdr.ar_size);
122 stream.writer().print("{d}", .{opts.size}) catch unreachable;
123 }
124
125 return hdr;
126}
127
7128// Archive files start with the ARMAG identifying string. Then follows a
8129// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
9130// member indicates, for each member file.
10131/// String that begins an archive file.
11132pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
12133/// Size of that string.
13pub const SARMAG: u4 = 8;
134pub const SARMAG = 8;
14135
15136/// String in ar_fmag at the end of each header.
16137const ARFMAG: *const [2:0]u8 = "`\n";
17138
139/// Strtab identifier
140const STRNAME: *const [2:0]u8 = "//";
141
142/// 32-bit symtab identifier
143const SYMNAME: *const [1:0]u8 = "/";
144
145/// 64-bit symtab identifier
18146const SYM64NAME: *const [7:0]u8 = "/SYM64/";
19147
20const ar_hdr = extern struct {
148const strtab_delimiter = '\n';
149
150pub const ar_hdr = extern struct {
21151 /// Member file name, sometimes / terminated.
22152 ar_name: [16]u8,
23153
......@@ -54,93 +184,170 @@ const ar_hdr = extern struct {
54184 }
55185
56186 fn isStrtab(self: ar_hdr) bool {
57 return mem.eql(u8, getValue(&self.ar_name), "//");
187 return mem.eql(u8, getValue(&self.ar_name), STRNAME);
58188 }
59189
60190 fn isSymtab(self: ar_hdr) bool {
61 return mem.eql(u8, getValue(&self.ar_name), "/");
191 return mem.eql(u8, getValue(&self.ar_name), SYMNAME) or mem.eql(u8, getValue(&self.ar_name), SYM64NAME);
62192 }
63193};
64194
65pub fn isArchive(path: []const u8) !bool {
66 const file = try std.fs.cwd().openFile(path, .{});
67 defer file.close();
68 const reader = file.reader();
69 const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false;
70 if (!mem.eql(u8, &magic, ARMAG)) return false;
71 return true;
72}
195pub const ArSymtab = struct {
196 symtab: std.ArrayListUnmanaged(Entry) = .{},
197 strtab: StringTable = .{},
73198
74pub fn deinit(self: *Archive, allocator: Allocator) void {
75 allocator.free(self.path);
76 allocator.free(self.data);
77 self.objects.deinit(allocator);
78}
199 pub fn deinit(ar: *ArSymtab, allocator: Allocator) void {
200 ar.symtab.deinit(allocator);
201 ar.strtab.deinit(allocator);
202 }
79203
80pub fn parse(self: *Archive, elf_file: *Elf) !void {
81 const gpa = elf_file.base.allocator;
204 pub fn sort(ar: *ArSymtab) void {
205 mem.sort(Entry, ar.symtab.items, {}, Entry.lessThan);
206 }
82207
83 var stream = std.io.fixedBufferStream(self.data);
84 const reader = stream.reader();
85 _ = try reader.readBytesNoEof(SARMAG);
208 pub fn size(ar: ArSymtab, kind: enum { p32, p64 }) usize {
209 const ptr_size: usize = switch (kind) {
210 .p32 => 4,
211 .p64 => 8,
212 };
213 var ss: usize = ptr_size + ar.symtab.items.len * ptr_size;
214 for (ar.symtab.items) |entry| {
215 ss += ar.strtab.getAssumeExists(entry.off).len + 1;
216 }
217 return ss;
218 }
86219
87 while (true) {
88 if (stream.pos % 2 != 0) {
89 stream.pos += 1;
220 pub fn write(ar: ArSymtab, kind: enum { p32, p64 }, elf_file: *Elf, writer: anytype) !void {
221 assert(kind == .p64); // TODO p32
222 const hdr = setArHdr(.{ .name = .symtab, .size = @intCast(ar.size(.p64)) });
223 try writer.writeAll(mem.asBytes(&hdr));
224
225 const gpa = elf_file.base.allocator;
226 var offsets = std.AutoHashMap(File.Index, u64).init(gpa);
227 defer offsets.deinit();
228 try offsets.ensureUnusedCapacity(@intCast(elf_file.objects.items.len + 1));
229
230 if (elf_file.zigObjectPtr()) |zig_object| {
231 offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off);
90232 }
91233
92 const hdr = reader.readStruct(ar_hdr) catch break;
234 // Number of symbols
235 try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big);
93236
94 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
95 // TODO convert into an error
96 log.debug(
97 "{s}: invalid header delimiter: expected '{s}', found '{s}'",
98 .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) },
99 );
100 return;
237 // Offsets to files
238 for (ar.symtab.items) |entry| {
239 const off = offsets.get(entry.file_index).?;
240 try writer.writeInt(u64, off, .big);
101241 }
102242
103 const size = try hdr.size();
104 defer {
105 _ = stream.seekBy(size) catch {};
243 // Strings
244 for (ar.symtab.items) |entry| {
245 try writer.print("{s}\x00", .{ar.strtab.getAssumeExists(entry.off)});
106246 }
247 }
107248
108 if (hdr.isSymtab()) continue;
109 if (hdr.isStrtab()) {
110 self.strtab = self.data[stream.pos..][0..size];
111 continue;
249 pub fn format(
250 ar: ArSymtab,
251 comptime unused_fmt_string: []const u8,
252 options: std.fmt.FormatOptions,
253 writer: anytype,
254 ) !void {
255 _ = ar;
256 _ = unused_fmt_string;
257 _ = options;
258 _ = writer;
259 @compileError("do not format ar symtab directly; use fmt instead");
260 }
261
262 const FormatContext = struct {
263 ar: ArSymtab,
264 elf_file: *Elf,
265 };
266
267 pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(format2) {
268 return .{ .data = .{
269 .ar = ar,
270 .elf_file = elf_file,
271 } };
272 }
273
274 fn format2(
275 ctx: FormatContext,
276 comptime unused_fmt_string: []const u8,
277 options: std.fmt.FormatOptions,
278 writer: anytype,
279 ) !void {
280 _ = unused_fmt_string;
281 _ = options;
282 const ar = ctx.ar;
283 const elf_file = ctx.elf_file;
284 for (ar.symtab.items, 0..) |entry, i| {
285 const name = ar.strtab.getAssumeExists(entry.off);
286 const file = elf_file.file(entry.file_index).?;
287 try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file_index, file.fmtPath() });
112288 }
289 }
113290
114 const name = ar_hdr.getValue(&hdr.ar_name);
291 const Entry = struct {
292 /// Offset into the string table.
293 off: u32,
294 /// Index of the file defining the global.
295 file_index: File.Index,
115296
116 if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
297 pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool {
298 _ = ctx;
299 if (lhs.off == rhs.off) return lhs.file_index < rhs.file_index;
300 return lhs.off < rhs.off;
301 }
302 };
303};
117304
118 const object_name = blk: {
119 if (name[0] == '/') {
120 const off = try std.fmt.parseInt(u32, name[1..], 10);
121 break :blk self.getString(off);
122 }
123 break :blk name;
124 };
305pub const ArStrtab = struct {
306 buffer: std.ArrayListUnmanaged(u8) = .{},
125307
126 const object = Object{
127 .archive = try gpa.dupe(u8, self.path),
128 .path = try gpa.dupe(u8, object_name[0 .. object_name.len - 1]), // To account for trailing '/'
129 .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
130 .index = undefined,
131 .alive = false,
132 };
308 pub fn deinit(ar: *ArStrtab, allocator: Allocator) void {
309 ar.buffer.deinit(allocator);
310 }
133311
134 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });
312 pub fn insert(ar: *ArStrtab, allocator: Allocator, name: []const u8) error{OutOfMemory}!u32 {
313 const off = @as(u32, @intCast(ar.buffer.items.len));
314 try ar.buffer.writer(allocator).print("{s}/{c}", .{ name, strtab_delimiter });
315 return off;
316 }
135317
136 try self.objects.append(gpa, object);
318 pub fn size(ar: ArStrtab) usize {
319 return ar.buffer.items.len;
137320 }
138}
139321
140fn getString(self: Archive, off: u32) []const u8 {
141 assert(off < self.strtab.len);
142 return mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0);
143}
322 pub fn write(ar: ArStrtab, writer: anytype) !void {
323 const hdr = setArHdr(.{ .name = .strtab, .size = @intCast(ar.size()) });
324 try writer.writeAll(mem.asBytes(&hdr));
325 try writer.writeAll(ar.buffer.items);
326 }
327
328 pub fn format(
329 ar: ArStrtab,
330 comptime unused_fmt_string: []const u8,
331 options: std.fmt.FormatOptions,
332 writer: anytype,
333 ) !void {
334 _ = unused_fmt_string;
335 _ = options;
336 try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)});
337 }
338};
339
340pub const ArState = struct {
341 /// Name offset in the string table.
342 name_off: u32 = 0,
343
344 /// File offset of the ar_hdr describing the contributing
345 /// object in the archive.
346 file_off: u64 = 0,
347
348 /// Total size of the contributing object (excludes ar_hdr).
349 size: u64 = 0,
350};
144351
145352const std = @import("std");
146353const assert = std.debug.assert;
......@@ -152,4 +359,6 @@ const mem = std.mem;
152359const Allocator = mem.Allocator;
153360const Archive = @This();
154361const Elf = @import("../Elf.zig");
362const File = @import("file.zig").File;
155363const Object = @import("Object.zig");
364const StringTable = @import("../StringTable.zig");
src/link/Elf/Atom.zig+7-3
......@@ -42,7 +42,10 @@ next_index: Index = 0,
4242pub const Alignment = @import("../../InternPool.zig").Alignment;
4343
4444pub fn name(self: Atom, elf_file: *Elf) []const u8 {
45 return elf_file.strtab.getAssumeExists(self.name_offset);
45 const file_ptr = self.file(elf_file).?;
46 return switch (file_ptr) {
47 inline else => |x| x.getString(self.name_offset),
48 };
4649}
4750
4851pub fn file(self: Atom, elf_file: *Elf) ?File {
......@@ -602,7 +605,8 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
602605}
603606
604607fn outputType(elf_file: *Elf) u2 {
605 return switch (elf_file.base.options.effectiveOutputMode()) {
608 assert(!elf_file.isRelocatable());
609 return switch (elf_file.base.options.output_mode) {
606610 .Obj => unreachable,
607611 .Lib => 0,
608612 .Exe => if (elf_file.base.options.pie) 1 else 2,
......@@ -692,7 +696,7 @@ fn reportUndefined(
692696) !void {
693697 const rel_esym = switch (self.file(elf_file).?) {
694698 .zig_object => |x| x.elfSym(rel.r_sym()).*,
695 .object => |x| x.symtab[rel.r_sym()],
699 .object => |x| x.symtab.items[rel.r_sym()],
696700 else => unreachable,
697701 };
698702 const esym = sym.elfSym(elf_file);
src/link/Elf/LinkerDefined.zig+15-27
......@@ -1,11 +1,13 @@
11index: File.Index,
22symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
3strtab: std.ArrayListUnmanaged(u8) = .{},
34symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
45
56output_symtab_size: Elf.SymtabSize = .{},
67
78pub fn deinit(self: *LinkerDefined, allocator: Allocator) void {
89 self.symtab.deinit(allocator);
10 self.strtab.deinit(allocator);
911 self.symbols.deinit(allocator);
1012}
1113
......@@ -13,16 +15,17 @@ pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32
1315 const gpa = elf_file.base.allocator;
1416 try self.symtab.ensureUnusedCapacity(gpa, 1);
1517 try self.symbols.ensureUnusedCapacity(gpa, 1);
18 const name_off = @as(u32, @intCast(self.strtab.items.len));
19 try self.strtab.writer(gpa).print("{s}\x00", .{name});
1620 self.symtab.appendAssumeCapacity(.{
17 .st_name = try elf_file.strtab.insert(gpa, name),
21 .st_name = name_off,
1822 .st_info = elf.STB_GLOBAL << 4,
1923 .st_other = @intFromEnum(elf.STV.HIDDEN),
2024 .st_shndx = elf.SHN_ABS,
2125 .st_value = 0,
2226 .st_size = 0,
2327 });
24 const off = try elf_file.strtab.insert(gpa, name);
25 const gop = try elf_file.getOrPutGlobal(off);
28 const gop = try elf_file.getOrPutGlobal(name);
2629 self.symbols.addOneAssumeCapacity().* = gop.index;
2730 return gop.index;
2831}
......@@ -37,7 +40,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
3740 const global = elf_file.symbol(index);
3841 if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
3942 global.value = 0;
40 global.name_offset = global.name_offset;
4143 global.atom_index = 0;
4244 global.file_index = self.index;
4345 global.esym_index = sym_idx;
......@@ -46,26 +48,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
4648 }
4749}
4850
49pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
50 for (self.globals()) |global_index| {
51 const global = elf_file.symbol(global_index);
52 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
53 global.flags.output_symtab = true;
54 self.output_symtab_size.nlocals += 1;
55 }
56}
57
58pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void {
59 var ilocal = ctx.ilocal;
60 for (self.globals()) |global_index| {
61 const global = elf_file.symbol(global_index);
62 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
63 if (!global.flags.output_symtab) continue;
64 global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
65 ilocal += 1;
66 }
67}
68
6951pub fn globals(self: *LinkerDefined) []const Symbol.Index {
7052 return self.symbols.items;
7153}
......@@ -74,6 +56,11 @@ pub fn asFile(self: *LinkerDefined) File {
7456 return .{ .linker_defined = self };
7557}
7658
59pub fn getString(self: LinkerDefined, off: u32) [:0]const u8 {
60 assert(off < self.strtab.items.len);
61 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
62}
63
7764pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
7865 return .{ .data = .{
7966 .self = self,
......@@ -101,12 +88,13 @@ fn formatSymtab(
10188 }
10289}
10390
104const std = @import("std");
91const assert = std.debug.assert;
10592const elf = std.elf;
93const mem = std.mem;
94const std = @import("std");
10695
107const Allocator = std.mem.Allocator;
96const Allocator = mem.Allocator;
10897const Elf = @import("../Elf.zig");
10998const File = @import("file.zig").File;
11099const LinkerDefined = @This();
111// const Object = @import("Object.zig");
112100const Symbol = @import("Symbol.zig");
src/link/Elf/Object.zig+51-116
......@@ -5,11 +5,10 @@ index: File.Index,
55
66header: ?elf.Elf64_Ehdr = null,
77shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
8strings: StringTable(.object_strings) = .{},
9symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
10strtab: []const u8 = &[0]u8{},
11first_global: ?Symbol.Index = null,
128
9symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
10strtab: std.ArrayListUnmanaged(u8) = .{},
11first_global: ?Symbol.Index = null,
1312symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
1413atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
1514comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
......@@ -39,7 +38,8 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
3938 allocator.free(self.path);
4039 allocator.free(self.data);
4140 self.shdrs.deinit(allocator);
42 self.strings.deinit(allocator);
41 self.symtab.deinit(allocator);
42 self.strtab.deinit(allocator);
4343 self.symbols.deinit(allocator);
4444 self.atoms.deinit(allocator);
4545 self.comdat_groups.deinit(allocator);
......@@ -68,7 +68,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
6868 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
6969 }
7070
71 try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
71 try self.strtab.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
7272
7373 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
7474 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
......@@ -79,10 +79,22 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
7979 const shdr = shdrs[index];
8080 self.first_global = shdr.sh_info;
8181
82 const symtab = self.shdrContents(index);
83 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
84 self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
85 self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
82 const raw_symtab = self.shdrContents(index);
83 const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));
84 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
85
86 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
87 try self.strtab.appendSlice(gpa, self.shdrContents(@as(u16, @intCast(shdr.sh_link))));
88
89 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
90 for (symtab) |sym| {
91 const out_sym = self.symtab.addOneAssumeCapacity();
92 out_sym.* = sym;
93 out_sym.st_name = if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION)
94 shdrs[sym.st_shndx].sh_name
95 else
96 sym.st_name + strtab_bias;
97 }
8698 }
8799
88100 try self.initAtoms(elf_file);
......@@ -108,16 +120,16 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
108120
109121 switch (shdr.sh_type) {
110122 elf.SHT_GROUP => {
111 if (shdr.sh_info >= self.symtab.len) {
123 if (shdr.sh_info >= self.symtab.items.len) {
112124 // TODO convert into an error
113125 log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()});
114126 continue;
115127 }
116 const group_info_sym = self.symtab[shdr.sh_info];
128 const group_info_sym = self.symtab.items[shdr.sh_info];
117129 const group_signature = blk: {
118130 if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
119131 const sym_shdr = shdrs[group_info_sym.st_shndx];
120 break :blk self.strings.getAssumeExists(sym_shdr.sh_name);
132 break :blk self.getString(sym_shdr.sh_name);
121133 }
122134 break :blk self.getString(group_info_sym.st_name);
123135 };
......@@ -133,11 +145,8 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
133145 continue;
134146 }
135147
136 // Note the assumption about a global strtab used here to disambiguate common
137 // COMDAT owners.
138148 const gpa = elf_file.base.allocator;
139 const group_signature_off = try elf_file.strtab.insert(gpa, group_signature);
140 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);
149 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature);
141150 const comdat_group_index = try elf_file.addComdatGroup();
142151 const comdat_group = elf_file.comdatGroup(comdat_group_index);
143152 comdat_group.* = .{
......@@ -157,10 +166,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
157166 => {},
158167
159168 else => {
160 const name = self.strings.getAssumeExists(shdr.sh_name);
161169 const shndx = @as(u16, @intCast(i));
162170 if (self.skipShdr(shndx, elf_file)) continue;
163 try self.addAtom(shdr, shndx, name, elf_file);
171 try self.addAtom(shdr, shndx, elf_file);
164172 },
165173 }
166174 }
......@@ -177,17 +185,11 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
177185 };
178186}
179187
180fn addAtom(
181 self: *Object,
182 shdr: ElfShdr,
183 shndx: u16,
184 name: [:0]const u8,
185 elf_file: *Elf,
186) error{OutOfMemory}!void {
188fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOfMemory}!void {
187189 const atom_index = try elf_file.addAtom();
188190 const atom = elf_file.atom(atom_index).?;
189191 atom.atom_index = atom_index;
190 atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
192 atom.name_offset = shdr.sh_name;
191193 atom.file_index = self.index;
192194 atom.input_section_index = shndx;
193195 self.atoms.items[shndx] = atom_index;
......@@ -205,7 +207,7 @@ fn addAtom(
205207
206208fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 {
207209 const name = blk: {
208 const name = self.strings.getAssumeExists(shdr.sh_name);
210 const name = self.getString(shdr.sh_name);
209211 if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name;
210212 const sh_name_prefixes: []const [:0]const u8 = &.{
211213 ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
......@@ -248,7 +250,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem
248250
249251fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
250252 const shdr = self.shdrs.items[index];
251 const name = self.strings.getAssumeExists(shdr.sh_name);
253 const name = self.getString(shdr.sh_name);
252254 const ignore = blk: {
253255 if (mem.startsWith(u8, name, ".note")) break :blk true;
254256 if (mem.startsWith(u8, name, ".comment")) break :blk true;
......@@ -262,33 +264,24 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
262264
263265fn initSymtab(self: *Object, elf_file: *Elf) !void {
264266 const gpa = elf_file.base.allocator;
265 const first_global = self.first_global orelse self.symtab.len;
266 const shdrs = self.shdrs.items;
267 const first_global = self.first_global orelse self.symtab.items.len;
267268
268 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
269 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);
269270
270 for (self.symtab[0..first_global], 0..) |sym, i| {
271 for (self.symtab.items[0..first_global], 0..) |sym, i| {
271272 const index = try elf_file.addSymbol();
272273 self.symbols.appendAssumeCapacity(index);
273274 const sym_ptr = elf_file.symbol(index);
274 const name = blk: {
275 if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) {
276 const shdr = shdrs[sym.st_shndx];
277 break :blk self.strings.getAssumeExists(shdr.sh_name);
278 }
279 break :blk self.getString(sym.st_name);
280 };
281275 sym_ptr.value = sym.st_value;
282 sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name);
276 sym_ptr.name_offset = sym.st_name;
283277 sym_ptr.esym_index = @as(u32, @intCast(i));
284278 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
285279 sym_ptr.file_index = self.index;
286280 }
287281
288 for (self.symtab[first_global..]) |sym| {
282 for (self.symtab.items[first_global..]) |sym| {
289283 const name = self.getString(sym.st_name);
290 const off = try elf_file.strtab.insert(gpa, name);
291 const gop = try elf_file.getOrPutGlobal(off);
284 const gop = try elf_file.getOrPutGlobal(name);
292285 self.symbols.addOneAssumeCapacity().* = gop.index;
293286 }
294287}
......@@ -437,7 +430,7 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
437430 const first_global = self.first_global orelse return;
438431 for (self.globals(), 0..) |index, i| {
439432 const esym_index = @as(Symbol.Index, @intCast(first_global + i));
440 const esym = self.symtab[esym_index];
433 const esym = self.symtab.items[esym_index];
441434
442435 if (esym.st_shndx == elf.SHN_UNDEF) continue;
443436
......@@ -467,7 +460,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
467460 const first_global = self.first_global orelse return;
468461 for (self.globals(), 0..) |index, i| {
469462 const esym_index = @as(u32, @intCast(first_global + i));
470 const esym = self.symtab[esym_index];
463 const esym = self.symtab.items[esym_index];
471464 if (esym.st_shndx != elf.SHN_UNDEF) continue;
472465
473466 const global = elf_file.symbol(index);
......@@ -491,20 +484,11 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
491484 }
492485}
493486
494pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
495 for (self.globals()) |index| {
496 const global = elf_file.symbol(index);
497 const off = global.name_offset;
498 global.* = .{};
499 global.name_offset = off;
500 }
501}
502
503487pub fn markLive(self: *Object, elf_file: *Elf) void {
504488 const first_global = self.first_global orelse return;
505489 for (self.globals(), 0..) |index, i| {
506490 const sym_idx = first_global + i;
507 const sym = self.symtab[sym_idx];
491 const sym = self.symtab.items[sym_idx];
508492 if (sym.st_bind() == elf.STB_WEAK) continue;
509493
510494 const global = elf_file.symbol(index);
......@@ -531,7 +515,7 @@ pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
531515 const first_global = self.first_global orelse return;
532516 for (self.globals(), 0..) |index, i| {
533517 const sym_idx = @as(u32, @intCast(first_global + i));
534 const this_sym = self.symtab[sym_idx];
518 const this_sym = self.symtab.items[sym_idx];
535519 const global = elf_file.symbol(index);
536520 const global_file = global.getFile(elf_file) orelse continue;
537521
......@@ -560,7 +544,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
560544 const first_global = self.first_global orelse return;
561545 for (self.globals(), 0..) |index, i| {
562546 const sym_idx = @as(u32, @intCast(first_global + i));
563 const this_sym = self.symtab[sym_idx];
547 const this_sym = self.symtab.items[sym_idx];
564548 if (this_sym.st_shndx != elf.SHN_COMMON) continue;
565549
566550 const global = elf_file.symbol(index);
......@@ -584,8 +568,10 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
584568 const name = if (is_tls) ".tls_common" else ".common";
585569
586570 const atom = elf_file.atom(atom_index).?;
571 const name_offset = @as(u32, @intCast(self.strtab.items.len));
572 try self.strtab.writer(gpa).print("{s}\x00", .{name});
587573 atom.atom_index = atom_index;
588 atom.name_offset = try elf_file.strtab.insert(gpa, name);
574 atom.name_offset = name_offset;
589575 atom.file_index = self.index;
590576 atom.size = this_sym.st_size;
591577 const alignment = this_sym.st_value;
......@@ -597,7 +583,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
597583 const shdr = try self.shdrs.addOne(gpa);
598584 const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow;
599585 shdr.* = .{
600 .sh_name = try self.strings.insert(gpa, name),
586 .sh_name = name_offset,
601587 .sh_type = elf.SHT_NOBITS,
602588 .sh_flags = sh_flags,
603589 .sh_addr = 0,
......@@ -665,56 +651,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
665651 }
666652}
667653
668pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
669 for (self.locals()) |local_index| {
670 const local = elf_file.symbol(local_index);
671 if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
672 const esym = local.elfSym(elf_file);
673 switch (esym.st_type()) {
674 elf.STT_SECTION, elf.STT_NOTYPE => continue,
675 else => {},
676 }
677 local.flags.output_symtab = true;
678 self.output_symtab_size.nlocals += 1;
679 }
680
681 for (self.globals()) |global_index| {
682 const global = elf_file.symbol(global_index);
683 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
684 if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
685 global.flags.output_symtab = true;
686 if (global.isLocal()) {
687 self.output_symtab_size.nlocals += 1;
688 } else {
689 self.output_symtab_size.nglobals += 1;
690 }
691 }
692}
693
694pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void {
695 var ilocal = ctx.ilocal;
696 for (self.locals()) |local_index| {
697 const local = elf_file.symbol(local_index);
698 if (!local.flags.output_symtab) continue;
699 local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
700 ilocal += 1;
701 }
702
703 var iglobal = ctx.iglobal;
704 for (self.globals()) |global_index| {
705 const global = elf_file.symbol(global_index);
706 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
707 if (!global.flags.output_symtab) continue;
708 if (global.isLocal()) {
709 global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
710 ilocal += 1;
711 } else {
712 global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
713 iglobal += 1;
714 }
715 }
716}
717
718654pub fn locals(self: Object) []const Symbol.Index {
719655 const end = self.first_global orelse self.symbols.items.len;
720656 return self.symbols.items[0..end];
......@@ -760,11 +696,6 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
760696 } else return gpa.dupe(u8, data);
761697}
762698
763fn getString(self: *Object, off: u32) [:0]const u8 {
764 assert(off < self.strtab.len);
765 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
766}
767
768699pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 {
769700 const raw = self.shdrContents(index);
770701 const nmembers = @divExact(raw.len, @sizeOf(u32));
......@@ -782,6 +713,11 @@ pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela {
782713 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
783714}
784715
716pub fn getString(self: Object, off: u32) [:0]const u8 {
717 assert(off < self.strtab.items.len);
718 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
719}
720
785721pub fn format(
786722 self: *Object,
787723 comptime unused_fmt_string: []const u8,
......@@ -991,6 +927,5 @@ const Cie = eh_frame.Cie;
991927const Elf = @import("../Elf.zig");
992928const Fde = eh_frame.Fde;
993929const File = @import("file.zig").File;
994const StringTable = @import("../strtab.zig").StringTable;
995930const Symbol = @import("Symbol.zig");
996931const Alignment = Atom.Alignment;
src/link/Elf/SharedObject.zig+50-62
......@@ -4,19 +4,20 @@ index: File.Index,
44
55header: ?elf.Elf64_Ehdr = null,
66shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
7symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
8strtab: []const u8 = &[0]u8{},
7
8symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
9strtab: std.ArrayListUnmanaged(u8) = .{},
910/// Version symtab contains version strings of the symbols if present.
1011versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
1112verstrings: std.ArrayListUnmanaged(u32) = .{},
13symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
14aliases: ?std.ArrayListUnmanaged(u32) = null,
1215
16dynsym_sect_index: ?u16 = null,
1317dynamic_sect_index: ?u16 = null,
1418versym_sect_index: ?u16 = null,
1519verdef_sect_index: ?u16 = null,
1620
17symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
18aliases: ?std.ArrayListUnmanaged(u32) = null,
19
2021needed: bool,
2122alive: bool,
2223
......@@ -36,6 +37,8 @@ pub fn isSharedObject(path: []const u8) !bool {
3637pub fn deinit(self: *SharedObject, allocator: Allocator) void {
3738 allocator.free(self.path);
3839 allocator.free(self.data);
40 self.symtab.deinit(allocator);
41 self.strtab.deinit(allocator);
3942 self.versyms.deinit(allocator);
4043 self.verstrings.deinit(allocator);
4144 self.symbols.deinit(allocator);
......@@ -51,7 +54,6 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
5154 self.header = try reader.readStruct(elf.Elf64_Ehdr);
5255 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
5356
54 var dynsym_index: ?u16 = null;
5557 const shdrs = @as(
5658 [*]align(1) const elf.Elf64_Shdr,
5759 @ptrCast(self.data.ptr + shoff),
......@@ -61,7 +63,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
6163 for (shdrs, 0..) |shdr, i| {
6264 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
6365 switch (shdr.sh_type) {
64 elf.SHT_DYNSYM => dynsym_index = @as(u16, @intCast(i)),
66 elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),
6567 elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)),
6668 elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)),
6769 elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)),
......@@ -69,20 +71,13 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
6971 }
7072 }
7173
72 if (dynsym_index) |index| {
73 const shdr = self.shdrs.items[index];
74 const symtab = self.shdrContents(index);
75 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
76 self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
77 self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
78 }
79
8074 try self.parseVersions(elf_file);
8175 try self.initSymtab(elf_file);
8276}
8377
8478fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
8579 const gpa = elf_file.base.allocator;
80 const symtab = self.getSymtabRaw();
8681
8782 try self.verstrings.resize(gpa, 2);
8883 self.verstrings.items[elf.VER_NDX_LOCAL] = 0;
......@@ -107,7 +102,7 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
107102 }
108103 }
109104
110 try self.versyms.ensureTotalCapacityPrecise(gpa, self.symtab.len);
105 try self.versyms.ensureTotalCapacityPrecise(gpa, symtab.len);
111106
112107 if (self.versym_sect_index) |shndx| {
113108 const versyms_raw = self.shdrContents(shndx);
......@@ -120,30 +115,39 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
120115 ver;
121116 self.versyms.appendAssumeCapacity(normalized_ver);
122117 }
123 } else for (0..self.symtab.len) |_| {
118 } else for (0..symtab.len) |_| {
124119 self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL);
125120 }
126121}
127122
128123fn initSymtab(self: *SharedObject, elf_file: *Elf) !void {
129124 const gpa = elf_file.base.allocator;
125 const symtab = self.getSymtabRaw();
126 const strtab = self.getStrtabRaw();
130127
131 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
128 try self.strtab.appendSlice(gpa, strtab);
129 try self.symtab.ensureTotalCapacityPrecise(gpa, symtab.len);
130 try self.symbols.ensureTotalCapacityPrecise(gpa, symtab.len);
132131
133 for (self.symtab, 0..) |sym, i| {
132 for (symtab, 0..) |sym, i| {
134133 const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;
135134 const name = self.getString(sym.st_name);
136135 // We need to garble up the name so that we don't pick this symbol
137136 // during symbol resolution. Thank you GNU!
138 const off = if (hidden) blk: {
139 const full_name = try std.fmt.allocPrint(gpa, "{s}@{s}", .{
137 const name_off = if (hidden) blk: {
138 const mangled = try std.fmt.allocPrint(gpa, "{s}@{s}", .{
140139 name,
141140 self.versionString(self.versyms.items[i]),
142141 });
143 defer gpa.free(full_name);
144 break :blk try elf_file.strtab.insert(gpa, full_name);
145 } else try elf_file.strtab.insert(gpa, name);
146 const gop = try elf_file.getOrPutGlobal(off);
142 defer gpa.free(mangled);
143 const name_off = @as(u32, @intCast(self.strtab.items.len));
144 try self.strtab.writer(gpa).print("{s}\x00", .{mangled});
145 break :blk name_off;
146 } else sym.st_name;
147 const out_sym = self.symtab.addOneAssumeCapacity();
148 out_sym.* = sym;
149 out_sym.st_name = name_off;
150 const gop = try elf_file.getOrPutGlobal(self.getString(name_off));
147151 self.symbols.addOneAssumeCapacity().* = gop.index;
148152 }
149153}
......@@ -151,7 +155,7 @@ fn initSymtab(self: *SharedObject, elf_file: *Elf) !void {
151155pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
152156 for (self.globals(), 0..) |index, i| {
153157 const esym_index = @as(u32, @intCast(i));
154 const this_sym = self.symtab[esym_index];
158 const this_sym = self.symtab.items[esym_index];
155159
156160 if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
157161
......@@ -166,18 +170,9 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
166170 }
167171}
168172
169pub fn resetGlobals(self: *SharedObject, elf_file: *Elf) void {
170 for (self.globals()) |index| {
171 const global = elf_file.symbol(index);
172 const off = global.name_offset;
173 global.* = .{};
174 global.name_offset = off;
175 }
176}
177
178173pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
179174 for (self.globals(), 0..) |index, i| {
180 const sym = self.symtab[i];
175 const sym = self.symtab.items[i];
181176 if (sym.st_shndx != elf.SHN_UNDEF) continue;
182177
183178 const global = elf_file.symbol(index);
......@@ -193,27 +188,6 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
193188 }
194189}
195190
196pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void {
197 for (self.globals()) |global_index| {
198 const global = elf_file.symbol(global_index);
199 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
200 if (global.isLocal()) continue;
201 global.flags.output_symtab = true;
202 self.output_symtab_size.nglobals += 1;
203 }
204}
205
206pub fn writeSymtab(self: *SharedObject, elf_file: *Elf, ctx: anytype) void {
207 var iglobal = ctx.iglobal;
208 for (self.globals()) |global_index| {
209 const global = elf_file.symbol(global_index);
210 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
211 if (!global.flags.output_symtab) continue;
212 global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
213 iglobal += 1;
214 }
215}
216
217191pub fn globals(self: SharedObject) []const Symbol.Index {
218192 return self.symbols.items;
219193}
......@@ -223,11 +197,6 @@ pub fn shdrContents(self: SharedObject, index: u16) []const u8 {
223197 return self.data[shdr.sh_offset..][0..shdr.sh_size];
224198}
225199
226pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
227 assert(off < self.strtab.len);
228 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
229}
230
231200pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 {
232201 const off = self.verstrings.items[index & elf.VERSYM_VERSION];
233202 return self.getString(off);
......@@ -309,6 +278,25 @@ pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u3
309278 return aliases.items[start..end];
310279}
311280
281pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
282 assert(off < self.strtab.items.len);
283 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
284}
285
286pub fn getSymtabRaw(self: SharedObject) []align(1) const elf.Elf64_Sym {
287 const index = self.dynsym_sect_index orelse return &[0]elf.Elf64_Sym{};
288 const raw_symtab = self.shdrContents(index);
289 const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));
290 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
291 return symtab;
292}
293
294pub fn getStrtabRaw(self: SharedObject) []const u8 {
295 const index = self.dynsym_sect_index orelse return &[0]u8{};
296 const shdr = self.shdrs.items[index];
297 return self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
298}
299
312300pub fn format(
313301 self: SharedObject,
314302 comptime unused_fmt_string: []const u8,
src/link/Elf/Symbol.zig+29-20
......@@ -42,7 +42,8 @@ pub fn outputShndx(symbol: Symbol) ?u16 {
4242 return symbol.output_section_index;
4343}
4444
45pub fn isLocal(symbol: Symbol) bool {
45pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool {
46 if (elf_file.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL;
4647 return !(symbol.flags.import or symbol.flags.@"export");
4748}
4849
......@@ -58,7 +59,11 @@ pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
5859}
5960
6061pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
61 return elf_file.strtab.getAssumeExists(symbol.name_offset);
62 if (symbol.flags.global) return elf_file.strings.getAssumeExists(symbol.name_offset);
63 const file_ptr = symbol.file(elf_file).?;
64 return switch (file_ptr) {
65 inline else => |x| x.getString(symbol.name_offset),
66 };
6267}
6368
6469pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
......@@ -71,11 +76,10 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
7176
7277pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
7378 const file_ptr = symbol.file(elf_file).?;
74 switch (file_ptr) {
75 .zig_object => |x| return x.elfSym(symbol.esym_index).*,
76 .linker_defined => |x| return x.symtab.items[symbol.esym_index],
77 inline else => |x| return x.symtab[symbol.esym_index],
78 }
79 return switch (file_ptr) {
80 .zig_object => |x| x.elfSym(symbol.esym_index).*,
81 inline else => |x| x.symtab.items[symbol.esym_index],
82 };
7983}
8084
8185pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
......@@ -164,6 +168,8 @@ const GetOrCreateZigGotEntryResult = struct {
164168};
165169
166170pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult {
171 assert(!elf_file.isRelocatable());
172 assert(symbol.flags.needs_zig_got);
167173 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got };
168174 const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file);
169175 return .{ .found_existing = false, .index = index };
......@@ -201,14 +207,11 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void {
201207}
202208
203209pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
204 const file_ptr = symbol.file(elf_file) orelse {
205 out.* = Elf.null_sym;
206 return;
207 };
210 const file_ptr = symbol.file(elf_file).?;
208211 const esym = symbol.elfSym(elf_file);
209212 const st_type = symbol.type(elf_file);
210213 const st_bind: u8 = blk: {
211 if (symbol.isLocal()) break :blk 0;
214 if (symbol.isLocal(elf_file)) break :blk 0;
212215 if (symbol.flags.weak) break :blk elf.STB_WEAK;
213216 if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL;
214217 break :blk esym.st_bind();
......@@ -216,6 +219,8 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
216219 const st_shndx = blk: {
217220 if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?;
218221 if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
222 // TODO I think this is wrong and obsolete
223 if (elf_file.isRelocatable() and st_type == elf.STT_SECTION) break :blk symbol.outputShndx().?;
219224 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)
220225 break :blk elf.SHN_ABS;
221226 break :blk symbol.outputShndx() orelse elf.SHN_UNDEF;
......@@ -232,14 +237,11 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
232237 break :blk symbol.value - elf_file.tlsAddress();
233238 break :blk symbol.value;
234239 };
235 out.* = .{
236 .st_name = symbol.name_offset,
237 .st_info = (st_bind << 4) | st_type,
238 .st_other = esym.st_other,
239 .st_shndx = st_shndx,
240 .st_value = st_value,
241 .st_size = esym.st_size,
242 };
240 out.st_info = (st_bind << 4) | st_type;
241 out.st_other = esym.st_other;
242 out.st_shndx = st_shndx;
243 out.st_value = st_value;
244 out.st_size = esym.st_size;
243245}
244246
245247pub fn format(
......@@ -340,6 +342,12 @@ pub const Flags = packed struct {
340342 /// Whether this symbol is weak.
341343 weak: bool = false,
342344
345 /// Whether the symbol has its name interned in global symbol
346 /// resolver table.
347 /// This happens for any symbol that is considered a global
348 /// symbol, but is not necessarily an import or export.
349 global: bool = false,
350
343351 /// Whether the symbol makes into the output symtab.
344352 output_symtab: bool = false,
345353
......@@ -373,6 +381,7 @@ pub const Flags = packed struct {
373381 has_tlsdesc: bool = false,
374382
375383 /// Whether the symbol contains .zig.got indirection.
384 needs_zig_got: bool = false,
376385 has_zig_got: bool = false,
377386};
378387
src/link/Elf/ZigObject.zig+305-105
......@@ -9,6 +9,7 @@ index: File.Index,
99
1010local_esyms: std.MultiArrayList(ElfSym) = .{},
1111global_esyms: std.MultiArrayList(ElfSym) = .{},
12strtab: StringTable = .{},
1213local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
1314global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
1415globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
......@@ -19,6 +20,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
1920num_dynrelocs: u32 = 0,
2021
2122output_symtab_size: Elf.SymtabSize = .{},
23output_ar_state: Archive.ArState = .{},
2224
2325dwarf: ?Dwarf = null,
2426
......@@ -74,8 +76,9 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
7476 const gpa = elf_file.base.allocator;
7577
7678 try self.atoms.append(gpa, 0); // null input section
79 try self.strtab.buffer.append(gpa, 0);
7780
78 const name_off = try elf_file.strtab.insert(gpa, std.fs.path.stem(self.path));
81 const name_off = try self.strtab.insert(gpa, std.fs.path.stem(self.path));
7982 const symbol_index = try elf_file.addSymbol();
8083 try self.local_symbols.append(gpa, symbol_index);
8184 const symbol_ptr = elf_file.symbol(symbol_index);
......@@ -85,7 +88,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
8588 const esym_index = try self.addLocalEsym(gpa);
8689 const esym = &self.local_esyms.items(.elf_sym)[esym_index];
8790 esym.st_name = name_off;
88 esym.st_info |= elf.STT_FILE;
91 esym.st_info = elf.STT_FILE;
8992 esym.st_shndx = elf.SHN_ABS;
9093 symbol_ptr.esym_index = esym_index;
9194
......@@ -97,6 +100,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
97100pub fn deinit(self: *ZigObject, allocator: Allocator) void {
98101 self.local_esyms.deinit(allocator);
99102 self.global_esyms.deinit(allocator);
103 self.strtab.deinit(allocator);
100104 self.local_symbols.deinit(allocator);
101105 self.global_symbols.deinit(allocator);
102106 self.globals_lookup.deinit(allocator);
......@@ -177,16 +181,16 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
177181 }
178182
179183 if (self.debug_info_header_dirty) {
180 const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?];
181 const low_pc = text_phdr.p_vaddr;
182 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
184 const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?];
185 const low_pc = text_shdr.sh_addr;
186 const high_pc = text_shdr.sh_addr + text_shdr.sh_size;
183187 try dw.writeDbgInfoHeader(elf_file.base.options.module.?, low_pc, high_pc);
184188 self.debug_info_header_dirty = false;
185189 }
186190
187191 if (self.debug_aranges_section_dirty) {
188 const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?];
189 try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
192 const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?];
193 try dw.writeDbgAranges(text_shdr.sh_addr, text_shdr.sh_size);
190194 self.debug_aranges_section_dirty = false;
191195 }
192196
......@@ -207,6 +211,8 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
207211 self.saveDebugSectionsSizes(elf_file);
208212 }
209213
214 try self.sortSymbols(elf_file);
215
210216 // The point of flushModule() is to commit changes, so in theory, nothing should
211217 // be dirty after this. However, it is possible for some things to remain
212218 // dirty because they fail to be written in the event of compile errors,
......@@ -281,6 +287,22 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
281287 return symbol_index;
282288}
283289
290pub fn addSectionSymbol(self: *ZigObject, shndx: u16, elf_file: *Elf) !void {
291 assert(elf_file.isRelocatable());
292 const gpa = elf_file.base.allocator;
293 const symbol_index = try elf_file.addSymbol();
294 try self.local_symbols.append(gpa, symbol_index);
295 const symbol_ptr = elf_file.symbol(symbol_index);
296 symbol_ptr.file_index = self.index;
297 symbol_ptr.output_section_index = shndx;
298
299 const esym_index = try self.addLocalEsym(gpa);
300 const esym = &self.local_esyms.items(.elf_sym)[esym_index];
301 esym.st_info = elf.STT_SECTION;
302 esym.st_shndx = shndx;
303 symbol_ptr.esym_index = esym_index;
304}
305
284306/// TODO actually create fake input shdrs and return that instead.
285307pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr {
286308 _ = self;
......@@ -334,7 +356,7 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
334356 }
335357}
336358
337pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void {
359pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void {
338360 for (self.globals(), 0..) |index, i| {
339361 const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
340362 const esym = self.global_esyms.items(.elf_sym)[i];
......@@ -362,6 +384,26 @@ pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void {
362384 }
363385}
364386
387pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
388 for (self.globals(), 0..) |index, i| {
389 const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
390 const esym = self.global_esyms.items(.elf_sym)[i];
391
392 if (esym.st_shndx != elf.SHN_UNDEF) continue;
393
394 const global = elf_file.symbol(index);
395 if (global.file(elf_file)) |file| {
396 if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or
397 file.index() <= self.index) continue;
398 }
399
400 global.value = 0;
401 global.atom_index = 0;
402 global.esym_index = esym_index;
403 global.file_index = self.index;
404 }
405}
406
365407pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
366408 for (self.atoms.items) |atom_index| {
367409 const atom = elf_file.atom(atom_index) orelse continue;
......@@ -379,15 +421,6 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
379421 }
380422}
381423
382pub fn resetGlobals(self: *ZigObject, elf_file: *Elf) void {
383 for (self.globals()) |index| {
384 const global = elf_file.symbol(index);
385 const off = global.name_offset;
386 global.* = .{};
387 global.name_offset = off;
388 }
389}
390
391424pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
392425 for (self.globals(), 0..) |index, i| {
393426 const esym = self.global_esyms.items(.elf_sym)[i];
......@@ -404,79 +437,241 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
404437 }
405438}
406439
407pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) void {
408 for (self.locals()) |local_index| {
409 const local = elf_file.symbol(local_index);
410 const esym = local.elfSym(elf_file);
411 switch (esym.st_type()) {
412 elf.STT_SECTION, elf.STT_NOTYPE => {
413 local.flags.output_symtab = false;
414 continue;
415 },
416 else => {},
417 }
418 local.flags.output_symtab = true;
419 self.output_symtab_size.nlocals += 1;
420 }
440fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void {
441 _ = self;
442 _ = elf_file;
443 // const Entry = struct {
444 // index: Symbol.Index,
445
446 // const Ctx = struct {
447 // zobj: ZigObject,
448 // efile: *Elf,
449 // };
450
451 // pub fn lessThan(ctx: Ctx, lhs: @This(), rhs: @This()) bool {
452 // const lhs_sym = ctx.efile.symbol(zobj.symbol(lhs.index));
453 // const rhs_sym = ctx.efile.symbol(zobj.symbol(rhs.index));
454 // if (lhs_sym.outputShndx() != null and rhs_sym.outputShndx() != null) {
455 // if (lhs_sym.output_section_index == rhs_sym.output_section_index) {
456 // if (lhs_sym.value == rhs_sym.value) {
457 // return lhs_sym.name_offset < rhs_sym.name_offset;
458 // }
459 // return lhs_sym.value < rhs_sym.value;
460 // }
461 // return lhs_sym.output_section_index < rhs_sym.output_section_index;
462 // }
463 // if (lhs_sym.outputShndx() != null) {
464 // if (rhs_sym.isAbs(ctx.efile)) return false;
465 // return true;
466 // }
467 // return false;
468 // }
469 // };
470
471 // const gpa = elf_file.base.allocator;
472
473 // {
474 // const sorted = try gpa.alloc(Entry, self.local_symbols.items.len);
475 // defer gpa.free(sorted);
476 // for (0..self.local_symbols.items.len) |index| {
477 // sorted[i] = .{ .index = @as(Symbol.Index, @intCast(index)) };
478 // }
479 // mem.sort(Entry, sorted, .{ .zobj = self, .efile = elf_file }, Entry.lessThan);
480
481 // const backlinks = try gpa.alloc(Symbol.Index, sorted.len);
482 // defer gpa.free(backlinks);
483 // for (sorted, 0..) |entry, i| {
484 // backlinks[entry.index] = @as(Symbol.Index, @intCast(i));
485 // }
486
487 // const local_symbols = try self.local_symbols.toOwnedSlice(gpa);
488 // defer gpa.free(local_symbols);
489
490 // try self.local_symbols.ensureTotalCapacityPrecise(gpa, local_symbols.len);
491 // for (sorted) |entry| {
492 // self.local_symbols.appendAssumeCapacity(local_symbols[entry.index]);
493 // }
494
495 // for (self.)
496 // }
497
498 // const sorted_globals = try gpa.alloc(Entry, self.global_symbols.items.len);
499 // defer gpa.free(sorted_globals);
500 // for (self.global_symbols.items, 0..) |index, i| {
501 // sorted_globals[i] = .{ .index = index };
502 // }
503 // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan);
504}
505
506pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
507 const gpa = elf_file.base.allocator;
508
509 try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len);
421510
422511 for (self.globals()) |global_index| {
423512 const global = elf_file.symbol(global_index);
424 if (global.file(elf_file)) |file| if (file.index() != self.index) {
425 global.flags.output_symtab = false;
426 continue;
427 };
428 global.flags.output_symtab = true;
429 if (global.isLocal()) {
430 self.output_symtab_size.nlocals += 1;
431 } else {
432 self.output_symtab_size.nglobals += 1;
433 }
513 const file_ptr = global.file(elf_file).?;
514 assert(file_ptr.index() == self.index);
515 if (global.type(elf_file) == elf.SHN_UNDEF) continue;
516
517 const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file));
518 ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index });
434519 }
435520}
436521
437pub fn writeSymtab(self: *ZigObject, elf_file: *Elf, ctx: anytype) void {
438 var ilocal = ctx.ilocal;
439 for (self.locals()) |local_index| {
440 const local = elf_file.symbol(local_index);
441 if (!local.flags.output_symtab) continue;
442 local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
443 ilocal += 1;
522pub fn updateArStrtab(
523 self: *ZigObject,
524 allocator: Allocator,
525 ar_strtab: *Archive.ArStrtab,
526) error{OutOfMemory}!void {
527 const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)});
528 defer allocator.free(name);
529 if (name.len <= 15) return;
530 const name_off = try ar_strtab.insert(allocator, name);
531 self.output_ar_state.name_off = name_off;
532}
533
534pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void {
535 var end_pos: u64 = elf_file.shdr_table_offset.?;
536 for (elf_file.shdrs.items) |shdr| {
537 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
444538 }
539 self.output_ar_state.size = end_pos;
540}
445541
446 var iglobal = ctx.iglobal;
447 for (self.globals()) |global_index| {
448 const global = elf_file.symbol(global_index);
449 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
450 if (!global.flags.output_symtab) continue;
451 if (global.isLocal()) {
452 global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
453 ilocal += 1;
454 } else {
455 global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
456 iglobal += 1;
542pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void {
543 const gpa = elf_file.base.allocator;
544
545 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
546 const contents = try gpa.alloc(u8, size);
547 defer gpa.free(contents);
548
549 const amt = try elf_file.base.file.?.preadAll(contents, 0);
550 if (amt != self.output_ar_state.size) return error.InputOutput;
551
552 const name = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(self.path)});
553 defer gpa.free(name);
554
555 const hdr = Archive.setArHdr(.{
556 .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off },
557 .size = @intCast(size),
558 });
559 try writer.writeAll(mem.asBytes(&hdr));
560 try writer.writeAll(contents);
561}
562
563pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void {
564 _ = self;
565
566 for (&[_]?u16{
567 elf_file.zig_text_rela_section_index,
568 elf_file.zig_data_rel_ro_rela_section_index,
569 elf_file.zig_data_rela_section_index,
570 }) |maybe_index| {
571 const index = maybe_index orelse continue;
572 const shdr = &elf_file.shdrs.items[index];
573 const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?;
574 const last_atom_index = meta.last_atom_index;
575
576 var atom = elf_file.atom(last_atom_index) orelse continue;
577 while (true) {
578 const relocs = atom.relocs(elf_file);
579 shdr.sh_size += relocs.len * shdr.sh_entsize;
580 if (elf_file.atom(atom.prev_index)) |prev| {
581 atom = prev;
582 } else break;
583 }
584 }
585
586 for (&[_]?u16{
587 elf_file.zig_text_rela_section_index,
588 elf_file.zig_data_rel_ro_rela_section_index,
589 elf_file.zig_data_rela_section_index,
590 }) |maybe_index| {
591 const index = maybe_index orelse continue;
592 const shdr = &elf_file.shdrs.items[index];
593 if (shdr.sh_size == 0) shdr.sh_offset = 0;
594 }
595}
596
597pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void {
598 const gpa = elf_file.base.allocator;
599
600 for (&[_]?u16{
601 elf_file.zig_text_rela_section_index,
602 elf_file.zig_data_rel_ro_rela_section_index,
603 elf_file.zig_data_rela_section_index,
604 }) |maybe_index| {
605 const index = maybe_index orelse continue;
606 const shdr = elf_file.shdrs.items[index];
607 const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?;
608 const last_atom_index = meta.last_atom_index;
609
610 var atom = elf_file.atom(last_atom_index) orelse continue;
611
612 var relocs = std.ArrayList(elf.Elf64_Rela).init(gpa);
613 defer relocs.deinit();
614 try relocs.ensureTotalCapacityPrecise(@intCast(@divExact(shdr.sh_size, shdr.sh_entsize)));
615
616 while (true) {
617 for (atom.relocs(elf_file)) |rel| {
618 const target = elf_file.symbol(self.symbol(rel.r_sym()));
619 const r_offset = atom.value + rel.r_offset;
620 const r_sym: u32 = if (target.flags.global)
621 (target.esym_index & symbol_mask) + @as(u32, @intCast(self.local_esyms.slice().len))
622 else
623 target.esym_index;
624 const r_type = switch (rel.r_type()) {
625 Elf.R_X86_64_ZIG_GOT32,
626 Elf.R_X86_64_ZIG_GOTPCREL,
627 => unreachable, // Sanity check if we accidentally emitted those.
628 else => |r_type| r_type,
629 };
630 relocs.appendAssumeCapacity(.{
631 .r_offset = r_offset,
632 .r_addend = rel.r_addend,
633 .r_info = (@as(u64, @intCast(r_sym + 1)) << 32) | r_type,
634 });
635 }
636 if (elf_file.atom(atom.prev_index)) |prev| {
637 atom = prev;
638 } else break;
457639 }
640
641 const SortRelocs = struct {
642 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
643 _ = ctx;
644 return lhs.r_offset < rhs.r_offset;
645 }
646 };
647
648 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
649
650 try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
458651 }
459652}
460653
461pub fn symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index {
462 const is_global = index & global_symbol_bit != 0;
654inline fn isGlobal(index: Symbol.Index) bool {
655 return index & global_symbol_bit != 0;
656}
657
658pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index {
463659 const actual_index = index & symbol_mask;
464 if (is_global) return self.global_symbols.items[actual_index];
660 if (isGlobal(index)) return self.global_symbols.items[actual_index];
465661 return self.local_symbols.items[actual_index];
466662}
467663
468664pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym {
469 const is_global = index & global_symbol_bit != 0;
470665 const actual_index = index & symbol_mask;
471 if (is_global) return &self.global_esyms.items(.elf_sym)[actual_index];
666 if (isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index];
472667 return &self.local_esyms.items(.elf_sym)[actual_index];
473668}
474669
475pub fn locals(self: *ZigObject) []const Symbol.Index {
670pub fn locals(self: ZigObject) []const Symbol.Index {
476671 return self.local_symbols.items;
477672}
478673
479pub fn globals(self: *ZigObject) []const Symbol.Index {
674pub fn globals(self: ZigObject) []const Symbol.Index {
480675 return self.global_symbols.items;
481676}
482677
......@@ -570,7 +765,7 @@ pub fn lowerAnonDecl(
570765 name,
571766 tv,
572767 decl_alignment,
573 elf_file.zig_rodata_section_index.?,
768 elf_file.zig_data_rel_ro_section_index.?,
574769 src_loc,
575770 ) catch |err| switch (err) {
576771 error.OutOfMemory => return error.OutOfMemory,
......@@ -682,7 +877,7 @@ fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.In
682877 .Fn => elf_file.zig_text_section_index.?,
683878 else => blk: {
684879 if (decl.getOwnedVariable(mod)) |variable| {
685 if (variable.is_const) break :blk elf_file.zig_rodata_section_index.?;
880 if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?;
686881 if (variable.init.toValue().isUndefDeep(mod)) {
687882 const mode = elf_file.base.options.optimize_mode;
688883 if (mode == .Debug or mode == .ReleaseSafe) break :blk elf_file.zig_data_section_index.?;
......@@ -696,7 +891,7 @@ fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.In
696891 if (is_all_zeroes) break :blk elf_file.zig_bss_section_index.?;
697892 break :blk elf_file.zig_data_section_index.?;
698893 }
699 break :blk elf_file.zig_rodata_section_index.?;
894 break :blk elf_file.zig_data_rel_ro_section_index.?;
700895 },
701896 };
702897 return shdr_index;
......@@ -727,7 +922,7 @@ fn updateDeclCode(
727922 sym.output_section_index = shdr_index;
728923 atom_ptr.output_section_index = shdr_index;
729924
730 sym.name_offset = try elf_file.strtab.insert(gpa, decl_name);
925 sym.name_offset = try self.strtab.insert(gpa, decl_name);
731926 atom_ptr.flags.alive = true;
732927 atom_ptr.name_offset = sym.name_offset;
733928 esym.st_name = sym.name_offset;
......@@ -749,10 +944,12 @@ fn updateDeclCode(
749944 sym.value = atom_ptr.value;
750945 esym.st_value = atom_ptr.value;
751946
752 log.debug(" (writing new offset table entry)", .{});
753 assert(sym.flags.has_zig_got);
754 const extra = sym.extra(elf_file).?;
755 try elf_file.zig_got.writeOne(elf_file, extra.zig_got);
947 if (!elf_file.isRelocatable()) {
948 log.debug(" (writing new offset table entry)", .{});
949 assert(sym.flags.has_zig_got);
950 const extra = sym.extra(elf_file).?;
951 try elf_file.zig_got.writeOne(elf_file, extra.zig_got);
952 }
756953 }
757954 } else if (code.len < old_size) {
758955 atom_ptr.shrink(elf_file);
......@@ -762,10 +959,13 @@ fn updateDeclCode(
762959 errdefer self.freeDeclMetadata(elf_file, sym_index);
763960
764961 sym.value = atom_ptr.value;
962 sym.flags.needs_zig_got = true;
765963 esym.st_value = atom_ptr.value;
766964
767 const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
768 try elf_file.zig_got.writeOne(elf_file, gop.index);
965 if (!elf_file.isRelocatable()) {
966 const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
967 try elf_file.zig_got.writeOne(elf_file, gop.index);
968 }
769969 }
770970
771971 if (elf_file.base.child_pid) |pid| {
......@@ -791,9 +991,7 @@ fn updateDeclCode(
791991
792992 const shdr = elf_file.shdrs.items[shdr_index];
793993 if (shdr.sh_type != elf.SHT_NOBITS) {
794 const phdr_index = elf_file.phdr_to_shdr_table.get(shdr_index).?;
795 const section_offset = sym.value - elf_file.phdrs.items[phdr_index].p_vaddr;
796 const file_offset = shdr.sh_offset + section_offset;
994 const file_offset = shdr.sh_offset + sym.value - shdr.sh_addr;
797995 try elf_file.base.file.?.pwriteAll(code, file_offset);
798996 }
799997}
......@@ -967,7 +1165,7 @@ fn updateLazySymbol(
9671165 sym.ty.fmt(mod),
9681166 });
9691167 defer gpa.free(name);
970 break :blk try elf_file.strtab.insert(gpa, name);
1168 break :blk try self.strtab.insert(gpa, name);
9711169 };
9721170
9731171 const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
......@@ -997,10 +1195,9 @@ fn updateLazySymbol(
9971195
9981196 const output_section_index = switch (sym.kind) {
9991197 .code => elf_file.zig_text_section_index.?,
1000 .const_data => elf_file.zig_rodata_section_index.?,
1198 .const_data => elf_file.zig_data_rel_ro_section_index.?,
10011199 };
10021200 const local_sym = elf_file.symbol(symbol_index);
1003 const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?;
10041201 local_sym.name_offset = name_str_index;
10051202 local_sym.output_section_index = output_section_index;
10061203 const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
......@@ -1018,13 +1215,16 @@ fn updateLazySymbol(
10181215 errdefer self.freeDeclMetadata(elf_file, symbol_index);
10191216
10201217 local_sym.value = atom_ptr.value;
1218 local_sym.flags.needs_zig_got = true;
10211219 local_esym.st_value = atom_ptr.value;
10221220
1023 const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file);
1024 try elf_file.zig_got.writeOne(elf_file, gop.index);
1221 if (!elf_file.isRelocatable()) {
1222 const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file);
1223 try elf_file.zig_got.writeOne(elf_file, gop.index);
1224 }
10251225
1026 const section_offset = atom_ptr.value - elf_file.phdrs.items[phdr_index].p_vaddr;
1027 const file_offset = elf_file.shdrs.items[output_section_index].sh_offset + section_offset;
1226 const shdr = elf_file.shdrs.items[output_section_index];
1227 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
10281228 try elf_file.base.file.?.pwriteAll(code, file_offset);
10291229}
10301230
......@@ -1051,7 +1251,7 @@ pub fn lowerUnnamedConst(
10511251 name,
10521252 typed_value,
10531253 typed_value.ty.abiAlignment(mod),
1054 elf_file.zig_rodata_section_index.?,
1254 elf_file.zig_data_rel_ro_section_index.?,
10551255 decl.srcLoc(mod),
10561256 )) {
10571257 .ok => |sym_index| sym_index,
......@@ -1098,9 +1298,8 @@ fn lowerConst(
10981298 .fail => |em| return .{ .fail = em },
10991299 };
11001300
1101 const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?;
11021301 const local_sym = elf_file.symbol(sym_index);
1103 const name_str_index = try elf_file.strtab.insert(gpa, name);
1302 const name_str_index = try self.strtab.insert(gpa, name);
11041303 local_sym.name_offset = name_str_index;
11051304 local_sym.output_section_index = output_section_index;
11061305 const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
......@@ -1121,8 +1320,8 @@ fn lowerConst(
11211320 local_sym.value = atom_ptr.value;
11221321 local_esym.st_value = atom_ptr.value;
11231322
1124 const section_offset = atom_ptr.value - elf_file.phdrs.items[phdr_index].p_vaddr;
1125 const file_offset = elf_file.shdrs.items[output_section_index].sh_offset + section_offset;
1323 const shdr = elf_file.shdrs.items[output_section_index];
1324 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
11261325 try elf_file.base.file.?.pwriteAll(code, file_offset);
11271326
11281327 return .{ .ok = sym_index };
......@@ -1195,18 +1394,12 @@ pub fn updateExports(
11951394 };
11961395 const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
11971396 const exp_name = mod.intern_pool.stringToSlice(exp.opts.name);
1198 const name_off = try elf_file.strtab.insert(gpa, exp_name);
1199 const global_esym_index = if (metadata.@"export"(self, elf_file, exp_name)) |exp_index|
1397 const name_off = try self.strtab.insert(gpa, exp_name);
1398 const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index|
12001399 exp_index.*
12011400 else blk: {
1202 const global_esym_index = try self.addGlobalEsym(gpa);
1203 const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_off);
1204 const global_esym = self.elfSym(global_esym_index);
1205 global_esym.st_name = name_off;
1206 lookup_gop.value_ptr.* = global_esym_index;
1401 const global_esym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
12071402 try metadata.exports.append(gpa, global_esym_index);
1208 const gop = try elf_file.getOrPutGlobal(name_off);
1209 try self.global_symbols.append(gpa, gop.index);
12101403 break :blk global_esym_index;
12111404 };
12121405
......@@ -1216,6 +1409,7 @@ pub fn updateExports(
12161409 global_esym.st_shndx = esym.st_shndx;
12171410 global_esym.st_info = (stb_bits << 4) | stt_bits;
12181411 global_esym.st_name = name_off;
1412 global_esym.st_size = esym.st_size;
12191413 self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx;
12201414 }
12211415}
......@@ -1248,7 +1442,7 @@ pub fn deleteDeclExport(
12481442 const metadata = self.decls.getPtr(decl_index) orelse return;
12491443 const mod = elf_file.base.options.module.?;
12501444 const exp_name = mod.intern_pool.stringToSlice(name);
1251 const esym_index = metadata.@"export"(self, elf_file, exp_name) orelse return;
1445 const esym_index = metadata.@"export"(self, exp_name) orelse return;
12521446 log.debug("deleting export '{s}'", .{exp_name});
12531447 const esym = &self.global_esyms.items(.elf_sym)[esym_index.*];
12541448 _ = self.globals_lookup.remove(esym.st_name);
......@@ -1265,19 +1459,23 @@ pub fn deleteDeclExport(
12651459pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
12661460 _ = lib_name;
12671461 const gpa = elf_file.base.allocator;
1268 const off = try elf_file.strtab.insert(gpa, name);
1462 const off = try self.strtab.insert(gpa, name);
12691463 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
12701464 if (!lookup_gop.found_existing) {
12711465 const esym_index = try self.addGlobalEsym(gpa);
12721466 const esym = self.elfSym(esym_index);
12731467 esym.st_name = off;
12741468 lookup_gop.value_ptr.* = esym_index;
1275 const gop = try elf_file.getOrPutGlobal(off);
1469 const gop = try elf_file.getOrPutGlobal(name);
12761470 try self.global_symbols.append(gpa, gop.index);
12771471 }
12781472 return lookup_gop.value_ptr.*;
12791473}
12801474
1475pub fn getString(self: ZigObject, off: u32) [:0]const u8 {
1476 return self.strtab.getAssumeExists(off);
1477}
1478
12811479pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
12821480 return .{ .data = .{
12831481 .self = self,
......@@ -1350,9 +1548,9 @@ const DeclMetadata = struct {
13501548 /// A list of all exports aliases of this Decl.
13511549 exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
13521550
1353 fn @"export"(m: DeclMetadata, zig_object: *ZigObject, elf_file: *Elf, name: []const u8) ?*u32 {
1551 fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
13541552 for (m.exports.items) |*exp| {
1355 const exp_name = elf_file.strtab.getAssumeExists(zig_object.elfSym(exp.*).st_name);
1553 const exp_name = zig_object.getString(zig_object.elfSym(exp.*).st_name);
13561554 if (mem.eql(u8, name, exp_name)) return exp;
13571555 }
13581556 return null;
......@@ -1377,6 +1575,7 @@ const std = @import("std");
13771575
13781576const Air = @import("../../Air.zig");
13791577const Allocator = std.mem.Allocator;
1578const Archive = @import("Archive.zig");
13801579const Atom = @import("Atom.zig");
13811580const Dwarf = @import("../Dwarf.zig");
13821581const Elf = @import("../Elf.zig");
......@@ -1386,5 +1585,6 @@ const Liveness = @import("../../Liveness.zig");
13861585const Module = @import("../../Module.zig");
13871586const Object = @import("Object.zig");
13881587const Symbol = @import("Symbol.zig");
1588const StringTable = @import("../StringTable.zig");
13891589const TypedValue = @import("../../TypedValue.zig");
13901590const ZigObject = @This();
src/link/Elf/eh_frame.zig+1-1
......@@ -43,7 +43,7 @@ pub const Fde = struct {
4343 pub fn atom(fde: Fde, elf_file: *Elf) *Atom {
4444 const object = elf_file.file(fde.file_index).?.object;
4545 const rel = fde.relocs(elf_file)[0];
46 const sym = object.symtab[rel.r_sym()];
46 const sym = object.symtab.items[rel.r_sym()];
4747 const atom_index = object.atoms.items[sym.st_shndx];
4848 return elf_file.atom(atom_index).?;
4949 }
src/link/Elf/file.zig+103-8
......@@ -68,9 +68,12 @@ pub const File = union(enum) {
6868 }
6969
7070 pub fn resetGlobals(file: File, elf_file: *Elf) void {
71 switch (file) {
72 .linker_defined => unreachable,
73 inline else => |x| x.resetGlobals(elf_file),
71 for (file.globals()) |global_index| {
72 const global = elf_file.symbol(global_index);
73 const name_offset = global.name_offset;
74 global.* = .{};
75 global.name_offset = name_offset;
76 global.flags.global = true;
7477 }
7578 }
7679
......@@ -83,24 +86,37 @@ pub const File = union(enum) {
8386
8487 pub fn markLive(file: File, elf_file: *Elf) void {
8588 switch (file) {
86 .linker_defined => unreachable,
89 .linker_defined => {},
8790 inline else => |x| x.markLive(elf_file),
8891 }
8992 }
9093
9194 pub fn atoms(file: File) []const Atom.Index {
9295 return switch (file) {
93 .linker_defined => unreachable,
94 .shared_object => unreachable,
96 .linker_defined, .shared_object => &[0]Atom.Index{},
9597 .zig_object => |x| x.atoms.items,
9698 .object => |x| x.atoms.items,
9799 };
98100 }
99101
102 pub fn cies(file: File) []const Cie {
103 return switch (file) {
104 .zig_object => &[0]Cie{},
105 .object => |x| x.cies.items,
106 inline else => unreachable,
107 };
108 }
109
110 pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index {
111 return switch (file) {
112 .zig_object => |x| x.symbol(ind),
113 inline else => |x| x.symbols.items[ind],
114 };
115 }
116
100117 pub fn locals(file: File) []const Symbol.Index {
101118 return switch (file) {
102 .linker_defined => unreachable,
103 .shared_object => unreachable,
119 .linker_defined, .shared_object => &[0]Symbol.Index{},
104120 inline else => |x| x.locals(),
105121 };
106122 }
......@@ -111,6 +127,83 @@ pub const File = union(enum) {
111127 };
112128 }
113129
130 pub fn updateSymtabSize(file: File, elf_file: *Elf) void {
131 const output_symtab_size = switch (file) {
132 inline else => |x| &x.output_symtab_size,
133 };
134 for (file.locals()) |local_index| {
135 const local = elf_file.symbol(local_index);
136 if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
137 const esym = local.elfSym(elf_file);
138 switch (esym.st_type()) {
139 elf.STT_SECTION => if (!elf_file.isRelocatable()) continue,
140 elf.STT_NOTYPE => continue,
141 else => {},
142 }
143 local.flags.output_symtab = true;
144 output_symtab_size.nlocals += 1;
145 output_symtab_size.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
146 }
147
148 for (file.globals()) |global_index| {
149 const global = elf_file.symbol(global_index);
150 const file_ptr = global.file(elf_file) orelse continue;
151 if (file_ptr.index() != file.index()) continue;
152 if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
153 global.flags.output_symtab = true;
154 if (global.isLocal(elf_file)) {
155 output_symtab_size.nlocals += 1;
156 } else {
157 output_symtab_size.nglobals += 1;
158 }
159 output_symtab_size.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
160 }
161 }
162
163 pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void {
164 var ilocal = ctx.ilocal;
165 for (file.locals()) |local_index| {
166 const local = elf_file.symbol(local_index);
167 if (!local.flags.output_symtab) continue;
168 const out_sym = &elf_file.symtab.items[ilocal];
169 out_sym.st_name = @intCast(elf_file.strtab.items.len);
170 elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file));
171 elf_file.strtab.appendAssumeCapacity(0);
172 local.setOutputSym(elf_file, out_sym);
173 ilocal += 1;
174 }
175
176 var iglobal = ctx.iglobal;
177 for (file.globals()) |global_index| {
178 const global = elf_file.symbol(global_index);
179 const file_ptr = global.file(elf_file) orelse continue;
180 if (file_ptr.index() != file.index()) continue;
181 if (!global.flags.output_symtab) continue;
182 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
183 elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
184 elf_file.strtab.appendAssumeCapacity(0);
185 if (global.isLocal(elf_file)) {
186 const out_sym = &elf_file.symtab.items[ilocal];
187 out_sym.st_name = st_name;
188 global.setOutputSym(elf_file, out_sym);
189 ilocal += 1;
190 } else {
191 const out_sym = &elf_file.symtab.items[iglobal];
192 out_sym.st_name = st_name;
193 global.setOutputSym(elf_file, out_sym);
194 iglobal += 1;
195 }
196 }
197 }
198
199 pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
200 return switch (file) {
201 .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file),
202 .object => @panic("TODO"),
203 inline else => unreachable,
204 };
205 }
206
114207 pub const Index = u32;
115208
116209 pub const Entry = union(enum) {
......@@ -126,7 +219,9 @@ const std = @import("std");
126219const elf = std.elf;
127220
128221const Allocator = std.mem.Allocator;
222const Archive = @import("Archive.zig");
129223const Atom = @import("Atom.zig");
224const Cie = @import("eh_frame.zig").Cie;
130225const Elf = @import("../Elf.zig");
131226const LinkerDefined = @import("LinkerDefined.zig");
132227const Object = @import("Object.zig");
src/link/Elf/gc.zig+26-17
......@@ -1,19 +1,27 @@
11pub fn gcAtoms(elf_file: *Elf) !void {
2 var roots = std.ArrayList(*Atom).init(elf_file.base.allocator);
2 const gpa = elf_file.base.allocator;
3 const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null);
4 var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files);
5 defer files.deinit();
6 if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index);
7 for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
8
9 var roots = std.ArrayList(*Atom).init(gpa);
310 defer roots.deinit();
4 try collectRoots(&roots, elf_file);
11 try collectRoots(&roots, files.items, elf_file);
12
513 mark(roots, elf_file);
6 prune(elf_file);
14 prune(files.items, elf_file);
715}
816
9fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
17fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void {
1018 if (elf_file.entry_index) |index| {
1119 const global = elf_file.symbol(index);
1220 try markSymbol(global, roots, elf_file);
1321 }
1422
15 for (elf_file.objects.items) |index| {
16 for (elf_file.file(index).?.object.globals()) |global_index| {
23 for (files) |index| {
24 for (elf_file.file(index).?.globals()) |global_index| {
1725 const global = elf_file.symbol(global_index);
1826 if (global.file(elf_file)) |file| {
1927 if (file.index() == index and global.flags.@"export")
......@@ -22,10 +30,10 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
2230 }
2331 }
2432
25 for (elf_file.objects.items) |index| {
26 const object = elf_file.file(index).?.object;
33 for (files) |index| {
34 const file = elf_file.file(index).?;
2735
28 for (object.atoms.items) |atom_index| {
36 for (file.atoms()) |atom_index| {
2937 const atom = elf_file.atom(atom_index) orelse continue;
3038 if (!atom.flags.alive) continue;
3139
......@@ -49,9 +57,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
4957 }
5058
5159 // Mark every atom referenced by CIE as alive.
52 for (object.cies.items) |cie| {
60 for (file.cies()) |cie| {
5361 for (cie.relocs(elf_file)) |rel| {
54 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
62 const sym = elf_file.symbol(file.symbol(rel.r_sym()));
5563 try markSymbol(sym, roots, elf_file);
5664 }
5765 }
......@@ -73,11 +81,11 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
7381 if (@import("build_options").enable_logging) track_live_level.incr();
7482
7583 assert(atom.flags.visited);
76 const object = atom.file(elf_file).?.object;
84 const file = atom.file(elf_file).?;
7785
7886 for (atom.fdes(elf_file)) |fde| {
7987 for (fde.relocs(elf_file)[1..]) |rel| {
80 const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
88 const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
8189 const target_atom = target_sym.atom(elf_file) orelse continue;
8290 target_atom.flags.alive = true;
8391 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
......@@ -86,7 +94,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
8694 }
8795
8896 for (atom.relocs(elf_file)) |rel| {
89 const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
97 const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
9098 const target_atom = target_sym.atom(elf_file) orelse continue;
9199 target_atom.flags.alive = true;
92100 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
......@@ -101,9 +109,9 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
101109 }
102110}
103111
104fn prune(elf_file: *Elf) void {
105 for (elf_file.objects.items) |index| {
106 for (elf_file.file(index).?.object.atoms.items) |atom_index| {
112fn prune(files: []const File.Index, elf_file: *Elf) void {
113 for (files) |index| {
114 for (elf_file.file(index).?.atoms()) |atom_index| {
107115 const atom = elf_file.atom(atom_index) orelse continue;
108116 if (atom.flags.alive and !atom.flags.visited) {
109117 atom.flags.alive = false;
......@@ -158,4 +166,5 @@ const mem = std.mem;
158166const Allocator = mem.Allocator;
159167const Atom = @import("Atom.zig");
160168const Elf = @import("../Elf.zig");
169const File = @import("file.zig").File;
161170const Symbol = @import("Symbol.zig");
src/link/Elf/synthetic_sections.zig+39-67
......@@ -9,7 +9,7 @@ pub const DynamicSection = struct {
99
1010 pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void {
1111 const gpa = elf_file.base.allocator;
12 const off = try elf_file.dynstrtab.insert(gpa, shared.soname());
12 const off = try elf_file.insertDynString(shared.soname());
1313 try dt.needed.append(gpa, off);
1414 }
1515
......@@ -22,11 +22,11 @@ pub const DynamicSection = struct {
2222 if (i > 0) try rpath.append(':');
2323 try rpath.appendSlice(path);
2424 }
25 dt.rpath = try elf_file.dynstrtab.insert(gpa, rpath.items);
25 dt.rpath = try elf_file.insertDynString(rpath.items);
2626 }
2727
2828 pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void {
29 dt.soname = try elf_file.dynstrtab.insert(elf_file.base.allocator, soname);
29 dt.soname = try elf_file.insertDynString(soname);
3030 }
3131
3232 fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 {
......@@ -359,31 +359,24 @@ pub const ZigGotSection = struct {
359359 }
360360
361361 pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void {
362 _ = elf_file;
363362 zig_got.output_symtab_size.nlocals = @as(u32, @intCast(zig_got.entries.items.len));
364 }
365
366 pub fn updateStrtab(zig_got: ZigGotSection, elf_file: *Elf) !void {
367 const gpa = elf_file.base.allocator;
368363 for (zig_got.entries.items) |entry| {
369 const symbol_name = elf_file.symbol(entry).name(elf_file);
370 const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name});
371 defer gpa.free(name);
372 _ = try elf_file.strtab.insert(gpa, name);
364 const name = elf_file.symbol(entry).name(elf_file);
365 zig_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1;
373366 }
374367 }
375368
376 pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) !void {
377 const gpa = elf_file.base.allocator;
369 pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) void {
378370 for (zig_got.entries.items, ctx.ilocal.., 0..) |entry, ilocal, index| {
379371 const symbol = elf_file.symbol(entry);
380372 const symbol_name = symbol.name(elf_file);
381 const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name});
382 defer gpa.free(name);
383 const st_name = try elf_file.strtab.insert(gpa, name);
373 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
374 elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
375 elf_file.strtab.appendSliceAssumeCapacity("$ziggot");
376 elf_file.strtab.appendAssumeCapacity(0);
384377 const st_value = zig_got.entryAddress(@intCast(index), elf_file);
385378 const st_size = elf_file.archPtrWidthBytes();
386 ctx.symtab[ilocal] = .{
379 elf_file.symtab.items[ilocal] = .{
387380 .st_name = st_name,
388381 .st_info = elf.STT_OBJECT,
389382 .st_other = 0,
......@@ -767,25 +760,17 @@ pub const GotSection = struct {
767760 }
768761
769762 pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {
770 _ = elf_file;
771763 got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len));
772 }
773
774 pub fn updateStrtab(got: GotSection, elf_file: *Elf) !void {
775 const gpa = elf_file.base.allocator;
776764 for (got.entries.items) |entry| {
777765 const symbol_name = switch (entry.tag) {
778766 .tlsld => "",
779767 inline else => elf_file.symbol(entry.symbol_index).name(elf_file),
780768 };
781 const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) });
782 defer gpa.free(name);
783 _ = try elf_file.strtab.insert(gpa, name);
769 got.output_symtab_size.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1;
784770 }
785771 }
786772
787 pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void {
788 const gpa = elf_file.base.allocator;
773 pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) void {
789774 for (got.entries.items, ctx.ilocal..) |entry, ilocal| {
790775 const symbol = switch (entry.tag) {
791776 .tlsld => null,
......@@ -795,12 +780,14 @@ pub const GotSection = struct {
795780 .tlsld => "",
796781 inline else => symbol.?.name(elf_file),
797782 };
798 const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) });
799 defer gpa.free(name);
800 const st_name = try elf_file.strtab.insert(gpa, name);
783 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
784 elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
785 elf_file.strtab.appendAssumeCapacity('$');
786 elf_file.strtab.appendSliceAssumeCapacity(@tagName(entry.tag));
787 elf_file.strtab.appendAssumeCapacity(0);
801788 const st_value = entry.address(elf_file);
802789 const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes();
803 ctx.symtab[ilocal] = .{
790 elf_file.symtab.items[ilocal] = .{
804791 .st_name = st_name,
805792 .st_info = elf.STT_OBJECT,
806793 .st_other = 0,
......@@ -922,30 +909,22 @@ pub const PltSection = struct {
922909 }
923910
924911 pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void {
925 _ = elf_file;
926912 plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len));
927 }
928
929 pub fn updateStrtab(plt: PltSection, elf_file: *Elf) !void {
930 const gpa = elf_file.base.allocator;
931913 for (plt.symbols.items) |sym_index| {
932 const sym = elf_file.symbol(sym_index);
933 const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)});
934 defer gpa.free(name);
935 _ = try elf_file.strtab.insert(gpa, name);
914 const name = elf_file.symbol(sym_index).name(elf_file);
915 plt.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1;
936916 }
937917 }
938918
939 pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) !void {
940 const gpa = elf_file.base.allocator;
941
919 pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) void {
942920 var ilocal = ctx.ilocal;
943921 for (plt.symbols.items) |sym_index| {
944922 const sym = elf_file.symbol(sym_index);
945 const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)});
946 defer gpa.free(name);
947 const st_name = try elf_file.strtab.insert(gpa, name);
948 ctx.symtab[ilocal] = .{
923 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
924 elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
925 elf_file.strtab.appendSliceAssumeCapacity("$plt");
926 elf_file.strtab.appendAssumeCapacity(0);
927 elf_file.symtab.items[ilocal] = .{
949928 .st_name = st_name,
950929 .st_info = elf.STT_FUNC,
951930 .st_other = 0,
......@@ -1029,29 +1008,22 @@ pub const PltGotSection = struct {
10291008 }
10301009
10311010 pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void {
1032 _ = elf_file;
10331011 plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len));
1034 }
1035
1036 pub fn updateStrtab(plt_got: PltGotSection, elf_file: *Elf) !void {
1037 const gpa = elf_file.base.allocator;
10381012 for (plt_got.symbols.items) |sym_index| {
1039 const sym = elf_file.symbol(sym_index);
1040 const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)});
1041 defer gpa.free(name);
1042 _ = try elf_file.strtab.insert(gpa, name);
1013 const name = elf_file.symbol(sym_index).name(elf_file);
1014 plt_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1;
10431015 }
10441016 }
10451017
1046 pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) !void {
1047 const gpa = elf_file.base.allocator;
1018 pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) void {
10481019 var ilocal = ctx.ilocal;
10491020 for (plt_got.symbols.items) |sym_index| {
10501021 const sym = elf_file.symbol(sym_index);
1051 const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)});
1052 defer gpa.free(name);
1053 const st_name = try elf_file.strtab.insert(gpa, name);
1054 ctx.symtab[ilocal] = .{
1022 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
1023 elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
1024 elf_file.strtab.appendSliceAssumeCapacity("$pltgot");
1025 elf_file.strtab.appendAssumeCapacity(0);
1026 elf_file.symtab.items[ilocal] = .{
10551027 .st_name = st_name,
10561028 .st_info = elf.STT_FUNC,
10571029 .st_other = 0,
......@@ -1166,7 +1138,7 @@ pub const DynsymSection = struct {
11661138 new_extra.dynamic = index;
11671139 sym.setExtra(new_extra, elf_file);
11681140 } else try sym.addExtra(.{ .dynamic = index }, elf_file);
1169 const off = try elf_file.dynstrtab.insert(gpa, sym.name(elf_file));
1141 const off = try elf_file.insertDynString(sym.name(elf_file));
11701142 try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off });
11711143 }
11721144
......@@ -1251,7 +1223,7 @@ pub const HashSection = struct {
12511223 @memset(chains, 0);
12521224
12531225 for (elf_file.dynsym.entries.items, 1..) |entry, i| {
1254 const name = elf_file.dynstrtab.getAssumeExists(entry.off);
1226 const name = elf_file.getDynString(entry.off);
12551227 const hash = hasher(name) % buckets.len;
12561228 chains[@as(u32, @intCast(i))] = buckets[hash];
12571229 buckets[hash] = @as(u32, @intCast(i));
......@@ -1490,7 +1462,7 @@ pub const VerneedSection = struct {
14901462 sym.* = .{
14911463 .vn_version = 1,
14921464 .vn_cnt = 0,
1493 .vn_file = try elf_file.dynstrtab.insert(gpa, soname),
1465 .vn_file = try elf_file.insertDynString(soname),
14941466 .vn_aux = 0,
14951467 .vn_next = 0,
14961468 };
......@@ -1509,7 +1481,7 @@ pub const VerneedSection = struct {
15091481 .vna_hash = HashSection.hasher(version),
15101482 .vna_flags = 0,
15111483 .vna_other = vern.index,
1512 .vna_name = try elf_file.dynstrtab.insert(gpa, version),
1484 .vna_name = try elf_file.insertDynString(version),
15131485 .vna_next = 0,
15141486 };
15151487 verneed_sym.vn_cnt += 1;
src/link/MachO.zig+2-2
......@@ -58,7 +58,7 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{},
5858dyld_stub_binder_index: ?u32 = null,
5959dyld_private_atom_index: ?Atom.Index = null,
6060
61strtab: StringTable(.strtab) = .{},
61strtab: StringTable = .{},
6262
6363got_table: TableSection(SymbolWithLoc) = .{},
6464stub_table: TableSection(SymbolWithLoc) = .{},
......@@ -5643,7 +5643,7 @@ const Module = @import("../Module.zig");
56435643const InternPool = @import("../InternPool.zig");
56445644const Platform = load_commands.Platform;
56455645const Relocation = @import("MachO/Relocation.zig");
5646const StringTable = @import("strtab.zig").StringTable;
5646const StringTable = @import("StringTable.zig");
56475647const TableSection = @import("table_section.zig").TableSection;
56485648const Trie = @import("MachO/Trie.zig");
56495649const Type = @import("../type.zig").Type;
src/link/MachO/DebugSymbols.zig+2-2
......@@ -22,7 +22,7 @@ debug_aranges_section_dirty: bool = false,
2222debug_info_header_dirty: bool = false,
2323debug_line_header_dirty: bool = false,
2424
25strtab: StringTable(.strtab) = .{},
25strtab: StringTable = .{},
2626relocs: std.ArrayListUnmanaged(Reloc) = .{},
2727
2828pub const Reloc = struct {
......@@ -567,5 +567,5 @@ const Allocator = mem.Allocator;
567567const Dwarf = @import("../Dwarf.zig");
568568const MachO = @import("../MachO.zig");
569569const Module = @import("../../Module.zig");
570const StringTable = @import("../strtab.zig").StringTable;
570const StringTable = @import("../StringTable.zig");
571571const Type = @import("../../type.zig").Type;
src/link/MachO/zld.zig-1
......@@ -1227,7 +1227,6 @@ const LibStub = @import("../tapi.zig").LibStub;
12271227const Object = @import("Object.zig");
12281228const Platform = load_commands.Platform;
12291229const Section = MachO.Section;
1230const StringTable = @import("../strtab.zig").StringTable;
12311230const SymbolWithLoc = MachO.SymbolWithLoc;
12321231const TableSection = @import("../table_section.zig").TableSection;
12331232const Trie = @import("Trie.zig");
src/link/StringTable.zig created+49
......@@ -0,0 +1,49 @@
1buffer: std.ArrayListUnmanaged(u8) = .{},
2table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
3
4pub fn deinit(self: *Self, gpa: Allocator) void {
5 self.buffer.deinit(gpa);
6 self.table.deinit(gpa);
7}
8
9pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 {
10 const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{
11 .bytes = &self.buffer,
12 }, StringIndexContext{
13 .bytes = &self.buffer,
14 });
15 if (gop.found_existing) return gop.key_ptr.*;
16
17 try self.buffer.ensureUnusedCapacity(gpa, string.len + 1);
18 const new_off = @as(u32, @intCast(self.buffer.items.len));
19
20 self.buffer.appendSliceAssumeCapacity(string);
21 self.buffer.appendAssumeCapacity(0);
22
23 gop.key_ptr.* = new_off;
24
25 return new_off;
26}
27
28pub fn getOffset(self: *Self, string: []const u8) ?u32 {
29 return self.table.getKeyAdapted(string, StringIndexAdapter{
30 .bytes = &self.buffer,
31 });
32}
33
34pub fn get(self: Self, off: u32) ?[:0]const u8 {
35 if (off >= self.buffer.items.len) return null;
36 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0);
37}
38
39pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 {
40 return self.get(off) orelse unreachable;
41}
42
43const std = @import("std");
44const mem = std.mem;
45
46const Allocator = mem.Allocator;
47const Self = @This();
48const StringIndexAdapter = std.hash_map.StringIndexAdapter;
49const StringIndexContext = std.hash_map.StringIndexContext;
src/link/strtab.zig deleted-121
......@@ -1,121 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3
4const Allocator = mem.Allocator;
5const StringIndexAdapter = std.hash_map.StringIndexAdapter;
6const StringIndexContext = std.hash_map.StringIndexContext;
7
8pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
9 return struct {
10 const Self = @This();
11
12 const log = std.log.scoped(log_scope);
13
14 buffer: std.ArrayListUnmanaged(u8) = .{},
15 table: std.HashMapUnmanaged(u32, bool, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
16
17 pub fn deinit(self: *Self, gpa: Allocator) void {
18 self.buffer.deinit(gpa);
19 self.table.deinit(gpa);
20 }
21
22 pub fn toOwnedSlice(self: *Self, gpa: Allocator) []const u8 {
23 const result = self.buffer.toOwnedSlice(gpa);
24 self.table.clearRetainingCapacity();
25 return result;
26 }
27
28 pub const PrunedResult = struct {
29 buffer: []const u8,
30 idx_map: std.AutoHashMap(u32, u32),
31 };
32
33 pub fn toPrunedResult(self: *Self, gpa: Allocator) !PrunedResult {
34 var buffer = std.ArrayList(u8).init(gpa);
35 defer buffer.deinit();
36 try buffer.ensureTotalCapacity(self.buffer.items.len);
37 buffer.appendAssumeCapacity(0);
38
39 var idx_map = std.AutoHashMap(u32, u32).init(gpa);
40 errdefer idx_map.deinit();
41 try idx_map.ensureTotalCapacity(self.table.count());
42
43 var it = self.table.iterator();
44 while (it.next()) |entry| {
45 const off = entry.key_ptr.*;
46 const save = entry.value_ptr.*;
47 if (!save) continue;
48 const new_off = @as(u32, @intCast(buffer.items.len));
49 buffer.appendSliceAssumeCapacity(self.getAssumeExists(off));
50 idx_map.putAssumeCapacityNoClobber(off, new_off);
51 }
52
53 self.buffer.clearRetainingCapacity();
54 self.table.clearRetainingCapacity();
55
56 return PrunedResult{
57 .buffer = buffer.toOwnedSlice(),
58 .idx_map = idx_map,
59 };
60 }
61
62 pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 {
63 const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{
64 .bytes = &self.buffer,
65 }, StringIndexContext{
66 .bytes = &self.buffer,
67 });
68 if (gop.found_existing) {
69 const off = gop.key_ptr.*;
70 gop.value_ptr.* = true;
71 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
72 return off;
73 }
74
75 try self.buffer.ensureUnusedCapacity(gpa, string.len + 1);
76 const new_off = @as(u32, @intCast(self.buffer.items.len));
77
78 log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });
79
80 self.buffer.appendSliceAssumeCapacity(string);
81 self.buffer.appendAssumeCapacity(0);
82
83 gop.key_ptr.* = new_off;
84 gop.value_ptr.* = true;
85
86 return new_off;
87 }
88
89 pub fn delete(self: *Self, string: []const u8) void {
90 const value_ptr = self.table.getPtrAdapted(@as([]const u8, string), StringIndexAdapter{
91 .bytes = &self.buffer,
92 }) orelse return;
93 value_ptr.* = false;
94 log.debug("marked '{s}' for deletion", .{string});
95 }
96
97 pub fn getOffset(self: *Self, string: []const u8) ?u32 {
98 return self.table.getKeyAdapted(string, StringIndexAdapter{
99 .bytes = &self.buffer,
100 });
101 }
102
103 pub fn get(self: Self, off: u32) ?[:0]const u8 {
104 log.debug("getting string at 0x{x}", .{off});
105 if (off >= self.buffer.items.len) return null;
106 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0);
107 }
108
109 pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 {
110 return self.get(off) orelse unreachable;
111 }
112
113 pub fn items(self: Self) []const u8 {
114 return self.buffer.items;
115 }
116
117 pub fn len(self: Self) usize {
118 return self.buffer.items.len;
119 }
120 };
121}
test/link/elf.zig+183-5
......@@ -6,9 +6,13 @@ pub fn build(b: *Build) void {
66 const elf_step = b.step("test-elf", "Run ELF tests");
77 b.default_step = elf_step;
88
9 const musl_target = CrossTarget{
9 const default_target = CrossTarget{
1010 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
1111 .os_tag = .linux,
12 };
13 const musl_target = CrossTarget{
14 .cpu_arch = .x86_64,
15 .os_tag = .linux,
1216 .abi = .musl,
1317 };
1418 const glibc_target = CrossTarget{
......@@ -18,7 +22,10 @@ pub fn build(b: *Build) void {
1822 };
1923
2024 // Exercise linker with self-hosted backend (no LLVM)
21 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));
25 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
26 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
27 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
28 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
2229 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target }));
2330 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target }));
2431
......@@ -876,6 +883,110 @@ fn testGcSections(b: *Build, opts: Options) *Step {
876883 return test_step;
877884}
878885
886fn testGcSectionsZig(b: *Build, opts: Options) *Step {
887 const test_step = addTestStep(b, "gc-sections-zig", opts);
888
889 const obj = addObject(b, "obj", .{
890 .target = opts.target,
891 .use_llvm = true,
892 .use_lld = true,
893 });
894 addCSourceBytes(obj,
895 \\int live_var1 = 1;
896 \\int live_var2 = 2;
897 \\int dead_var1 = 3;
898 \\int dead_var2 = 4;
899 \\void live_fn1() {}
900 \\void live_fn2() { live_fn1(); }
901 \\void dead_fn1() {}
902 \\void dead_fn2() { dead_fn1(); }
903 , &.{});
904 obj.link_function_sections = true;
905 obj.link_data_sections = true;
906
907 {
908 const exe = addExecutable(b, "test1", opts);
909 addZigSourceBytes(exe,
910 \\const std = @import("std");
911 \\extern var live_var1: i32;
912 \\extern var live_var2: i32;
913 \\extern fn live_fn2() void;
914 \\pub fn main() void {
915 \\ const stdout = std.io.getStdOut();
916 \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
917 \\ live_fn2();
918 \\}
919 );
920 exe.addObject(obj);
921 exe.link_gc_sections = false;
922
923 const run = addRunArtifact(exe);
924 run.expectStdOutEqual("1 2\n");
925 test_step.dependOn(&run.step);
926
927 const check = exe.checkObject();
928 check.checkInSymtab();
929 check.checkContains("live_var1");
930 check.checkInSymtab();
931 check.checkContains("live_var2");
932 check.checkInSymtab();
933 check.checkContains("dead_var1");
934 check.checkInSymtab();
935 check.checkContains("dead_var2");
936 check.checkInSymtab();
937 check.checkContains("live_fn1");
938 check.checkInSymtab();
939 check.checkContains("live_fn2");
940 check.checkInSymtab();
941 check.checkContains("dead_fn1");
942 check.checkInSymtab();
943 check.checkContains("dead_fn2");
944 test_step.dependOn(&check.step);
945 }
946
947 {
948 const exe = addExecutable(b, "test2", opts);
949 addZigSourceBytes(exe,
950 \\const std = @import("std");
951 \\extern var live_var1: i32;
952 \\extern var live_var2: i32;
953 \\extern fn live_fn2() void;
954 \\pub fn main() void {
955 \\ const stdout = std.io.getStdOut();
956 \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
957 \\ live_fn2();
958 \\}
959 );
960 exe.addObject(obj);
961 exe.link_gc_sections = true;
962
963 const run = addRunArtifact(exe);
964 run.expectStdOutEqual("1 2\n");
965 test_step.dependOn(&run.step);
966
967 const check = exe.checkObject();
968 check.checkInSymtab();
969 check.checkContains("live_var1");
970 check.checkInSymtab();
971 check.checkContains("live_var2");
972 check.checkInSymtab();
973 check.checkNotPresent("dead_var1");
974 check.checkInSymtab();
975 check.checkNotPresent("dead_var2");
976 check.checkInSymtab();
977 check.checkContains("live_fn1");
978 check.checkInSymtab();
979 check.checkContains("live_fn2");
980 check.checkInSymtab();
981 check.checkNotPresent("dead_fn1");
982 check.checkInSymtab();
983 check.checkNotPresent("dead_fn2");
984 test_step.dependOn(&check.step);
985 }
986
987 return test_step;
988}
989
879990fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
880991 const test_step = addTestStep(b, "hidden-weak-undef", opts);
881992
......@@ -1714,6 +1825,72 @@ fn testLinkingCpp(b: *Build, opts: Options) *Step {
17141825 return test_step;
17151826}
17161827
1828fn testLinkingObj(b: *Build, opts: Options) *Step {
1829 const test_step = addTestStep(b, "linking-obj", opts);
1830
1831 const obj = addObject(b, "aobj", opts);
1832 addZigSourceBytes(obj,
1833 \\extern var mod: usize;
1834 \\export fn callMe() usize {
1835 \\ return me * mod;
1836 \\}
1837 \\var me: usize = 42;
1838 );
1839
1840 const exe = addExecutable(b, "testobj", opts);
1841 addZigSourceBytes(exe,
1842 \\const std = @import("std");
1843 \\extern fn callMe() usize;
1844 \\export var mod: usize = 2;
1845 \\pub fn main() void {
1846 \\ std.debug.print("{d}\n", .{callMe()});
1847 \\}
1848 );
1849 exe.addObject(obj);
1850
1851 const run = addRunArtifact(exe);
1852 run.expectStdErrEqual("84\n");
1853 test_step.dependOn(&run.step);
1854
1855 return test_step;
1856}
1857
1858fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
1859 const test_step = addTestStep(b, "linking-static-lib", opts);
1860
1861 const lib = b.addStaticLibrary(.{
1862 .name = "alib",
1863 .target = opts.target,
1864 .optimize = opts.optimize,
1865 .use_llvm = opts.use_llvm,
1866 .use_lld = false,
1867 });
1868 addZigSourceBytes(lib,
1869 \\extern var mod: usize;
1870 \\export fn callMe() usize {
1871 \\ return me * mod;
1872 \\}
1873 \\var me: usize = 42;
1874 );
1875
1876 const exe = addExecutable(b, "testlib", opts);
1877 addZigSourceBytes(exe,
1878 \\const std = @import("std");
1879 \\extern fn callMe() usize;
1880 \\export var mod: usize = 2;
1881 \\pub fn main() void {
1882 \\ std.debug.print("{d}\n", .{callMe()});
1883 \\}
1884 );
1885 exe.linkLibrary(lib);
1886
1887 const run = addRunArtifact(exe);
1888 run.expectStdErrEqual("84\n");
1889 test_step.dependOn(&run.step);
1890
1891 return test_step;
1892}
1893
17171894fn testLinkingZig(b: *Build, opts: Options) *Step {
17181895 const test_step = addTestStep(b, "linking-zig-static", opts);
17191896
......@@ -3114,6 +3291,7 @@ const Options = struct {
31143291 target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
31153292 optimize: std.builtin.OptimizeMode = .Debug,
31163293 use_llvm: bool = true,
3294 use_lld: bool = false,
31173295};
31183296
31193297fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
......@@ -3134,7 +3312,7 @@ fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
31343312 .target = opts.target,
31353313 .optimize = opts.optimize,
31363314 .use_llvm = opts.use_llvm,
3137 .use_lld = false,
3315 .use_lld = opts.use_lld,
31383316 });
31393317}
31403318
......@@ -3144,7 +3322,7 @@ fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
31443322 .target = opts.target,
31453323 .optimize = opts.optimize,
31463324 .use_llvm = opts.use_llvm,
3147 .use_lld = false,
3325 .use_lld = opts.use_lld,
31483326 });
31493327}
31503328
......@@ -3164,7 +3342,7 @@ fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
31643342 .target = opts.target,
31653343 .optimize = opts.optimize,
31663344 .use_llvm = opts.use_llvm,
3167 .use_lld = false,
3345 .use_lld = opts.use_lld,
31683346 });
31693347}
31703348