authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-09 10:18:46+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-22 12:14:27+01:00
log93b5572d4e58b77d85bf059aca160ea09ce9e465
tree7e96a310e77e5304af5f235cd12298ebb0dc06e8
parent215b8ba6fdfa31699be3a1cf76ab406694dcac72
signature Commit is signed but in an unrecognized format.

Elf2: refactor global symbol representation

Instead of 4 separate hash maps for different kinds of global symbol, store all global symbols in one contiguous array, and exploit some sometimes-unused bits to represent the state previously held by the 4 hash maps. There is an `ArrayHashMap` for name lookups, which, instead of redundantly storing the `String(.strtab)` symbol name again, just pulls that data straight from the symtab, decreasing memory usage by 4 bytes per global symbol. (This `ArrayHashMap` could have itself contained the array of globals, but when using custom contexts/adapters I find it simpler and cleaner to separate the lookup data structure from the actual data storage. Indices into the array of global symbols are *not* the same as (nor in any way related to!) indices into `.symtab` or `.dynsym`.

1 files changed, 616 insertions(+), 488 deletions(-)

src/link/Elf2.zig+616-488
...@@ -75,32 +75,30 @@ dynamic: struct {...@@ -75,32 +75,30 @@ dynamic: struct {
75 soname: String(.dynstr),75 soname: String(.dynstr),
76},76},
77symtab: std.ArrayList(Symbol),77symtab: std.ArrayList(Symbol),
78globals: struct {78globals: std.ArrayList(Symbol.Global),
79 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),79/// Accessed with `Symbol.Global.NameAdapter`. Items map 1--1 to `globals`.
80 weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),80globals_by_name: std.array_hash_map.Custom(void, void, void, true),
81 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),81/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We
82 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),82/// maintain this set to allow efficient reporting of "undefined global symbol" errors.
83},83unknown_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
84/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).84/// Key is an undef global for which we have created a "copy relocation" (`R_*_COPY`).
85copied_globals: std.array_hash_map.Auto(String(.strtab), struct {85copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, struct {
86 node: MappedFile.Node.Index,86 node: MappedFile.Node.Index,
87 /// The index of this global's runtime relocation in `.rela.dyn`.87 /// The index of this global's runtime relocation in `.rela.dyn`.
88 rela_index: Section.RelaIndex,88 rela_index: Section.RelaIndex,
89}),89}),
90/// Key is the name of an undef global for which we would *like* to create a copy relocation90/// Key is an undef global for which we would *like* to create a copy relocation (`R_*_COPY`), but
91/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.91/// cannot because we have not seen an appropriate definition in a linked DSO yet.
92///92///
93/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we93/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
94/// will remove it from this map and call `maybeAddCopyRelocation`.94/// will remove it from this map and call `maybeAddCopyRelocation`.
95want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),95want_copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
96/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol96/// Key is a node which is a valid `Symbol.node` value, value is the first global symbol in that
97/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.97/// node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
98///
99/// Value is never `.empty`.
100///98///
101/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,99/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
102/// because the vast majority of nodes which can export global symbols actually will not.100/// because the vast majority of nodes which can export global symbols actually will not.
103node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),101node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Global.Index),
104/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:102/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:
105///103///
106/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has104/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
...@@ -132,7 +130,7 @@ dynstr: StringTable,...@@ -132,7 +130,7 @@ dynstr: StringTable,
132///130///
133/// Value is the output relocation in `.rela.dyn` for the GOT entry.131/// Value is the output relocation in `.rela.dyn` for the GOT entry.
134got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),132got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
135/// Key is the name of a global.133/// Key is a global with a PLT entry.
136///134///
137/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into135/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
138/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime136/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
...@@ -140,7 +138,7 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),...@@ -140,7 +138,7 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
140///138///
141/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is139/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
142/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.140/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
143plt: std.array_hash_map.Auto(String(.strtab), void),141plt: std.array_hash_map.Auto(Symbol.Global.Index, void),
144/// The `.plt` section contains zero or more symbol relocations starting at this index.142/// The `.plt` section contains zero or more symbol relocations starting at this index.
145plt_first_symbol_reloc: SymbolReloc.Index,143plt_first_symbol_reloc: SymbolReloc.Index,
146/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.144/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.
...@@ -203,12 +201,13 @@ got_relocs: std.ArrayList(GotReloc),...@@ -203,12 +201,13 @@ got_relocs: std.ArrayList(GotReloc),
203tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),201tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
204/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.202/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
205section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),203section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
206/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation204/// Key is a global symbol which has been moved to a new index in the symbol table. Any relocation
207/// entries which target that symbol must be updated to reference the correct symbol index.205/// entries which target that symbol must be updated to reference the correct symbol index.
208///206///
209/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it207/// * In an `ET_REL`, this means the index in `.symtab` has changed.
210/// refers to the index in `.dynsym`.208/// * In a DSO (or static PIE), this means the index in `.dynsym` has changed.
211changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),209/// * Otherwise this is always empty.
210changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
212/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`211/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
213/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`212/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
214/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.213/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
...@@ -271,14 +270,14 @@ const Node = union(enum) {...@@ -271,14 +270,14 @@ const Node = union(enum) {
271 section_manual_size: Section.Index,270 section_manual_size: Section.Index,
272 /// May contain relocations.271 /// May contain relocations.
273 input_section: InputSection.Index,272 input_section: InputSection.Index,
274 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for273 /// Value is a global which has an entry in `elf.copied_globals`, so, a global for which we have
275 /// which we have emitted a copy relocation.274 /// emitted a copy relocation.
276 ///275 ///
277 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.276 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
278 ///277 ///
279 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because278 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
280 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.279 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.
281 copied_global: String(.strtab),280 copied_global: Symbol.Global.Index,
282 /// May contain relocations.281 /// May contain relocations.
283 nav: NavMapIndex,282 nav: NavMapIndex,
284 /// May contain relocations.283 /// May contain relocations.
...@@ -2200,10 +2199,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -2200,10 +2199,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
2200 switch (kind) {2199 switch (kind) {
2201 .all_local => {},2200 .all_local => {},
2202 .maybe_global => {2201 .maybe_global => {
2203 try elf.globals.strong_def.ensureUnusedCapacity(gpa, len);2202 try elf.globals.ensureUnusedCapacity(gpa, len);
2204 try elf.globals.weak_def.ensureUnusedCapacity(gpa, len);2203 try elf.globals_by_name.ensureUnusedCapacity(gpa, len);
2205 try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len);2204 try elf.unknown_globals.ensureUnusedCapacity(gpa, len);
2206 try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len);
22072205
2208 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);2206 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
22092207
...@@ -2283,10 +2281,10 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {...@@ -2283,10 +2281,10 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
2283 chains[0] = 0;2281 chains[0] = 0;
2284 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {2282 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
2285 const dynsym_index: u32 = @intCast(dynsym_index_usize);2283 const dynsym_index: u32 = @intCast(dynsym_index_usize);
2286 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {2284 const dynsym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2287 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),2285 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2288 };2286 };
2289 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;2287 const b = std.elf.hash.calculate(dynsym_name.slice(elf)) % buckets.len;
2290 // Make this symbol the head of that bucket, and chain to the old head.2288 // Make this symbol the head of that bucket, and chain to the old head.
2291 chain.* = buckets[b];2289 chain.* = buckets[b];
2292 elf.targetStore(&buckets[b], dynsym_index);2290 elf.targetStore(&buckets[b], dynsym_index);
...@@ -2326,10 +2324,10 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {...@@ -2326,10 +2324,10 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2326 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];2324 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
2327 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];2325 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
23282326
2329 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {2327 const dynsym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2330 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),2328 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2331 };2329 };
2332 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;2330 const b = std.elf.hash.calculate(dynsym_name.slice(elf)) % buckets.len;
2333 // Make this symbol the head of that bucket, and chain to the old head.2331 // Make this symbol the head of that bucket, and chain to the old head.
2334 chains[dynsym_index] = buckets[b];2332 chains[dynsym_index] = buckets[b];
2335 elf.targetStore(&buckets[b], dynsym_index);2333 elf.targetStore(&buckets[b], dynsym_index);
...@@ -2367,10 +2365,10 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {...@@ -2367,10 +2365,10 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2367 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];2365 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
2368 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];2366 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
23692367
2370 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {2368 const dynsym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2371 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),2369 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2372 };2370 };
2373 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;2371 const b = std.elf.hash.calculate(dynsym_name.slice(elf)) % buckets.len;
23742372
2375 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);2373 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
2376 elf.targetStore(&chains[dynsym_index], 0);2374 elf.targetStore(&chains[dynsym_index], 0);
...@@ -2479,11 +2477,12 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2479,11 +2477,12 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2479 new_index.ptr(elf).* = target_index.ptr(elf).*;2477 new_index.ptr(elf).* = target_index.ptr(elf).*;
2480 // ...then update the `elf.globals` tracking.2478 // ...then update the `elf.globals` tracking.
2481 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));2479 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));
2482 elf.globalByName(global_name).?.symtab_index = new_index;2480 const gsi = elf.globalByName(global_name.slice(elf)).?;
2481 gsi.ptr(elf).symtab_index = new_index;
24832482
2484 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {2483 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
2485 // This symbol's index is changing, so queue an update of relocs targeting it.2484 // This symbol's index is changing, so queue an update of relocs targeting it.
2486 elf.changed_symtab_index.putAssumeCapacity(global_name, {});2485 elf.changed_symtab_index.putAssumeCapacity(gsi, {});
2487 }2486 }
2488 }2487 }
24892488
...@@ -2509,140 +2508,84 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2509,140 +2508,84 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2509 }2508 }
2510}2509}
25112510
2512const AddGlobalSymbolOptions = struct {2511fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
2513 const Name = struct {
2514 strtab: String(.strtab),
2515 dynstr: String(.dynstr),
2516 fn string(elf: *Elf, slice: []const u8) Error!Name {
2517 return .{
2518 .strtab = try elf.string(.strtab, slice),
2519 .dynstr = switch (elf.shndx.dynsym) {
2520 .UNDEF => .empty,
2521 else => try elf.string(.dynstr, slice),
2522 },
2523 };
2524 }
2525 };
2526
2527 node: MappedFile.Node.Index.Optional,2512 node: MappedFile.Node.Index.Optional,
2528 name: Name,2513 name: []const u8,
2529 lib_name: ?[]const u8 = null,2514 lib_name: ?[]const u8 = null,
2530 value: u64,2515 value: u64,
2531 size: u64,2516 size: u64,
2532 type: std.elf.STT,2517 type: std.elf.STT,
2533 bind: enum { strong, weak },2518 bind: Symbol.Global.Bind,
2534 visibility: std.elf.STV,2519 visibility: std.elf.STV,
2535 shndx: Section.Index,2520 shndx: Section.Index,
2536};2521}) (error{MultipleDefinitions} || Error)!Symbol.Global.Index {
2537fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{MultipleDefinitions}!Symbol.Id {
2538 _ = opts.lib_name; // TODO2522 _ = opts.lib_name; // TODO
25392523
2540 if (elf.shndx.dynsym == .UNDEF) {2524 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };
2541 assert(opts.name.dynstr == .empty);2525 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(opts.name, adapter);
2542 } else {2526 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));
2543 assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));2527
2544 }2528 if (gop.found_existing) {
25452529 // There's already a symbol with this name, we just need to potentially set its value, and
2546 // We break from this `switch` only if this symbol name did not previously exist at all and so2530 // to update its bind and visibility.
2547 // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need2531 const new_bind: Symbol.Global.Bind = new_bind: {
2548 // a new symtab entry.2532 if (opts.shndx == .UNDEF) {
2549 const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {2533 // We are not defining the symbol, so we won't set its value and we should only
2550 .strong => new_global: {2534 // update its bind if it is undefined and we won't weaken it.
2551 const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);2535 if (!Symbol.Id.global(gsi).defined(elf) and
2552 if (gop.found_existing) return error.MultipleDefinitions;2536 gsi.bind(elf) == .weak and
2553 const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse2537 opts.bind == .strong)
2554 elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse2538 {
2555 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {2539 // Since the symbol is not only undefined but is now also strong, we now *need*
2556 // The symbol did not already exist, so we'll use the "new global" path.2540 // to see a definition for it at some point.
2557 break :new_global gop.value_ptr;2541 if (!elf.dso_globals.contains(Symbol.Id.global(gsi).name(elf))) {
2558 };2542 elf.unknown_globals.putAssumeCapacityNoClobber(gsi, {});
2559 gop.value_ptr.* = old_kv.value;2543 }
2560 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{2544 break :new_bind .strong;
2561 .node = opts.node,2545 } else {
2562 .value = opts.value,2546 break :new_bind gsi.bind(elf);
2563 .size = opts.size,2547 }
2564 .type = opts.type,
2565 .shndx = opts.shndx,
2566 });
2567 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2568 return .global(opts.name.strtab);
2569 },
2570 .weak => new_global: {
2571 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2572 // The existing definition holds, we just merge our visibility in.
2573 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2574 return .global(opts.name.strtab);
2575 }
2576 const gop = elf.globals.weak_def.getOrPutAssumeCapacity(opts.name.strtab);
2577 if (gop.found_existing) {
2578 // The existing definition holds, we just merge our visibility in.
2579 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2580 return .global(opts.name.strtab);
2581 }
2582 const old_kv = elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2583 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2584 // The symbol did not already exist, so we'll use the "new global" path.
2585 break :new_global gop.value_ptr;
2586 };
2587 gop.value_ptr.* = old_kv.value;
2588 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2589 .node = opts.node,
2590 .value = opts.value,
2591 .size = opts.size,
2592 .type = opts.type,
2593 .shndx = opts.shndx,
2594 });
2595 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2596 return .global(opts.name.strtab);
2597 },
2598 } else switch (opts.bind) {
2599 .strong => new_global: {
2600 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2601 // The existing definition holds, we just merge our visibility in.
2602 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2603 return .global(opts.name.strtab);
2604 }
2605 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2606 // The existing definition holds, we just merge our visibility in.
2607 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2608 return .global(opts.name.strtab);
2609 }
2610 const gop = elf.globals.strong_undef.getOrPutAssumeCapacity(opts.name.strtab);
2611 if (gop.found_existing) {
2612 // The existing symbol is okay, we just merge our visibility in.
2613 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2614 return .global(opts.name.strtab);
2615 }
2616 const old_kv = elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2617 // The symbol did not already exist, so we'll use the "new global" path.
2618 break :new_global gop.value_ptr;
2619 };
2620 gop.value_ptr.* = old_kv.value;
2621 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2622 return .global(opts.name.strtab);
2623 },
2624 .weak => new_global: {
2625 if (elf.globals.strong_def.getPtr(opts.name.strtab) orelse
2626 elf.globals.strong_undef.getPtr(opts.name.strtab)) |global|
2627 {
2628 // The existing symbol is okay, we just merge our visibility in.
2629 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2630 return .global(opts.name.strtab);
2631 }
2632 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2633 // The existing symbol is okay, we just merge our visibility in.
2634 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2635 return .global(opts.name.strtab);
2636 }2548 }
2637 const gop = elf.globals.weak_undef.getOrPutAssumeCapacity(opts.name.strtab);2549 // We're providing a definition. If there is no other definition yet, that's easy, but
2638 if (gop.found_existing) {2550 // if there is, we need to figure out which to keep.
2639 // The existing symbol is okay, we just merge our visibility in.2551 if (!Symbol.Id.global(gsi).defined(elf)) {
2640 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);2552 _ = elf.unknown_globals.swapRemove(gsi); // previously undef so might be here
2641 return .global(opts.name.strtab);2553 elf.setGlobalSymbolValue(gsi, .{
2554 .node = opts.node,
2555 .value = opts.value,
2556 .size = opts.size,
2557 .type = opts.type,
2558 .shndx = opts.shndx,
2559 });
2560 break :new_bind opts.bind;
2561 }
2562 switch (opts.bind) {
2563 .strong => switch (gsi.bind(elf)) {
2564 .strong => return error.MultipleDefinitions,
2565 .weak => {
2566 elf.setGlobalSymbolValue(gsi, .{
2567 .node = opts.node,
2568 .value = opts.value,
2569 .size = opts.size,
2570 .type = opts.type,
2571 .shndx = opts.shndx,
2572 });
2573 break :new_bind .strong;
2574 },
2575 },
2576 .weak => {
2577 break :new_bind gsi.bind(elf);
2578 },
2642 }2579 }
2643 break :new_global gop.value_ptr;2580 };
2644 },2581 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility, new_bind);
2645 };2582 return gsi;
2583 }
2584
2585 assert(elf.globals.items.len == @backingInt(gsi));
2586 assert(elf.globals.addOneAssumeCapacity() == gsi.ptr(elf)); // populated later
2587
2588 const strtab_name = try elf.string(.strtab, opts.name);
26462589
2647 const force_local_bind: bool = switch (opts.visibility) {2590 const force_local_bind: bool = switch (opts.visibility) {
2648 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,2591 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
...@@ -2657,7 +2600,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2657,7 +2600,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2657 };2600 };
26582601
2659 const @"type": std.elf.STT = switch (opts.type) {2602 const @"type": std.elf.STT = switch (opts.type) {
2660 .NOTYPE => if (elf.dso_globals.get(opts.name.strtab)) |dso_global| t: {2603 .NOTYPE => if (elf.dso_globals.get(strtab_name)) |dso_global| t: {
2661 break :t dso_global.type;2604 break :t dso_global.type;
2662 } else .NOTYPE,2605 } else .NOTYPE,
2663 else => |t| t,2606 else => |t| t,
...@@ -2678,7 +2621,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2678,7 +2621,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2678 // ...then populate the newly-valid symbol pointer2621 // ...then populate the newly-valid symbol pointer
2679 const sym = @field(elf.symPtr(sym_index), @tagName(class));2622 const sym = @field(elf.symPtr(sym_index), @tagName(class));
2680 sym.* = .{2623 sym.* = .{
2681 .name = @backingInt(opts.name.strtab),2624 .name = @backingInt(strtab_name),
2682 .value = @intCast(opts.value),2625 .value = @intCast(opts.value),
2683 .size = @intCast(opts.size),2626 .size = @intCast(opts.size),
2684 .info = .{ .type = @"type", .bind = bind },2627 .info = .{ .type = @"type", .bind = bind },
...@@ -2691,19 +2634,30 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2691,19 +2634,30 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2691 },2634 },
2692 }2635 }
26932636
2694 const old_head: String(.strtab) = old_head: {2637 const old_head: Symbol.Global.Index.Optional = old_head: {
2695 const node = opts.node.unwrap() orelse break :old_head .empty;2638 const node = opts.node.unwrap() orelse break :old_head .none;
2696 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);2639 const node_globals_gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2697 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;2640 const old_head: Symbol.Global.Index.Optional = if (node_globals_gop.found_existing)
2698 gop.value_ptr.* = opts.name.strtab;2641 .wrap(node_globals_gop.value_ptr.*)
2642 else
2643 .none;
2644 node_globals_gop.value_ptr.* = gsi;
2699 break :old_head old_head;2645 break :old_head old_head;
2700 };2646 };
27012647
2702 new_global_ptr.* = .{2648 gsi.ptr(elf).* = .{
2703 .symtab_index = sym_index,2649 .symtab_index = sym_index,
2704 .dynsym_index = dynsym_index: {2650 .dynsym_index = dynsym_index: {
2705 if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;2651 if (force_local_bind) {
2706 if (force_local_bind) break :dynsym_index 0;2652 break :dynsym_index switch (opts.bind) {
2653 .strong => .demoted_strong,
2654 .weak => .demoted_weak,
2655 };
2656 }
2657 if (elf.shndx.dynsym == .UNDEF) {
2658 break :dynsym_index .no_section;
2659 }
2660 const dynstr_name = try elf.string(.dynstr, opts.name);
2707 switch (elf.shdrPtr(elf.shndx.dynsym)) {2661 switch (elf.shdrPtr(elf.shndx.dynsym)) {
2708 inline else => |shdr, class| {2662 inline else => |shdr, class| {
2709 const Sym = class.ElfN().Sym;2663 const Sym = class.ElfN().Sym;
...@@ -2714,7 +2668,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2714,7 +2668,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2714 // ...then populate the newly-valid symbol pointer2668 // ...then populate the newly-valid symbol pointer
2715 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));2669 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2716 sym.* = .{2670 sym.* = .{
2717 .name = @backingInt(opts.name.dynstr),2671 .name = @backingInt(dynstr_name),
2718 .value = @intCast(opts.value),2672 .value = @intCast(opts.value),
2719 .size = @intCast(opts.size),2673 .size = @intCast(opts.size),
2720 .info = .{ .type = @"type", .bind = bind },2674 .info = .{ .type = @"type", .bind = bind },
...@@ -2725,76 +2679,76 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2725,76 +2679,76 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2725 std.mem.byteSwapAllFields(Sym, sym);2679 std.mem.byteSwapAllFields(Sym, sym);
2726 }2680 }
2727 elf.appendDynsymHashEntry(dynsym_index);2681 elf.appendDynsymHashEntry(dynsym_index);
2728 break :dynsym_index dynsym_index;2682 break :dynsym_index .wrap(dynsym_index);
2729 },2683 },
2730 }2684 }
2731 },2685 },
2732 .prev_in_node = .empty,2686 .prev_in_node = .none,
2733 .next_in_node = old_head,2687 .next_in_node = old_head,
2734 };2688 };
27352689
2736 if (old_head != .empty) {2690 if (old_head.unwrap()) |old_head_gsi| {
2737 const old_head_ptr = elf.globalByName(old_head).?;2691 const old_head_ptr = old_head_gsi.ptr(elf);
2738 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);2692 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
2739 assert(old_head_ptr.prev_in_node == .empty);2693 assert(old_head_ptr.prev_in_node == .none);
2740 old_head_ptr.prev_in_node = opts.name.strtab;2694 old_head_ptr.prev_in_node = .wrap(gsi);
2695 }
2696
2697 if (opts.shndx == .UNDEF and
2698 opts.bind == .strong and
2699 !elf.dso_globals.contains(strtab_name))
2700 {
2701 elf.unknown_globals.putAssumeCapacityNoClobber(gsi, {});
2741 }2702 }
27422703
2743 if (force_local_bind) {2704 if (force_local_bind) {
2744 elf.moveDemotedGlobal(new_global_ptr);2705 elf.moveDemotedGlobal(gsi);
2745 }2706 }
27462707
2747 switch (@"type") {2708 switch (@"type") {
2748 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and2709 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2749 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)2710 elf.classifySymbolValue(.global(gsi)) == .dynamic)
2750 {2711 {
2751 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.2712 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
2752 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);2713 elf.addPltEntry(gsi);
2753 },2714 },
2754 else => {},2715 else => {},
2755 }2716 }
27562717
2757 return .global(opts.name.strtab);2718 return gsi;
2758}2719}
2759fn setGlobalSymbolValue(2720fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
2760 elf: *Elf,2721 node: MappedFile.Node.Index.Optional,
2761 global_name: String(.strtab),2722 value: u64,
2762 global_ptr: *Symbol.Global,2723 size: u64,
2763 new: struct {2724 type: std.elf.STT,
2764 node: MappedFile.Node.Index.Optional,2725 shndx: Section.Index,
2765 value: u64,2726}) void {
2766 size: u64,2727 const global_ptr = gsi.ptr(elf);
2767 type: std.elf.STT,2728
2768 shndx: Section.Index,
2769 },
2770) void {
2771 assert(new.shndx != .UNDEF);2729 assert(new.shndx != .UNDEF);
2772 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {2730 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2773 if (global_ptr.next_in_node != .empty) {2731 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
2774 const next = elf.globalByName(global_ptr.next_in_node).?;2732 assert(next_gsi.ptr(elf).prev_in_node == Symbol.Global.Index.Optional.wrap(gsi));
2775 assert(next.prev_in_node == global_name);2733 next_gsi.ptr(elf).prev_in_node = global_ptr.prev_in_node;
2776 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);2734 }
2777 next.prev_in_node = global_ptr.prev_in_node;2735 if (global_ptr.prev_in_node.unwrap()) |prev_gsi| {
2778 }2736 assert(prev_gsi.ptr(elf).next_in_node == Symbol.Global.Index.Optional.wrap(gsi));
2779 if (global_ptr.prev_in_node != .empty) {2737 prev_gsi.ptr(elf).next_in_node = global_ptr.next_in_node;
2780 const prev = elf.globalByName(global_ptr.prev_in_node).?;
2781 assert(prev.next_in_node == global_name);
2782 assert(prev.symtab_index.ptr(elf).node.unwrap().? == old_node);
2783 prev.next_in_node = global_ptr.next_in_node;
2784 } else {2738 } else {
2785 // We're the start of the linked list, so we need to change the head.2739 // We're the start of the linked list, so we need to change the head.
2786 if (global_ptr.next_in_node == .empty) {2740 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
2787 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);2741 elf.node_global_symbols.getPtr(old_node).?.* = next_gsi;
2788 } else {2742 } else {
2789 elf.node_global_symbols.getPtr(old_node).?.* = global_ptr.next_in_node;2743 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == gsi);
2790 }2744 }
2791 }2745 }
2792 } else {2746 } else {
2793 assert(global_ptr.next_in_node == .empty);2747 assert(global_ptr.next_in_node == .none);
2794 assert(global_ptr.prev_in_node == .empty);2748 assert(global_ptr.prev_in_node == .none);
2795 }2749 }
27962750
2797 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {2751 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
2798 // This is a quite rare case: there was a definition for this symbol in a shared library2752 // This is a quite rare case: there was a definition for this symbol in a shared library
2799 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*2753 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
2800 // definition which replaces it. We know that our definition cannot be preempted because we2754 // definition which replaces it. We know that our definition cannot be preempted because we
...@@ -2808,27 +2762,25 @@ fn setGlobalSymbolValue(...@@ -2808,27 +2762,25 @@ fn setGlobalSymbolValue(
2808 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we2762 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
2809 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.2763 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
2810 } else {2764 } else {
2811 _ = elf.want_copied_globals.swapRemove(global_name);2765 _ = elf.want_copied_globals.swapRemove(gsi);
2812 }2766 }
28132767
2814 global_ptr.symtab_index.ptr(elf).node = new.node;2768 global_ptr.symtab_index.ptr(elf).node = new.node;
28152769
2816 const old_head: String(.strtab) = old_head: {2770 const old_head: Symbol.Global.Index.Optional = old_head: {
2817 const new_node = new.node.unwrap() orelse break :old_head .empty;2771 const new_node = new.node.unwrap() orelse break :old_head .none;
2818 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);2772 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2819 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;2773 const old_head: Symbol.Global.Index.Optional = if (gop.found_existing) .wrap(gop.value_ptr.*) else .none;
2820 gop.value_ptr.* = global_name;2774 gop.value_ptr.* = gsi;
2821 break :old_head old_head;2775 break :old_head old_head;
2822 };2776 };
28232777
2824 global_ptr.prev_in_node = .empty;2778 global_ptr.prev_in_node = .none;
2825 global_ptr.next_in_node = old_head;2779 global_ptr.next_in_node = old_head;
28262780
2827 if (old_head != .empty) {2781 if (old_head.unwrap()) |old_head_gsi| {
2828 const old_head_ptr = elf.globalByName(old_head).?;2782 assert(old_head_gsi.ptr(elf).prev_in_node == .none);
2829 assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);2783 old_head_gsi.ptr(elf).prev_in_node = .wrap(gsi);
2830 assert(old_head_ptr.prev_in_node == .empty);
2831 old_head_ptr.prev_in_node = global_name;
2832 }2784 }
28332785
2834 // Now for the easy bit where we actually update the symtab entry.2786 // Now for the easy bit where we actually update the symtab entry.
...@@ -2846,7 +2798,7 @@ fn setGlobalSymbolValue(...@@ -2846,7 +2798,7 @@ fn setGlobalSymbolValue(
2846 }2798 }
28472799
2848 // ...and also the dynsym entry if there is one.2800 // ...and also the dynsym entry if there is one.
2849 if (global_ptr.dynsym_index != 0) switch (elf.dynsymPtr(global_ptr.dynsym_index)) {2801 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| switch (elf.dynsymPtr(dynsym_index)) {
2850 inline else => |sym| {2802 inline else => |sym| {
2851 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.2803 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2852 elf.targetStore(&sym.size, @intCast(new.size));2804 elf.targetStore(&sym.size, @intCast(new.size));
...@@ -2862,9 +2814,9 @@ fn setGlobalSymbolValue(...@@ -2862,9 +2814,9 @@ fn setGlobalSymbolValue(
2862 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to2814 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
2863 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.2815 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
2864 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.2816 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
2865 if (elf.plt.getIndex(global_name)) |plt_index| {2817 if (elf.plt.getIndex(gsi)) |plt_index| {
2866 if (!elf.pltEntryIsDead(plt_index) and2818 if (!elf.pltEntryIsDead(plt_index) and
2867 elf.classifySymbolValue(.global(global_name)) != .dynamic)2819 elf.classifySymbolValue(.global(gsi)) != .dynamic)
2868 {2820 {
2869 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));2821 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
2870 assert(elf.pltEntryIsDead(plt_index));2822 assert(elf.pltEntryIsDead(plt_index));
...@@ -2874,81 +2826,155 @@ fn setGlobalSymbolValue(...@@ -2874,81 +2826,155 @@ fn setGlobalSymbolValue(
2874 // If this symbol was previously undefined, relocations targeting it may have been lowered to2826 // If this symbol was previously undefined, relocations targeting it may have been lowered to
2875 // runtime relocations which we have now discovered we do not need, so delete those. This does2827 // runtime relocations which we have now discovered we do not need, so delete those. This does
2876 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.2828 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.
2877 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(global_name)) != .dynamic) {2829 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(gsi)) != .dynamic) {
2878 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);2830 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);
2879 }2831 }
28802832
2881 // Finally, update the symbol value, re-applying target relocations. Also note that because we2833 // Finally, update the symbol value, re-applying target relocations. Also note that because we
2882 // possibly removed the PLT entry above, some relocations which were previously targeting the2834 // possibly removed the PLT entry above, some relocations which were previously targeting the
2883 // PLT will now instead target the symbol itself.2835 // PLT will now instead target the symbol itself.
2884 Symbol.Id.global(global_name).flushMoved(elf, new.value);2836 Symbol.Id.global(gsi).flushMoved(elf, new.value);
2885}2837}
2886/// When the same global symbol appears in two inputs---even if one symbol is defined and the other2838/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
2887/// undefined---their visibility values are combined to determine the resulting visibility, which2839/// undefined---their visibility values are combined to determine the resulting visibility, which
2888/// can also affect the bind of the symbol we output.2840/// can also affect the bind of the symbol we output.
2889fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visibility: std.elf.STV, bind: enum { strong, weak }) void {2841fn mergeGlobalSymbolVisibility(
2842 elf: *Elf,
2843 gsi: Symbol.Global.Index,
2844 other_visibility: std.elf.STV,
2845 bind: Symbol.Global.Bind,
2846) void {
2847 const global_ptr = gsi.ptr(elf);
2848
2890 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {2849 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
2891 inline else => |sym| elf.targetLoad(&sym.other).visibility,2850 inline else => |sym| elf.targetLoad(&sym.other).visibility,
2892 };2851 };
2893 // The combined visibility is essentially the "strictest" of the two, with most strict being2852
2894 // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.2853 // We will combine the old and new visibilities. The combined visibility is basically whichever
2895 const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {2854 // is "stricter", with most strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2896 .INTERNAL => .{ .INTERNAL, false },2855
2856 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
2857 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
2858 // a relocatable), then the symbol should have binding `STB_LOCAL` in the output. Therefore, if
2859 // we are putting the global in this state for the first time---let's call it "demoting" the
2860 // global to `STB_LOCAL`---we'll need to update its bind in the symtab.
2861 const Demote = enum { no, yes_already, yes_new };
2862 const new_visibility: std.elf.STV, const demote_if_module: Demote = switch (old_visibility) {
2863 .INTERNAL => .{ .INTERNAL, .yes_already },
2897 .HIDDEN => switch (other_visibility) {2864 .HIDDEN => switch (other_visibility) {
2898 .INTERNAL => .{ .INTERNAL, false },2865 .INTERNAL => .{ .INTERNAL, .yes_already },
2899 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },2866 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, .yes_already },
2900 },2867 },
2901 .PROTECTED => switch (other_visibility) {2868 .PROTECTED => switch (other_visibility) {
2902 .INTERNAL => .{ .INTERNAL, true },2869 .INTERNAL => .{ .INTERNAL, .yes_new },
2903 .HIDDEN => .{ .HIDDEN, true },2870 .HIDDEN => .{ .HIDDEN, .yes_new },
2904 .PROTECTED, .DEFAULT => .{ .PROTECTED, false },2871 .PROTECTED, .DEFAULT => .{ .PROTECTED, .no },
2905 },2872 },
2906 .DEFAULT => switch (other_visibility) {2873 .DEFAULT => switch (other_visibility) {
2907 .INTERNAL => .{ .INTERNAL, true },2874 .INTERNAL => .{ .INTERNAL, .yes_new },
2908 .HIDDEN => .{ .HIDDEN, true },2875 .HIDDEN => .{ .HIDDEN, .yes_new },
2909 .PROTECTED => .{ .PROTECTED, false },2876 .PROTECTED => .{ .PROTECTED, .no },
2910 .DEFAULT => .{ .DEFAULT, false },2877 .DEFAULT => .{ .DEFAULT, .no },
2878 },
2879 };
2880 const demote: Demote = switch (elf.ehdrType()) {
2881 .REL => .no, // nothing is demoted in relocatables
2882 .EXEC, .DYN => demote_if_module,
2883 };
2884 switch (demote) {
2885 .no => switch (elf.symPtr(global_ptr.symtab_index)) {
2886 inline else => |sym, class| {
2887 // Just update the symtab entry with the new visibility and binding.
2888 const old_info = elf.targetLoad(&sym.info);
2889 assert(old_info.bind != .LOCAL); // symbol should not be demoted
2890 const new_info: class.ElfN().Sym.Info = .{
2891 .type = old_info.type,
2892 .bind = switch (bind) {
2893 .strong => .GLOBAL,
2894 .weak => .WEAK,
2895 },
2896 };
2897 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2898 elf.targetStore(&sym.info, new_info);
2899
2900 // Also update dynsym if necessary.
2901 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| {
2902 const dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2903 assert(elf.targetLoad(&dynsym_ptr.info) == old_info);
2904 elf.targetStore(&dynsym_ptr.other, .{ .visibility = new_visibility });
2905 elf.targetStore(&dynsym_ptr.info, new_info);
2906 } else {
2907 assert(global_ptr.dynsym_index == .no_section); // symbol should not be demoted
2908 }
2909 },
2911 },2910 },
2912 };2911
2913 // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared2912 .yes_already => {
2914 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are2913 // Update the visibility in the symtab entry...
2915 // putting the global in this state for the first time---let's call it "demoting" the global to2914 switch (elf.symPtr(global_ptr.symtab_index)) {
2916 // STB_LOCAL---we need to update its bind in the symtab.2915 inline else => |sym| {
2917 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;2916 assert(elf.targetLoad(&sym.info).bind == .LOCAL); // symbol should be demoted
2918 switch (elf.symPtr(global_ptr.symtab_index)) {2917 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2919 inline else => |sym, class| {
2920 const old_info = elf.targetLoad(&sym.info);
2921 const new_info: class.ElfN().Sym.Info = .{
2922 .type = old_info.type,
2923 .bind = if (demote_to_local) b: {
2924 assert(old_info.bind != .LOCAL);
2925 break :b .LOCAL;
2926 } else if (old_info.bind == .LOCAL) .LOCAL else switch (bind) {
2927 .strong => .GLOBAL,
2928 .weak => .WEAK,
2929 },2918 },
2919 }
2920 // ...and update the binding tracked in `global_ptr.dynsym_index`.
2921 switch (global_ptr.dynsym_index) {
2922 .demoted_strong, .demoted_weak => {},
2923 .no_section => unreachable, // `.demoted_strong` or `.demoted_weak` should take priority
2924 _ => unreachable, // demoted symbol should not have a dynsym index
2925 }
2926 global_ptr.dynsym_index = switch (bind) {
2927 .strong => .demoted_strong,
2928 .weak => .demoted_weak,
2929 };
2930 },
2931
2932 .yes_new => {
2933 // Update the binding and visibility in the symtab, but don't bother with dynsym because
2934 // we're going to delete the dynsym entry in a moment.
2935 switch (elf.symPtr(global_ptr.symtab_index)) {
2936 inline else => |sym| {
2937 const old_info = elf.targetLoad(&sym.info);
2938 assert(old_info.bind != .LOCAL); // symbol should not be already demoted
2939 elf.targetStore(&sym.info, .{ .type = old_info.type, .bind = .LOCAL });
2940 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2941 },
2942 }
2943
2944 // If there was a dynsym entry, it is no longer necessary.
2945 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| {
2946 elf.deleteDynsym(dynsym_index);
2947 } else {
2948 assert(global_ptr.dynsym_index == .no_section); // symbol should not be already demoted
2949 }
2950
2951 // Instead, that field is repurposed to track the symbol's binding.
2952 global_ptr.dynsym_index = switch (bind) {
2953 .strong => .demoted_strong,
2954 .weak => .demoted_weak,
2930 };2955 };
2931 elf.targetStore(&sym.other, .{ .visibility = new_visibility });2956
2932 elf.targetStore(&sym.info, new_info);2957 // If there was a copy relocation for this symbol, it is now unnecessary. See equivalent
2933 // also update dynsym2958 // logic in `setGlobalSymbolValue` for an explanation of this case.
2934 if (global_ptr.dynsym_index != 0) {2959 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
2935 const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));2960 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2936 elf.targetStore(&dynsym.other, .{ .visibility = new_visibility });2961 // TODO: once we can delete `MappedFile` nodes, delete `copied_global_kv.value.node`
2937 elf.targetStore(&dynsym.info, new_info);2962 } else {
2963 _ = elf.want_copied_globals.swapRemove(gsi);
2938 }2964 }
2965
2966 // Finally, we need to update the global's symtab index so that it is adjacent to the
2967 // other `STB_LOCAL` symbols instead of the global symbols.
2968 elf.moveDemotedGlobal(gsi);
2939 },2969 },
2940 }2970 }
2941 if (demote_to_local) {
2942 // When demoting a global to STB_LOCAL, we need to move its symtab index so that it is with
2943 // the STB_LOCAL symbols instead of the global symbols.
2944 elf.moveDemotedGlobal(global_ptr);
2945 }
2946}2971}
2947/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),2972/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
2948/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF2973/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
2949/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.2974/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
2950fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {2975fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {
2951 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module2976 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
2977 const global_ptr = gsi.ptr(elf);
2952 switch (elf.shdrPtr(.symtab)) {2978 switch (elf.shdrPtr(.symtab)) {
2953 inline else => |shdr, class| {2979 inline else => |shdr, class| {
2954 // `shdr.info` stores the index of the first global symbol. We are going to swap the2980 // `shdr.info` stores the index of the first global symbol. We are going to swap the
...@@ -2969,12 +2995,9 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2969,12 +2995,9 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2969 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));2995 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2970 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));2996 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
29712997
2972 const this_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&src_sym_ptr.name));
2973 assert(elf.globalByName(this_name).? == global_ptr);
2974
2975 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));2998 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));
2976 const other_global_ptr = elf.globalByName(other_name).?;2999 const other_gsi = elf.globalByName(other_name.slice(elf)).?;
2977 assert(other_global_ptr.symtab_index == dest_index);3000 assert(other_gsi.ptr(elf).symtab_index == dest_index);
29783001
2979 // First swap the symtab entries...3002 // First swap the symtab entries...
2980 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);3003 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
...@@ -2982,55 +3005,59 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2982,55 +3005,59 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2982 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));3005 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2983 // ...then update the `elf.globals` tracking.3006 // ...then update the `elf.globals` tracking.
2984 global_ptr.symtab_index = dest_index;3007 global_ptr.symtab_index = dest_index;
2985 other_global_ptr.symtab_index = src_index;3008 other_gsi.ptr(elf).symtab_index = src_index;
2986 }3009 }
3010 },
3011 }
3012}
3013/// Asserts that the `.dynsym` section exists, and removes the entry at index `dynsym_index`,
3014/// swapping another entry into its place to keep the section compact. Also updates the `.hash`
3015/// section, and updates `Symbol.Global.dynsym_index` for whichever global gets swapped into
3016/// `dynsym_index`. However, this does *not* in any way modify the `Symbol.Global` corresponding to
3017/// the old symbol at `dynsym_index`.
3018///
3019/// Asserts that there is at least 1 unused capacity in `elf.changed_symtab_index`.
3020fn deleteDynsym(elf: *Elf, dynsym_index: u32) void {
3021 // To keep dynsym compact, we'll swap another symbol into `dynsym_index`.
3022 switch (elf.shdrPtr(elf.shndx.dynsym)) {
3023 inline else => |dynsym_shdr, class| {
3024 const ent_size = @sizeOf(class.ElfN().Sym);
3025 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
29873026
2988 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,3027 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2989 // we'll move another symbol into its place just like we did above.3028 const old_size = elf.targetLoad(&dynsym_shdr.size);
2990 if (global_ptr.dynsym_index != 0) {3029 const new_size = old_size - ent_size;
2991 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));3030 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2992
2993 const ent_size = @sizeOf(class.ElfN().Sym);
2994 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2995
2996 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2997 const old_size = elf.targetLoad(&dynsym_shdr.size);
2998 const new_size = old_size - ent_size;
2999 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
3000
3001 elf.popDynsymHashEntry(remove_dynsym_index);
3002
3003 const free_dynsym_index = global_ptr.dynsym_index;
3004 global_ptr.dynsym_index = 0;
30053031
3006 if (free_dynsym_index != remove_dynsym_index) {3032 elf.popDynsymHashEntry(remove_dynsym_index);
3007 // The demoted global wasn't the last entry, so move whatever entry we just
3008 // truncated out of dynsym into its place.
30093033
3010 elf.clearDynsymHashEntry(free_dynsym_index);3034 if (dynsym_index != remove_dynsym_index) {
3035 // The demoted global wasn't the last entry, so move whatever entry we just
3036 // truncated out of dynsym into its place.
30113037
3012 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));3038 elf.clearDynsymHashEntry(dynsym_index);
3013 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
30143039
3015 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));3040 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
3016 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));3041 const dest_dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
3017 const moved_global_ptr = elf.globalByName(moved_name).?;
30183042
3019 dest_dynsym_ptr.* = src_dynsym_ptr.*;3043 // MLUGG TODO: this depends on symbol names matching between dynsym and symtab
3044 const moved_name: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
3045 const moved_gsi = elf.globalByName(moved_name.slice(elf)).?;
30203046
3021 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);3047 dest_dynsym_ptr.* = src_dynsym_ptr.*;
3022 moved_global_ptr.dynsym_index = free_dynsym_index;
30233048
3024 elf.populateDynsymHashEntry(free_dynsym_index);3049 assert(moved_gsi.ptr(elf).dynsym_index.unwrap().? == remove_dynsym_index);
3050 moved_gsi.ptr(elf).dynsym_index = .wrap(dynsym_index);
30253051
3026 // Since that symbol's dynsym index has changed, we'll have to update any3052 elf.populateDynsymHashEntry(dynsym_index);
3027 // relocation entries targeting it.
3028 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
3029 }
30303053
3031 // Now that we've given that symbol a new home, actually decrease the section size.3054 // Since that symbol's dynsym index has changed, we'll have to update any
3032 elf.targetStore(&dynsym_shdr.size, new_size);3055 // relocation entries targeting it.
3056 elf.changed_symtab_index.putAssumeCapacity(moved_gsi, {});
3033 }3057 }
3058
3059 // Now that we've given that symbol a new home, actually decrease the section size.
3060 elf.targetStore(&dynsym_shdr.size, new_size);
3034 },3061 },
3035 }3062 }
3036}3063}
...@@ -3049,18 +3076,102 @@ const Symbol = struct {...@@ -3049,18 +3076,102 @@ const Symbol = struct {
3049 const Global = struct {3076 const Global = struct {
3050 /// The current index of the symtab entry for this global symbol.3077 /// The current index of the symtab entry for this global symbol.
3051 symtab_index: Symbol.Index,3078 symtab_index: Symbol.Index,
3052 /// The current index of the dynsym entry for this global symbol. If the global has been3079 /// As well as the index of this global symbol's `.dynsym` entry, in the event that the
3053 /// demoted to STB_LOCAL, it does not have a dynsym entry and this field is set to 0.3080 /// symbol is *not* in `.dynsym`, we store a special value here indicating why that is.
3054 dynsym_index: u32,3081 dynsym_index: enum(u32) {
3082 /// In theory, this global should have a dynamic symbol index (it is not `STV_HIDDEN`),
3083 /// but there is no `.dynsym` section, so it does not. This can happens when we are
3084 /// emitting either a relocatable or a static executable.
3085 no_section = 0,
3086 /// This global has no dynamic symbol index because it has visibility `STV_HIDDEN`.
3087 ///
3088 /// Due to this `STV_HIDDEN` visibility, we have necessarily "demoted" the symtab entry
3089 /// to `STB_LOCAL` (see logic in `mergeGlobalSymbolVisibility`), but its actual binding
3090 /// is `STB_GLOBAL`.
3091 demoted_strong = std.math.maxInt(u32),
3092 /// Like `.demoted_global`, except indicates that the actual symbol binding is
3093 /// `STB_WEAK`, not `STB_GLOBAL`.
3094 demoted_weak = std.math.maxInt(u32) - 1,
3095 /// An actual index into the `.dynsym` section.
3096 _,
3097
3098 /// If this symbol is actually in `.dynsym`, returns the index as a raw `u32`. This
3099 /// index is always non-zero because index 0 holds the special "null" symbol.
3100 ///
3101 /// Otherwise, returns `null`.
3102 fn unwrap(dsi: @This()) ?u32 {
3103 return switch (dsi) {
3104 .no_section, .demoted_strong, .demoted_weak => null,
3105 _ => @backingInt(dsi),
3106 };
3107 }
3108
3109 /// Takes an index into `.dynsym` as a raw `u32`, and converts it to this type.
3110 ///
3111 /// Asserts that `index` is non-zero because index 0 in `.dynsym` is the "null" symbol.
3112 fn wrap(index: u32) @This() {
3113 assert(index != 0);
3114 return @fromBackingInt(index);
3115 }
3116 },
30553117
3056 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.3118 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
3057 ///3119 next_in_node: Symbol.Global.Index.Optional,
3058 /// If `node` is `.none`, this is `.empty`.
3059 next_in_node: String(.strtab),
3060 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.3120 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
3061 ///3121 prev_in_node: Symbol.Global.Index.Optional,
3062 /// If `node` is `.none`, this is `.empty`.3122
3063 prev_in_node: String(.strtab),3123 const NameAdapter = struct {
3124 elf: *Elf,
3125 pub fn eql(ctx: NameAdapter, lhs_slice: []const u8, _: void, rhs_index: usize) bool {
3126 const elf = ctx.elf;
3127 const rhs_gsi: Symbol.Global.Index = @fromBackingInt(@intCast(rhs_index));
3128 const rhs_slice = Symbol.Id.global(rhs_gsi).name(elf).slice(elf);
3129 return std.mem.eql(u8, lhs_slice, rhs_slice);
3130 }
3131 pub fn hash(ctx: NameAdapter, slice: []const u8) u32 {
3132 _ = ctx;
3133 return std.array_hash_map.hashString(slice);
3134 }
3135 };
3136
3137 const Index = enum(u32) {
3138 _,
3139
3140 fn ptr(gsi: Symbol.Global.Index, elf: *Elf) *Global {
3141 return &elf.globals.items[@backingInt(gsi)];
3142 }
3143
3144 fn bind(gsi: Symbol.Global.Index, elf: *Elf) Symbol.Global.Bind {
3145 return switch (gsi.ptr(elf).dynsym_index) {
3146 .demoted_strong => .strong,
3147 .demoted_weak => .weak,
3148 .no_section, _ => switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3149 inline else => |sym| switch (elf.targetLoad(&sym.info).bind) {
3150 .GLOBAL => .strong,
3151 .WEAK => .weak,
3152 else => unreachable,
3153 },
3154 },
3155 };
3156 }
3157
3158 const Optional = enum(u32) {
3159 none = std.math.maxInt(u32),
3160 _,
3161
3162 fn wrap(gsi: Symbol.Global.Index) Symbol.Global.Index.Optional {
3163 return @bitCast(gsi);
3164 }
3165 fn unwrap(ogsi: Symbol.Global.Index.Optional) ?Symbol.Global.Index {
3166 return switch (ogsi) {
3167 .none => null,
3168 _ => @fromBackingInt(@backingInt(ogsi)),
3169 };
3170 }
3171 };
3172 };
3173
3174 const Bind = enum { strong, weak };
3064 };3175 };
30653176
3066 /// An index directly into the symtab. These values are not stable (global symbols are sometimes3177 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
...@@ -3107,12 +3218,12 @@ const Symbol = struct {...@@ -3107,12 +3218,12 @@ const Symbol = struct {
3107 fn local(lsi: Symbol.LocalIndex) Symbol.Id {3218 fn local(lsi: Symbol.LocalIndex) Symbol.Id {
3108 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };3219 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };
3109 }3220 }
3110 fn global(name: String(.strtab)) Symbol.Id {3221 fn global(gsi: Symbol.Global.Index) Symbol.Id {
3111 return .{ .kind = .global, .raw = @intCast(@backingInt(name)) };3222 return .{ .kind = .global, .raw = @intCast(@backingInt(gsi)) };
3112 }3223 }
3113 fn unwrap(s: Symbol.Id) union(enum) {3224 fn unwrap(s: Symbol.Id) union(enum) {
3114 local: Symbol.LocalIndex,3225 local: Symbol.LocalIndex,
3115 global: String(.strtab),3226 global: Symbol.Global.Index,
3116 } {3227 } {
3117 return switch (s.kind) {3228 return switch (s.kind) {
3118 .local => .{ .local = @fromBackingInt(s.raw) },3229 .local => .{ .local = @fromBackingInt(s.raw) },
...@@ -3127,10 +3238,10 @@ const Symbol = struct {...@@ -3127,10 +3238,10 @@ const Symbol = struct {
3127 return @bitCast(s);3238 return @bitCast(s);
3128 }3239 }
31293240
3130 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {3241 fn index(s: Symbol.Id, elf: *Elf) Symbol.Index {
3131 return switch (s.unwrap()) {3242 return switch (s.unwrap()) {
3132 .local => |lsi| lsi.index(),3243 .local => |lsi| lsi.index(),
3133 .global => |name| elf.globalByName(name).?.symtab_index,3244 .global => |gsi| gsi.ptr(elf).symtab_index,
3134 };3245 };
3135 }3246 }
31363247
...@@ -3143,6 +3254,18 @@ const Symbol = struct {...@@ -3143,6 +3254,18 @@ const Symbol = struct {
3143 };3254 };
3144 }3255 }
31453256
3257 fn name(s: Symbol.Id, elf: *Elf) String(.strtab) {
3258 return switch (elf.symPtr(s.index(elf))) {
3259 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
3260 };
3261 }
3262
3263 fn defined(s: Symbol.Id, elf: *Elf) bool {
3264 return switch (elf.symPtr(s.index(elf))) {
3265 inline else => |sym| elf.targetLoad(&sym.shndx) != std.elf.SHN_UNDEF,
3266 };
3267 }
3268
3146 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {3269 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
3147 // Update the symbol value in `.symtab`3270 // Update the symbol value in `.symtab`
3148 const sym_index = sym_id.index(elf);3271 const sym_index = sym_id.index(elf);
...@@ -3153,10 +3276,9 @@ const Symbol = struct {...@@ -3153,10 +3276,9 @@ const Symbol = struct {
3153 // Update the symbol value in `.dynsym` if applicable3276 // Update the symbol value in `.dynsym` if applicable
3154 switch (sym_id.unwrap()) {3277 switch (sym_id.unwrap()) {
3155 .local => {},3278 .local => {},
3156 .global => |name| {3279 .global => |gsi| {
3157 const g = elf.globalByName(name).?;3280 if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {
3158 if (g.dynsym_index != 0) {3281 switch (elf.dynsymPtr(dynsym_index)) {
3159 switch (elf.dynsymPtr(g.dynsym_index)) {
3160 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),3282 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3161 }3283 }
3162 }3284 }
...@@ -3255,7 +3377,7 @@ const Symbol = struct {...@@ -3255,7 +3377,7 @@ const Symbol = struct {
3255 }3377 }
3256 switch (s.unwrap()) {3378 switch (s.unwrap()) {
3257 .local => {},3379 .local => {},
3258 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {3380 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {
3259 return copied_global.node.hasMoved(&elf.mf);3381 return copied_global.node.hasMoved(&elf.mf);
3260 },3382 },
3261 }3383 }
...@@ -3264,12 +3386,10 @@ const Symbol = struct {...@@ -3264,12 +3386,10 @@ const Symbol = struct {
3264 };3386 };
3265};3387};
32663388
3267fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {3389fn globalByName(elf: *Elf, name: []const u8) ?Symbol.Global.Index {
3268 if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;3390 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };
3269 if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;3391 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;
3270 if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;3392 return @fromBackingInt(@intCast(index_raw));
3271 if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;
3272 return null;
3273}3393}
32743394
3275fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {3395fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
...@@ -3307,11 +3427,11 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -3307,11 +3427,11 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3307 assert(shndx != .UNDEF);3427 assert(shndx != .UNDEF);
3308 assert(visibility == .DEFAULT);3428 assert(visibility == .DEFAULT);
3309 },3429 },
3310 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {3430 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
3311 // An unprotected symbol in a DSO which is not an executable is subject to runtime3431 // An unprotected symbol in a DSO which is not an executable is subject to runtime
3312 // preemption, so a dynamic relocation is required for it even if we have a definition.3432 // preemption, so a dynamic relocation is required for it even if we have a definition.
3313 return .dynamic;3433 return .dynamic;
3314 } else if (elf.copied_globals.contains(name)) {3434 } else if (elf.copied_globals.contains(gsi)) {
3315 // This becomes a locally-defined symbol in `.data`.3435 // This becomes a locally-defined symbol in `.data`.
3316 return if (runtime_load_addr) .static_relative else .static;3436 return if (runtime_load_addr) .static_relative else .static;
3317 },3437 },
...@@ -3449,9 +3569,9 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb...@@ -3449,9 +3569,9 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb
3449}3569}
3450fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {3570fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3451 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);3571 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
3452 const symbol = elf.addGlobalSymbolAssumeCapacity(.{3572 const gsi = elf.addGlobalSymbolAssumeCapacity(.{
3453 .node = .none,3573 .node = .none,
3454 .name = try .string(elf, opts.name),3574 .name = opts.name,
3455 .lib_name = opts.lib_name,3575 .lib_name = opts.lib_name,
3456 .value = 0,3576 .value = 0,
3457 .size = 0,3577 .size = 0,
...@@ -3470,8 +3590,9 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {...@@ -3470,8 +3590,9 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3470 .shndx = .UNDEF,3590 .shndx = .UNDEF,
3471 }) catch |err| switch (err) {3591 }) catch |err| switch (err) {
3472 error.MultipleDefinitions => unreachable, // shndx is undef3592 error.MultipleDefinitions => unreachable, // shndx is undef
3593 else => |e| return e,
3473 };3594 };
3474 return symbol;3595 return .global(gsi);
3475}3596}
3476pub fn addReloc(3597pub fn addReloc(
3477 elf: *Elf,3598 elf: *Elf,
...@@ -3836,12 +3957,9 @@ fn create(...@@ -3836,12 +3957,9 @@ fn create(
3836 .soname = .empty,3957 .soname = .empty,
3837 },3958 },
3838 .symtab = .empty,3959 .symtab = .empty,
3839 .globals = .{3960 .globals = .empty,
3840 .strong_def = .empty,3961 .globals_by_name = .empty,
3841 .weak_def = .empty,3962 .unknown_globals = .empty,
3842 .strong_undef = .empty,
3843 .weak_undef = .empty,
3844 },
3845 .copied_globals = .empty,3963 .copied_globals = .empty,
3846 .want_copied_globals = .empty,3964 .want_copied_globals = .empty,
3847 .node_global_symbols = .empty,3965 .node_global_symbols = .empty,
...@@ -3915,10 +4033,9 @@ pub fn deinit(elf: *Elf) void {...@@ -3915,10 +4033,9 @@ pub fn deinit(elf: *Elf) void {
3915 elf.shdrs.deinit(gpa);4033 elf.shdrs.deinit(gpa);
3916 elf.phdrs.deinit(gpa);4034 elf.phdrs.deinit(gpa);
3917 elf.symtab.deinit(gpa);4035 elf.symtab.deinit(gpa);
3918 elf.globals.strong_def.deinit(gpa);4036 elf.globals.deinit(gpa);
3919 elf.globals.weak_def.deinit(gpa);4037 elf.globals_by_name.deinit(gpa);
3920 elf.globals.strong_undef.deinit(gpa);4038 elf.unknown_globals.deinit(gpa);
3921 elf.globals.weak_undef.deinit(gpa);
3922 elf.copied_globals.deinit(gpa);4039 elf.copied_globals.deinit(gpa);
3923 elf.want_copied_globals.deinit(gpa);4040 elf.want_copied_globals.deinit(gpa);
3924 elf.node_global_symbols.deinit(gpa);4041 elf.node_global_symbols.deinit(gpa);
...@@ -4978,7 +5095,7 @@ fn initHeaders(...@@ -4978,7 +5095,7 @@ fn initHeaders(
4978 // Despite the name, `__dso_handle` is necessary even in static binaries.5095 // Despite the name, `__dso_handle` is necessary even in static binaries.
4979 _ = elf.addGlobalSymbolAssumeCapacity(.{5096 _ = elf.addGlobalSymbolAssumeCapacity(.{
4980 .node = .wrap(Section.Index.text.get(elf).ni),5097 .node = .wrap(Section.Index.text.get(elf).ni),
4981 .name = try .string(elf, "__dso_handle"),5098 .name = "__dso_handle",
4982 .value = Section.Index.text.vaddr(elf),5099 .value = Section.Index.text.vaddr(elf),
4983 .size = 0,5100 .size = 0,
4984 .type = .NOTYPE,5101 .type = .NOTYPE,
...@@ -4987,10 +5104,11 @@ fn initHeaders(...@@ -4987,10 +5104,11 @@ fn initHeaders(
4987 .shndx = .text,5104 .shndx = .text,
4988 }) catch |err| switch (err) {5105 }) catch |err| switch (err) {
4989 error.MultipleDefinitions => unreachable, // no inputs are processed yet5106 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5107 else => |e| return e,
4990 };5108 };
4991 _ = elf.addGlobalSymbolAssumeCapacity(.{5109 _ = elf.addGlobalSymbolAssumeCapacity(.{
4992 .node = .wrap(elf.shndx.plt.get(elf).ni),5110 .node = .wrap(elf.shndx.plt.get(elf).ni),
4993 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),5111 .name = "_PROCEDURE_LINKAGE_TABLE_",
4994 .value = elf.shndx.plt.vaddr(elf),5112 .value = elf.shndx.plt.vaddr(elf),
4995 .size = 0,5113 .size = 0,
4996 .type = .NOTYPE,5114 .type = .NOTYPE,
...@@ -4999,10 +5117,11 @@ fn initHeaders(...@@ -4999,10 +5117,11 @@ fn initHeaders(
4999 .shndx = elf.shndx.plt,5117 .shndx = elf.shndx.plt,
5000 }) catch |err| switch (err) {5118 }) catch |err| switch (err) {
5001 error.MultipleDefinitions => unreachable, // no inputs are processed yet5119 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5120 else => |e| return e,
5002 };5121 };
5003 _ = elf.addGlobalSymbolAssumeCapacity(.{5122 _ = elf.addGlobalSymbolAssumeCapacity(.{
5004 .node = .wrap(elf.shndx.got.get(elf).ni),5123 .node = .wrap(elf.shndx.got.get(elf).ni),
5005 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),5124 .name = "_GLOBAL_OFFSET_TABLE_",
5006 .value = switch (machine) {5125 .value = switch (machine) {
5007 .AARCH64,5126 .AARCH64,
5008 .LOONGARCH,5127 .LOONGARCH,
...@@ -5023,10 +5142,11 @@ fn initHeaders(...@@ -5023,10 +5142,11 @@ fn initHeaders(
5023 .shndx = elf.shndx.got,5142 .shndx = elf.shndx.got,
5024 }) catch |err| switch (err) {5143 }) catch |err| switch (err) {
5025 error.MultipleDefinitions => unreachable, // no inputs are processed yet5144 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5145 else => |e| return e,
5026 };5146 };
5027 _ = elf.addGlobalSymbolAssumeCapacity(.{5147 _ = elf.addGlobalSymbolAssumeCapacity(.{
5028 .node = .none,5148 .node = .none,
5029 .name = try .string(elf, "__init_array_start"),5149 .name = "__init_array_start",
5030 .value = 0,5150 .value = 0,
5031 .size = 0,5151 .size = 0,
5032 .type = .NOTYPE,5152 .type = .NOTYPE,
...@@ -5035,10 +5155,11 @@ fn initHeaders(...@@ -5035,10 +5155,11 @@ fn initHeaders(
5035 .shndx = .ABS,5155 .shndx = .ABS,
5036 }) catch |err| switch (err) {5156 }) catch |err| switch (err) {
5037 error.MultipleDefinitions => unreachable, // no inputs are processed yet5157 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5158 else => |e| return e,
5038 };5159 };
5039 _ = elf.addGlobalSymbolAssumeCapacity(.{5160 _ = elf.addGlobalSymbolAssumeCapacity(.{
5040 .node = .none,5161 .node = .none,
5041 .name = try .string(elf, "__init_array_end"),5162 .name = "__init_array_end",
5042 .value = 0,5163 .value = 0,
5043 .size = 0,5164 .size = 0,
5044 .type = .NOTYPE,5165 .type = .NOTYPE,
...@@ -5047,10 +5168,11 @@ fn initHeaders(...@@ -5047,10 +5168,11 @@ fn initHeaders(
5047 .shndx = .ABS,5168 .shndx = .ABS,
5048 }) catch |err| switch (err) {5169 }) catch |err| switch (err) {
5049 error.MultipleDefinitions => unreachable, // no inputs are processed yet5170 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5171 else => |e| return e,
5050 };5172 };
5051 _ = elf.addGlobalSymbolAssumeCapacity(.{5173 _ = elf.addGlobalSymbolAssumeCapacity(.{
5052 .node = .none,5174 .node = .none,
5053 .name = try .string(elf, "__fini_array_start"),5175 .name = "__fini_array_start",
5054 .value = 0,5176 .value = 0,
5055 .size = 0,5177 .size = 0,
5056 .type = .NOTYPE,5178 .type = .NOTYPE,
...@@ -5059,10 +5181,11 @@ fn initHeaders(...@@ -5059,10 +5181,11 @@ fn initHeaders(
5059 .shndx = .ABS,5181 .shndx = .ABS,
5060 }) catch |err| switch (err) {5182 }) catch |err| switch (err) {
5061 error.MultipleDefinitions => unreachable, // no inputs are processed yet5183 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5184 else => |e| return e,
5062 };5185 };
5063 _ = elf.addGlobalSymbolAssumeCapacity(.{5186 _ = elf.addGlobalSymbolAssumeCapacity(.{
5064 .node = .none,5187 .node = .none,
5065 .name = try .string(elf, "__fini_array_end"),5188 .name = "__fini_array_end",
5066 .value = 0,5189 .value = 0,
5067 .size = 0,5190 .size = 0,
5068 .type = .NOTYPE,5191 .type = .NOTYPE,
...@@ -5071,10 +5194,11 @@ fn initHeaders(...@@ -5071,10 +5194,11 @@ fn initHeaders(
5071 .shndx = .ABS,5194 .shndx = .ABS,
5072 }) catch |err| switch (err) {5195 }) catch |err| switch (err) {
5073 error.MultipleDefinitions => unreachable, // no inputs are processed yet5196 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5197 else => |e| return e,
5074 };5198 };
5075 _ = elf.addGlobalSymbolAssumeCapacity(.{5199 _ = elf.addGlobalSymbolAssumeCapacity(.{
5076 .node = .none,5200 .node = .none,
5077 .name = try .string(elf, "__preinit_array_start"),5201 .name = "__preinit_array_start",
5078 .value = 0,5202 .value = 0,
5079 .size = 0,5203 .size = 0,
5080 .type = .NOTYPE,5204 .type = .NOTYPE,
...@@ -5083,10 +5207,11 @@ fn initHeaders(...@@ -5083,10 +5207,11 @@ fn initHeaders(
5083 .shndx = .ABS,5207 .shndx = .ABS,
5084 }) catch |err| switch (err) {5208 }) catch |err| switch (err) {
5085 error.MultipleDefinitions => unreachable, // no inputs are processed yet5209 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5210 else => |e| return e,
5086 };5211 };
5087 _ = elf.addGlobalSymbolAssumeCapacity(.{5212 _ = elf.addGlobalSymbolAssumeCapacity(.{
5088 .node = .none,5213 .node = .none,
5089 .name = try .string(elf, "__preinit_array_end"),5214 .name = "__preinit_array_end",
5090 .value = 0,5215 .value = 0,
5091 .size = 0,5216 .size = 0,
5092 .type = .NOTYPE,5217 .type = .NOTYPE,
...@@ -5095,11 +5220,12 @@ fn initHeaders(...@@ -5095,11 +5220,12 @@ fn initHeaders(
5095 .shndx = .ABS,5220 .shndx = .ABS,
5096 }) catch |err| switch (err) {5221 }) catch |err| switch (err) {
5097 error.MultipleDefinitions => unreachable, // no inputs are processed yet5222 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5223 else => |e| return e,
5098 };5224 };
5099 if (have_dynamic) {5225 if (have_dynamic) {
5100 _ = elf.addGlobalSymbolAssumeCapacity(.{5226 _ = elf.addGlobalSymbolAssumeCapacity(.{
5101 .node = .wrap(elf.shndx.dynamic.get(elf).ni),5227 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
5102 .name = try .string(elf, "_DYNAMIC"),5228 .name = "_DYNAMIC",
5103 .value = elf.shndx.dynamic.vaddr(elf),5229 .value = elf.shndx.dynamic.vaddr(elf),
5104 .size = 0,5230 .size = 0,
5105 .type = .NOTYPE,5231 .type = .NOTYPE,
...@@ -5108,6 +5234,7 @@ fn initHeaders(...@@ -5108,6 +5234,7 @@ fn initHeaders(
5108 .shndx = elf.shndx.dynamic,5234 .shndx = elf.shndx.dynamic,
5109 }) catch |err| switch (err) {5235 }) catch |err| switch (err) {
5110 error.MultipleDefinitions => unreachable, // no inputs are processed yet5236 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5237 else => |e| return e,
5111 };5238 };
5112 }5239 }
5113 } else {5240 } else {
...@@ -6665,9 +6792,9 @@ fn loadObject(...@@ -6665,9 +6792,9 @@ fn loadObject(
6665 ),6792 ),
6666 .LOCAL => continue,6793 .LOCAL => continue,
6667 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {6794 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6668 si.* = elf.addGlobalSymbolAssumeCapacity(.{6795 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{
6669 .node = .none,6796 .node = .none,
6670 .name = try .string(elf, name),6797 .name = name,
6671 .value = input_sym.value,6798 .value = input_sym.value,
6672 .size = input_sym.size,6799 .size = input_sym.size,
6673 .type = sym_type,6800 .type = sym_type,
...@@ -6680,7 +6807,8 @@ fn loadObject(...@@ -6680,7 +6807,8 @@ fn loadObject(
6680 .shndx = .UNDEF,6807 .shndx = .UNDEF,
6681 }) catch |err| switch (err) {6808 }) catch |err| switch (err) {
6682 error.MultipleDefinitions => unreachable, // shndx is .UNDEF6809 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
6683 };6810 else => |e| return e,
6811 });
6684 continue;6812 continue;
6685 },6813 },
6686 };6814 };
...@@ -6705,9 +6833,9 @@ fn loadObject(...@@ -6705,9 +6833,9 @@ fn loadObject(
6705 si.* = .local(lsi);6833 si.* = .local(lsi);
6706 },6834 },
6707 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {6835 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6708 si.* = elf.addGlobalSymbolAssumeCapacity(.{6836 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{
6709 .node = .wrap(input_section_node),6837 .node = .wrap(input_section_node),
6710 .name = try .string(elf, name),6838 .name = name,
6711 .value = input_sym.value,6839 .value = input_sym.value,
6712 .size = input_sym.size,6840 .size = input_sym.size,
6713 .type = sym_type,6841 .type = sym_type,
...@@ -6724,7 +6852,8 @@ fn loadObject(...@@ -6724,7 +6852,8 @@ fn loadObject(
6724 "multiple definitions of '{s}'",6852 "multiple definitions of '{s}'",
6725 .{name},6853 .{name},
6726 ),6854 ),
6727 };6855 else => |e| return e,
6856 });
6728 },6857 },
6729 }6858 }
6730 }6859 }
...@@ -7002,9 +7131,13 @@ fn loadDso(...@@ -7002,9 +7131,13 @@ fn loadDso(
7002 {7131 {
7003 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);7132 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
7004 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);7133 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);
7005 if (elf.copied_globals.get(name)) |copied_global| {7134
7006 // We have a copy relocation for this global, but the amount of space we7135 // If there is already a copy relocation for this symbol, the amount of
7007 // reserved for it could be too small or underaligned!7136 // space reserved for it might be too small or underaligned, so update it:
7137 update: {
7138 const gsi = elf.globalByName(name.slice(elf)) orelse break :update;
7139 const copied_global = elf.copied_globals.get(gsi) orelse break :update;
7140
7008 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);7141 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
7009 try copied_global.node.resizeLeaf(7142 try copied_global.node.resizeLeaf(
7010 gpa,7143 gpa,
...@@ -7012,11 +7145,10 @@ fn loadDso(...@@ -7012,11 +7145,10 @@ fn loadDso(
7012 gop.value_ptr.alignment.forward(gop.value_ptr.size),7145 gop.value_ptr.alignment.forward(gop.value_ptr.size),
7013 );7146 );
7014 try copied_global.node.realign(gpa, &elf.mf, gop.value_ptr.alignment);7147 try copied_global.node.realign(gpa, &elf.mf, gop.value_ptr.alignment);
7015 const global_ptr = elf.globalByName(name).?;7148 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
7016 switch (elf.symPtr(global_ptr.symtab_index)) {
7017 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),7149 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
7018 }7150 }
7019 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {7151 switch (elf.dynsymPtr(gsi.ptr(elf).dynsym_index.unwrap().?)) {
7020 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),7152 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
7021 }7153 }
7022 }7154 }
...@@ -7032,19 +7164,20 @@ fn loadDso(...@@ -7032,19 +7164,20 @@ fn loadDso(
70327164
7033 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate7165 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
7034 // its type now.7166 // its type now.
7035 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse7167 const gsi = elf.globalByName(name.slice(elf)) orelse continue;
7036 elf.globals.weak_undef.getPtr(name) orelse7168 if (Symbol.Id.global(gsi).defined(elf)) continue;
7037 continue;
70387169
7039 if (global_ptr.dynsym_index == 0) continue;7170 _ = elf.unknown_globals.swapRemove(gsi);
70407171
7041 if (elf.want_copied_globals.swapRemove(name)) {7172 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap() orelse continue;
7173
7174 if (elf.want_copied_globals.swapRemove(gsi)) {
7042 // We just found a DSO definition of a symbol for which we wanted a copy7175 // We just found a DSO definition of a symbol for which we wanted a copy
7043 // relocation, so add one if we can!7176 // relocation, so add one if we can!
7044 _ = try elf.maybeAddCopyRelocation(name);7177 _ = try elf.maybeAddCopyRelocation(gsi);
7045 }7178 }
70467179
7047 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));7180 const sym_ptr = @field(elf.symPtr(gsi.ptr(elf).symtab_index), @tagName(class));
7048 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`7181 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
70497182
7050 switch (elf.targetLoad(&sym_ptr.other).visibility) {7183 switch (elf.targetLoad(&sym_ptr.other).visibility) {
...@@ -7064,7 +7197,7 @@ fn loadDso(...@@ -7064,7 +7197,7 @@ fn loadDso(
7064 .type = new_type,7197 .type = new_type,
7065 });7198 });
70667199
7067 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));7200 const dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
7068 elf.targetStore(&dynsym_ptr.info, .{7201 elf.targetStore(&dynsym_ptr.info, .{
7069 .bind = elf.targetLoad(&dynsym_ptr.info).bind,7202 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
7070 .type = new_type,7203 .type = new_type,
...@@ -7072,10 +7205,10 @@ fn loadDso(...@@ -7072,10 +7205,10 @@ fn loadDso(
70727205
7073 if (new_type == .FUNC) {7206 if (new_type == .FUNC) {
7074 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...7207 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
7075 elf.addPltEntry(name, global_ptr.dynsym_index);7208 elf.addPltEntry(gsi);
7076 // ...and therefore, we need to re-apply that symbol's relocations, as7209 // ...and therefore, we need to re-apply that symbol's relocations, as
7077 // some might be targeting its PLT entry.7210 // some might be targeting its PLT entry.
7078 Symbol.Id.global(name).applyTargetRelocs(elf);7211 Symbol.Id.global(gsi).applyTargetRelocs(elf);
7079 }7212 }
7080 }7213 }
7081 }7214 }
...@@ -7177,18 +7310,16 @@ fn createInitFiniArraySection(...@@ -7177,18 +7310,16 @@ fn createInitFiniArraySection(
7177 });7310 });
7178 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});7311 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
7179 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);7312 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
7180 // These symbols definitely already have strong definitions, because we added them alongside the7313 // These symbols definitely already exist with strong definitions, because we added them
7181 // other linker-defined symbols, all the way back in `initHeaders`.7314 // alongside the other linker-defined symbols, all the way back in `initHeaders`.
7182 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");7315 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_start").?, .{
7183 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");
7184 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{
7185 .node = .wrap(shndx.get(elf).ni),7316 .node = .wrap(shndx.get(elf).ni),
7186 .value = shndx.vaddr(elf),7317 .value = shndx.vaddr(elf),
7187 .size = 0,7318 .size = 0,
7188 .type = .NOTYPE,7319 .type = .NOTYPE,
7189 .shndx = shndx.*,7320 .shndx = shndx.*,
7190 });7321 });
7191 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{7322 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_end").?, .{
7192 .node = .wrap(shndx.get(elf).ni),7323 .node = .wrap(shndx.get(elf).ni),
7193 .value = shndx.vaddr(elf),7324 .value = shndx.vaddr(elf),
7194 .size = 0,7325 .size = 0,
...@@ -7204,8 +7335,8 @@ fn updateInitFiniArraySectionSize(...@@ -7204,8 +7335,8 @@ fn updateInitFiniArraySectionSize(
7204 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {7335 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
7205 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),7336 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
7206 };7337 };
7207 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");7338 const end_sym_gsi = elf.globalByName("__" ++ name ++ "_end").?;
7208 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);7339 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);
7209}7340}
72107341
7211pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {7342pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
...@@ -8265,7 +8396,7 @@ fn addSymbolRelocAssumeCapacity(...@@ -8265,7 +8396,7 @@ fn addSymbolRelocAssumeCapacity(
8265 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{8396 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8266 .type = dynamic_reloc_type,8397 .type = dynamic_reloc_type,
8267 .offset = node_vaddr + offset,8398 .offset = node_vaddr + offset,
8268 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,8399 .raw_sym_index = target.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
8269 .addend = addend,8400 .addend = addend,
8270 }).toOptional();8401 }).toOptional();
8271 },8402 },
...@@ -8508,21 +8639,25 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8508,21 +8639,25 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8508 const sym_value = sym_id.value(elf);8639 const sym_value = sym_id.value(elf);
8509 break :val .{ .signed = @bitCast(sym_value -% tls_size) };8640 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
8510 }8641 }
8511 break :val switch (sym_id.unwrap()) {8642 switch (sym_id.unwrap()) {
8512 // For global symbols, just target the right dynsym with no addend.8643 .global => |gsi| if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {
8513 .global => |name| .{ .reloc = .{8644 // When there is a dynsym entry, just target that with no addend.
8514 .type = .tpOff(elf),8645 break :val .{ .reloc = .{
8515 .dynsym_index = elf.globalByName(name).?.dynsym_index,8646 .type = .tpOff(elf),
8516 .addend = 0,8647 .dynsym_index = dynsym_index,
8517 } },8648 .addend = 0,
8518 // For local symbols, target the null symbol (index 0) so we get the offset to the8649 } };
8519 // base of our TLS block, and then use `addend` to offset to the right symbol.8650 },
8520 .local => .{ .reloc = .{8651 .local => {},
8521 .type = .tpOff(elf),8652 }
8522 .dynsym_index = 0,8653 // Otherwise, we know the offset of the symbol into our own TLS block, so target the
8523 .addend = @intCast(sym_id.value(elf)),8654 // null symbol (index 0) so we get the offset to the base of that block, and use the
8524 } },8655 // addend to offset to the correct symbol.
8525 };8656 break :val .{ .reloc = .{
8657 .type = .tpOff(elf),
8658 .dynsym_index = 0,
8659 .addend = @intCast(sym_id.value(elf)),
8660 } };
8526 },8661 },
8527 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {8662 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
8528 .static => .{ .unsigned = sym.value(elf) },8663 .static => .{ .unsigned = sym.value(elf) },
...@@ -8533,7 +8668,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8533,7 +8668,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8533 } },8668 } },
8534 .dynamic => .{ .reloc = .{8669 .dynamic => .{ .reloc = .{
8535 .type = .globDat(elf),8670 .type = .globDat(elf),
8536 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,8671 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
8537 .addend = 0,8672 .addend = 0,
8538 } },8673 } },
8539 },8674 },
...@@ -8542,7 +8677,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8542,7 +8677,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8542 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`8677 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
8543 .dynamic => .{ .reloc = .{8678 .dynamic => .{ .reloc = .{
8544 .type = .dtpOff(elf),8679 .type = .dtpOff(elf),
8545 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,8680 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
8546 .addend = 0,8681 .addend = 0,
8547 } },8682 } },
8548 },8683 },
...@@ -8555,7 +8690,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8555,7 +8690,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8555 .type = .dtpMod(elf),8690 .type = .dtpMod(elf),
8556 .dynsym_index = switch (elf.classifySymbolValue(sym)) {8691 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
8557 .static, .static_relative => 0,8692 .static, .static_relative => 0,
8558 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,8693 .dynamic => sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
8559 },8694 },
8560 .addend = 0,8695 .addend = 0,
8561 } },8696 } },
...@@ -8641,8 +8776,8 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye...@@ -8641,8 +8776,8 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
8641/// If this function creates a new copy relocation, it will also update relocations targeting the8776/// If this function creates a new copy relocation, it will also update relocations targeting the
8642/// global where needed---the caller does not need to do this.8777/// global where needed---the caller does not need to do this.
8643///8778///
8644/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global.8779/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `gsi` refers to an *undefined* global.
8645fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {8780fn maybeAddCopyRelocation(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
8646 assert(elf.shndx.dynamic != .UNDEF);8781 assert(elf.shndx.dynamic != .UNDEF);
86478782
8648 // Only dynamic executables may contain `R_*_COPY` relocations.8783 // Only dynamic executables may contain `R_*_COPY` relocations.
...@@ -8650,23 +8785,22 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -8650,23 +8785,22 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
86508785
8651 const gpa = elf.base.comp.gpa;8786 const gpa = elf.base.comp.gpa;
86528787
8653 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse8788 assert(!Symbol.Id.global(gsi).defined(elf));
8654 elf.globals.weak_undef.getPtr(global_name).?;
86558789
8656 assert(global_ptr.dynsym_index != 0);8790 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
86578791
8658 const dso_global = elf.dso_globals.get(global_name) orelse {8792 const dso_global = elf.dso_globals.get(Symbol.Id.global(gsi).name(elf)) orelse {
8659 // We do not have a definition to provide the correct size for the symbol. If a definition8793 // We do not have a definition to provide the correct size for the symbol. If a definition
8660 // is discovered in a later DSO, we may at that point be able to add a copy relocation.8794 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
8661 try elf.want_copied_globals.put(gpa, global_name, {});8795 try elf.want_copied_globals.put(gpa, gsi, {});
8662 return false;8796 return false;
8663 };8797 };
86648798
8665 if (dso_global.type != .OBJECT) return false;8799 if (dso_global.type != .OBJECT) return false;
86668800
8667 const gop = try elf.copied_globals.getOrPut(gpa, global_name);8801 const gop = try elf.copied_globals.getOrPut(gpa, gsi);
8668 if (gop.found_existing) return true;8802 if (gop.found_existing) return true;
8669 errdefer assert(elf.copied_globals.pop().?.key == global_name);8803 errdefer assert(elf.copied_globals.pop().?.key == gsi);
86708804
8671 try Section.Index.data.ensureAligned(elf, dso_global.alignment);8805 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
86728806
...@@ -8676,7 +8810,7 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -8676,7 +8810,7 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
8676 .size = dso_global.alignment.forward(dso_global.size),8810 .size = dso_global.alignment.forward(dso_global.size),
8677 .alignment = dso_global.alignment,8811 .alignment = dso_global.alignment,
8678 }),8812 }),
8679 .{ .copied_global = global_name },8813 .{ .copied_global = gsi },
8680 );8814 );
8681 errdefer comptime unreachable;8815 errdefer comptime unreachable;
86828816
...@@ -8684,7 +8818,7 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -8684,7 +8818,7 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
8684 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{8818 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8685 .type = .copy(elf),8819 .type = .copy(elf),
8686 .offset = vaddr,8820 .offset = vaddr,
8687 .raw_sym_index = global_ptr.dynsym_index,8821 .raw_sym_index = dynsym_index,
8688 .addend = 0,8822 .addend = 0,
8689 });8823 });
8690 gop.value_ptr.* = .{8824 gop.value_ptr.* = .{
...@@ -8692,18 +8826,18 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -8692,18 +8826,18 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
8692 .rela_index = rela_index,8826 .rela_index = rela_index,
8693 };8827 };
86948828
8695 switch (elf.symPtr(global_ptr.symtab_index)) {8829 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
8696 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),8830 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
8697 }8831 }
8698 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {8832 switch (elf.dynsymPtr(dynsym_index)) {
8699 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),8833 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
8700 }8834 }
87018835
8702 // Because we now have a copy relocation, any dynamic relocations which target this symbol are8836 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
8703 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs8837 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
8704 // and then update the symbol's address (and re-apply relocations targeting it of course).8838 // and then update the symbol's address (and re-apply relocations targeting it of course).
8705 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);8839 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);
8706 Symbol.Id.global(global_name).flushMoved(elf, vaddr);8840 Symbol.Id.global(gsi).flushMoved(elf, vaddr);
87078841
8708 return true;8842 return true;
8709}8843}
...@@ -9242,14 +9376,11 @@ fn flushInner(...@@ -9242,14 +9376,11 @@ fn flushInner(
92429376
9243 try elf.flushFiles();9377 try elf.flushFiles();
92449378
9245 if (comp.config.output_mode == .Exe) {9379 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {
9246 var any_undef = false;9380 for (elf.unknown_globals.keys()) |gsi| {
9247 for (elf.globals.strong_undef.keys()) |name| {9381 diags.addError("undefined global symbol '{s}'", .{Symbol.Id.global(gsi).name(elf).slice(elf)});
9248 if (elf.dso_globals.contains(name)) continue;
9249 any_undef = true;
9250 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
9251 }9382 }
9252 if (any_undef) return error.AlreadyReported;9383 return error.AlreadyReported;
9253 }9384 }
92549385
9255 try elf.prepareDynamic();9386 try elf.prepareDynamic();
...@@ -9289,7 +9420,7 @@ fn flushInner(...@@ -9289,7 +9420,7 @@ fn flushInner(
9289 elf.flushDynamic();9420 elf.flushDynamic();
92909421
9291 const entry_addr: u64 = entry: {9422 const entry_addr: u64 = entry: {
9292 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {9423 const sym_name: []const u8 = name: switch (elf.options.entry) {
9293 .default => switch (comp.config.output_mode) {9424 .default => switch (comp.config.output_mode) {
9294 .Exe => continue :name .enabled,9425 .Exe => continue :name .enabled,
9295 .Lib, .Obj => continue :name .disabled,9426 .Lib, .Obj => continue :name .disabled,
...@@ -9298,9 +9429,8 @@ fn flushInner(...@@ -9298,9 +9429,8 @@ fn flushInner(
9298 .enabled => "_start",9429 .enabled => "_start",
9299 .named => |named| named,9430 .named => |named| named,
9300 };9431 };
9301 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);9432 const gsi = elf.globalByName(sym_name) orelse break :entry 0;
9302 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;9433 break :entry Symbol.Id.global(gsi).value(elf);
9303 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
9304 };9434 };
9305 switch (elf.ehdrPtr()) {9435 switch (elf.ehdrPtr()) {
9306 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),9436 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
...@@ -9369,26 +9499,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -9369,26 +9499,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9369 break :task;9499 break :task;
9370 }9500 }
9371 if (elf.changed_symtab_index.pop()) |kv| {9501 if (elf.changed_symtab_index.pop()) |kv| {
9372 const idle_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);9502 const gsi = kv.key;
9503
9504 const idle_prog_node = elf.mf.update_prog_node.start(Symbol.Id.global(gsi).name(elf).slice(elf), 0);
9373 defer idle_prog_node.end();9505 defer idle_prog_node.end();
93749506
9375 const global_name = kv.key;9507 const sym_id: Symbol.Id = .global(gsi);
9376 const global = elf.globalByName(global_name).?;9508 const symtab_index = gsi.ptr(elf).symtab_index;
9377 const sym_id: Symbol.Id = .global(global_name);
9378 const sym = global.symtab_index.ptr(elf);
93799509
9380 switch (elf.ehdrType()) {9510 switch (elf.ehdrType()) {
9381 .REL => {9511 .REL => {
9382 // Index in `.symtab` has changed. Relocatables are easy, we just need to update9512 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
9383 // all of the output relocations.9513 // all of the output relocations.
9384 const symtab_index = @backingInt(global.symtab_index);9514 var ri = symtab_index.ptr(elf).first_target_reloc;
9385 var ri = sym.first_target_reloc;
9386 while (ri != .none) {9515 while (ri != .none) {
9387 const reloc = ri.get(elf);9516 const reloc = ri.get(elf);
9388 assert(reloc.target == sym_id);9517 assert(reloc.target == sym_id);
9389 // In relocatables, every symbol relocation has an output relocation.9518 // In relocatables, every symbol relocation has an output relocation.
9390 const rela_index = reloc.rela_index.unwrap().?;9519 const rela_index = reloc.rela_index.unwrap().?;
9391 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index);9520 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, @backingInt(symtab_index));
9392 ri = reloc.next;9521 ri = reloc.next;
9393 }9522 }
9394 },9523 },
...@@ -9401,29 +9530,32 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -9401,29 +9530,32 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9401 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they9530 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
9402 // don't care about the dynsym index). The only exception is a copy reloc9531 // don't care about the dynsym index). The only exception is a copy reloc
9403 // could exist (and be the *reason* the symbol value is statically known).9532 // could exist (and be the *reason* the symbol value is statically known).
9404 if (elf.copied_globals.get(global_name)) |copied| {9533 if (elf.copied_globals.get(gsi)) |copied| {
9405 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);9534 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
9535 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);
9406 }9536 }
9407 },9537 },
9408 .dynamic => {9538 .dynamic => {
9409 assert(!elf.copied_globals.contains(global_name)); // value would be statically known9539 assert(!elf.copied_globals.contains(gsi)); // value would be statically known
9540
9541 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
94109542
9411 // Update symbol relocs:9543 // Update symbol relocs:
9412 var ri = sym.first_target_reloc;9544 var ri = symtab_index.ptr(elf).first_target_reloc;
9413 while (ri != .none) {9545 while (ri != .none) {
9414 const reloc = ri.get(elf);9546 const reloc = ri.get(elf);
9415 assert(reloc.target == sym_id);9547 assert(reloc.target == sym_id);
9416 // There may or may not be a runtime relocation for this symbol reloc.9548 // There may or may not be a runtime relocation for this symbol reloc.
9417 if (reloc.rela_index.unwrap()) |rela_index| {9549 if (reloc.rela_index.unwrap()) |rela_index| {
9418 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index);9550 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, dynsym_index);
9419 }9551 }
9420 ri = reloc.next;9552 ri = reloc.next;
9421 }9553 }
94229554
9423 // Update the PLT entry's reloc if there is one:9555 // Update the PLT entry's reloc if there is one:
9424 if (elf.plt.getIndex(global_name)) |plt_index| {9556 if (elf.plt.getIndex(gsi)) |plt_index| {
9425 // PLT indices exactly match `.rela.plt` relocation indices.9557 // PLT indices exactly match `.rela.plt` relocation indices.
9426 elf.shndx.rela_plt.relaUpdateSym(elf, @fromBackingInt(@intCast(plt_index)), global.dynsym_index);9558 elf.shndx.rela_plt.relaUpdateSym(elf, @fromBackingInt(@intCast(plt_index)), dynsym_index);
9427 }9559 }
94289560
9429 // Update relocs for any relevant GOT entries:9561 // Update relocs for any relevant GOT entries:
...@@ -9882,13 +10014,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9882,13 +10014,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9882 }10014 }
988310015
9884 // Update global symbols targeting this section10016 // Update global symbols targeting this section
9885 if (elf.node_global_symbols.get(ni)) |first_name| {10017 if (elf.node_global_symbols.get(ni)) |first_gsi| {
9886 assert(first_name != .empty);10018 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
9887 var name = first_name;10019 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
9888 while (name != .empty) {10020 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
9889 const old_sym_addr = Symbol.Id.global(name).value(elf);10021 Symbol.Id.global(gsi).flushMoved(elf, old_sym_addr - old_addr + addr);
9890 Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr);
9891 name = elf.globalByName(name).?.next_in_node;
9892 }10022 }
9893 }10023 }
989410024
...@@ -9950,16 +10080,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9950,16 +10080,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9950 }10080 }
995110081
9952 // Update global symbols10082 // Update global symbols
9953 if (elf.node_global_symbols.get(ni)) |first_name| {10083 if (elf.node_global_symbols.get(ni)) |first_gsi| {
9954 assert(first_name != .empty);10084 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
9955 var name = first_name;10085 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
9956 while (name != .empty) {10086 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
9957 const old_sym_addr = Symbol.Id.global(name).value(elf);10087 Symbol.Id.global(gsi).flushMoved(
9958 Symbol.Id.global(name).flushMoved(
9959 elf,10088 elf,
9960 old_sym_addr - old_section_addr + new_section_addr,10089 old_sym_addr - old_section_addr + new_section_addr,
9961 );10090 );
9962 name = elf.globalByName(name).?.next_in_node;
9963 }10091 }
9964 }10092 }
996510093
...@@ -9984,12 +10112,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9984,12 +10112,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9984 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {10112 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {
9985 const new_addr = elf.computeNodeVAddr(ni);10113 const new_addr = elf.computeNodeVAddr(ni);
9986 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);10114 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
9987 if (elf.node_global_symbols.get(ni)) |first_name| {10115 if (elf.node_global_symbols.get(ni)) |first_gsi| {
9988 assert(first_name != .empty);10116 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
9989 var name = first_name;10117 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
9990 while (name != .empty) {10118 Symbol.Id.global(gsi).flushMoved(elf, new_addr);
9991 Symbol.Id.global(name).flushMoved(elf, new_addr);
9992 name = elf.globalByName(name).?.next_in_node;
9993 }10119 }
9994 }10120 }
9995 elf.flushMovedNodeRelocs(ni, new_addr, .{10121 elf.flushMovedNodeRelocs(ni, new_addr, .{
...@@ -10722,7 +10848,8 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -10722,7 +10848,8 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
10722 }10848 }
10723}10849}
1072410850
10725fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {10851/// Asserts that `gsi` has a `.dynsym` entry.
10852fn addPltEntry(elf: *Elf, gsi: Symbol.Global.Index) void {
10726 const target_endian = elf.targetEndian();10853 const target_endian = elf.targetEndian();
1072710854
10728 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a10855 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
...@@ -10730,7 +10857,7 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -10730,7 +10857,7 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10730 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{10857 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
10731 .type = .jumpSlot(elf),10858 .type = .jumpSlot(elf),
10732 .offset = 0, // populated later10859 .offset = 0, // populated later
10733 .raw_sym_index = dynsym_index,10860 .raw_sym_index = gsi.ptr(elf).dynsym_index.unwrap().?,
10734 .addend = 0,10861 .addend = 0,
10735 }));10862 }));
1073610863
...@@ -10751,13 +10878,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -10751,13 +10878,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1075110878
10752 if (plt_index < elf.plt.count()) {10879 if (plt_index < elf.plt.count()) {
10753 // We reused a free entry, so we're already done!10880 // We reused a free entry, so we're already done!
10754 elf.plt.setKey(plt_index, global_name);10881 elf.plt.setKey(plt_index, gsi);
10755 return;10882 return;
10756 }10883 }
1075710884
10758 // We added a new entry, so we now need to extend the PLT sections.10885 // We added a new entry, so we now need to extend the PLT sections.
10759 assert(plt_index == elf.plt.count());10886 assert(plt_index == elf.plt.count());
10760 elf.plt.putAssumeCapacityNoClobber(global_name, {});10887 elf.plt.putAssumeCapacityNoClobber(gsi, {});
1076110888
10762 switch (elf.ehdrMachine()) {10889 switch (elf.ehdrMachine()) {
10763 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),10890 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
...@@ -10929,8 +11056,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -10929,8 +11056,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
10929 // its relocations are probably going through the PLT, so we don't bother with11056 // its relocations are probably going through the PLT, so we don't bother with
10930 // specific tracking for PLT relocations---instead just re-apply all relocations11057 // specific tracking for PLT relocations---instead just re-apply all relocations
10931 // targeting symbols with PLT entries.11058 // targeting symbols with PLT entries.
10932 for (elf.plt.keys()) |name| {11059 for (elf.plt.keys()) |gsi| {
10933 Symbol.Id.global(name).applyTargetRelocs(elf);11060 Symbol.Id.global(gsi).applyTargetRelocs(elf);
10934 }11061 }
10935 // We also need to update all of the references from `.plt.sec` to `.got.plt`.11062 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
10936 // However, if there's also a flush pending for `.got.plt`, don't bother doing11063 // However, if there's also a flush pending for `.got.plt`, don't bother doing
...@@ -10986,8 +11113,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -10986,8 +11113,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
10986 // its relocations are probably going through the PLT, so we don't bother with11113 // its relocations are probably going through the PLT, so we don't bother with
10987 // specific tracking for PLT relocations---instead just re-apply all relocations11114 // specific tracking for PLT relocations---instead just re-apply all relocations
10988 // targeting symbols with PLT entries.11115 // targeting symbols with PLT entries.
10989 for (elf.plt.keys()) |name| {11116 for (elf.plt.keys()) |gsi| {
10990 Symbol.Id.global(name).applyTargetRelocs(elf);11117 Symbol.Id.global(gsi).applyTargetRelocs(elf);
10991 }11118 }
10992 // We also need to update all of the references from `.plt` to `.got.plt`.11119 // We also need to update all of the references from `.plt` to `.got.plt`.
10993 // However, if there's also a flush pending for `.got.plt`, don't bother doing11120 // However, if there's also a flush pending for `.got.plt`, don't bother doing
...@@ -11053,8 +11180,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -11053,8 +11180,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
11053 // its relocations are probably going through the PLT, so we don't bother with11180 // its relocations are probably going through the PLT, so we don't bother with
11054 // specific tracking for PLT relocations---instead just re-apply all relocations11181 // specific tracking for PLT relocations---instead just re-apply all relocations
11055 // targeting symbols with PLT entries.11182 // targeting symbols with PLT entries.
11056 for (elf.plt.keys()) |name| {11183 for (elf.plt.keys()) |gsi| {
11057 Symbol.Id.global(name).applyTargetRelocs(elf);11184 Symbol.Id.global(gsi).applyTargetRelocs(elf);
11058 }11185 }
11059 // Update the offsets of the relocation entries in `.rela.plt`.11186 // Update the offsets of the relocation entries in `.rela.plt`.
11060 const rela_plt_shndx = elf.shndx.rela_plt;11187 const rela_plt_shndx = elf.shndx.rela_plt;
...@@ -11118,7 +11245,7 @@ fn updateExportInner(...@@ -11118,7 +11245,7 @@ fn updateExportInner(
11118 const name = @"export".opts.name.toSlice(ip);11245 const name = @"export".opts.name.toSlice(ip);
11119 _ = elf.addGlobalSymbolAssumeCapacity(.{11246 _ = elf.addGlobalSymbolAssumeCapacity(.{
11120 .node = exported_lsi.index().ptr(elf).node,11247 .node = exported_lsi.index().ptr(elf).node,
11121 .name = try .string(elf, name),11248 .name = name,
11122 .value = cur_value,11249 .value = cur_value,
11123 .size = cur_size,11250 .size = cur_size,
11124 .type = @"type",11251 .type = @"type",
...@@ -11140,8 +11267,8 @@ fn updateExportInner(...@@ -11140,8 +11267,8 @@ fn updateExportInner(
11140 // get these errors on every non-initial incremental update. Hack around that by11267 // get these errors on every non-initial incremental update. Hack around that by
11141 // only emitting this error if the symbol we're conflicting with comes from an input11268 // only emitting this error if the symbol we're conflicting with comes from an input
11142 // section (as opposed to the ZCU).11269 // section (as opposed to the ZCU).
11143 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;11270 const conflicting_global = elf.globalByName(name).?;
11144 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {11271 if (conflicting_global.ptr(elf).symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
11145 if (elf.getNode(conflicting_node) == .input_section) {11272 if (elf.getNode(conflicting_node) == .input_section) {
11146 return elf.base.comp.link_diags.fail(11273 return elf.base.comp.link_diags.fail(
11147 "multiple definitions of '{s}'",11274 "multiple definitions of '{s}'",
...@@ -11150,6 +11277,7 @@ fn updateExportInner(...@@ -11150,6 +11277,7 @@ fn updateExportInner(
11150 }11277 }
11151 }11278 }
11152 },11279 },
11280 else => |e| return e,
11153 };11281 };
11154}11282}
1115511283
...@@ -11214,7 +11342,7 @@ pub fn printNode(...@@ -11214,7 +11342,7 @@ pub fn printNode(
11214 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),11342 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
11215 });11343 });
11216 },11344 },
11217 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),11345 .copied_global => |gsi| try w.print("(copy:{s})", .{Symbol.Id.global(gsi).name(elf).slice(elf)}),
11218 .nav => |nmi| {11346 .nav => |nmi| {
11219 const zcu = elf.base.comp.zcu.?;11347 const zcu = elf.base.comp.zcu.?;
11220 const ip = &zcu.intern_pool;11348 const ip = &zcu.intern_pool;
...@@ -11402,7 +11530,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato...@@ -11402,7 +11530,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato
11402fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {11530fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
11403 const index = switch (sym.unwrap()) {11531 const index = switch (sym.unwrap()) {
11404 .local => return null,11532 .local => return null,
11405 .global => |name| elf.plt.getIndex(name) orelse return null,11533 .global => |gsi| elf.plt.getIndex(gsi) orelse return null,
11406 };11534 };
11407 if (elf.pltEntryIsDead(index)) return null;11535 if (elf.pltEntryIsDead(index)) return null;
11408 const plt = elf.targetPltInfo();11536 const plt = elf.targetPltInfo();