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 {
7575 soname: String(.dynstr),
7676},
7777symtab: std.ArrayList(Symbol),
78globals: struct {
79 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
80 weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
81 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
82 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
83},
84/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).
85copied_globals: std.array_hash_map.Auto(String(.strtab), struct {
78globals: std.ArrayList(Symbol.Global),
79/// Accessed with `Symbol.Global.NameAdapter`. Items map 1--1 to `globals`.
80globals_by_name: std.array_hash_map.Custom(void, void, void, true),
81/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We
82/// maintain this set to allow efficient reporting of "undefined global symbol" errors.
83unknown_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
84/// Key is an undef global for which we have created a "copy relocation" (`R_*_COPY`).
85copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, struct {
8686 node: MappedFile.Node.Index,
8787 /// The index of this global's runtime relocation in `.rela.dyn`.
8888 rela_index: Section.RelaIndex,
8989}),
90/// Key is the name of an undef global for which we would *like* to create a copy relocation
91/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.
90/// Key is an undef global for which we would *like* to create a copy relocation (`R_*_COPY`), but
91/// cannot because we have not seen an appropriate definition in a linked DSO yet.
9292///
9393/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
9494/// will remove it from this map and call `maybeAddCopyRelocation`.
95want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),
96/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
97/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
98///
99/// Value is never `.empty`.
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 first global symbol in that
97/// node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
10098///
10199/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
102100/// 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),
104102/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:
105103///
106104/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
......@@ -132,7 +130,7 @@ dynstr: StringTable,
132130///
133131/// Value is the output relocation in `.rela.dyn` for the GOT entry.
134132got: 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.
136134///
137135/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
138136/// 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),
140138///
141139/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
142140/// 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),
144142/// The `.plt` section contains zero or more symbol relocations starting at this index.
145143plt_first_symbol_reloc: SymbolReloc.Index,
146144/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.
......@@ -203,12 +201,13 @@ got_relocs: std.ArrayList(GotReloc),
203201tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
204202/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
205203section_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 relocation
204/// Key is a global symbol which has been moved to a new index in the symbol table. Any relocation
207205/// entries which target that symbol must be updated to reference the correct symbol index.
208206///
209/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it
210/// refers to the index in `.dynsym`.
211changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
207/// * In an `ET_REL`, this means the index in `.symtab` has changed.
208/// * In a DSO (or static PIE), this means the index in `.dynsym` has changed.
209/// * Otherwise this is always empty.
210changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
212211/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
213212/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
214213/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
......@@ -271,14 +270,14 @@ const Node = union(enum) {
271270 section_manual_size: Section.Index,
272271 /// May contain relocations.
273272 input_section: InputSection.Index,
274 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
275 /// which we have emitted a copy relocation.
273 /// Value is a global which has an entry in `elf.copied_globals`, so, a global for which we have
274 /// emitted a copy relocation.
276275 ///
277276 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
278277 ///
279278 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
280279 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.
281 copied_global: String(.strtab),
280 copied_global: Symbol.Global.Index,
282281 /// May contain relocations.
283282 nav: NavMapIndex,
284283 /// May contain relocations.
......@@ -2200,10 +2199,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
22002199 switch (kind) {
22012200 .all_local => {},
22022201 .maybe_global => {
2203 try elf.globals.strong_def.ensureUnusedCapacity(gpa, len);
2204 try elf.globals.weak_def.ensureUnusedCapacity(gpa, len);
2205 try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len);
2206 try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len);
2202 try elf.globals.ensureUnusedCapacity(gpa, len);
2203 try elf.globals_by_name.ensureUnusedCapacity(gpa, len);
2204 try elf.unknown_globals.ensureUnusedCapacity(gpa, len);
22072205
22082206 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
22092207
......@@ -2283,10 +2281,10 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
22832281 chains[0] = 0;
22842282 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
22852283 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)) {
22872285 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
22882286 };
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;
22902288 // Make this symbol the head of that bucket, and chain to the old head.
22912289 chain.* = buckets[b];
22922290 elf.targetStore(&buckets[b], dynsym_index);
......@@ -2326,10 +2324,10 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
23262324 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
23272325 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)) {
23302328 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
23312329 };
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;
23332331 // Make this symbol the head of that bucket, and chain to the old head.
23342332 chains[dynsym_index] = buckets[b];
23352333 elf.targetStore(&buckets[b], dynsym_index);
......@@ -2367,10 +2365,10 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
23672365 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
23682366 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)) {
23712369 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
23722370 };
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
23752373 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
23762374 elf.targetStore(&chains[dynsym_index], 0);
......@@ -2479,11 +2477,12 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
24792477 new_index.ptr(elf).* = target_index.ptr(elf).*;
24802478 // ...then update the `elf.globals` tracking.
24812479 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
24842483 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
24852484 // 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, {});
24872486 }
24882487 }
24892488
......@@ -2509,140 +2508,84 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
25092508 }
25102509}
25112510
2512const AddGlobalSymbolOptions = 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
2511fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
25272512 node: MappedFile.Node.Index.Optional,
2528 name: Name,
2513 name: []const u8,
25292514 lib_name: ?[]const u8 = null,
25302515 value: u64,
25312516 size: u64,
25322517 type: std.elf.STT,
2533 bind: enum { strong, weak },
2518 bind: Symbol.Global.Bind,
25342519 visibility: std.elf.STV,
25352520 shndx: Section.Index,
2536};
2537fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{MultipleDefinitions}!Symbol.Id {
2521}) (error{MultipleDefinitions} || Error)!Symbol.Global.Index {
25382522 _ = opts.lib_name; // TODO
25392523
2540 if (elf.shndx.dynsym == .UNDEF) {
2541 assert(opts.name.dynstr == .empty);
2542 } else {
2543 assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));
2544 }
2545
2546 // We break from this `switch` only if this symbol name did not previously exist at all and so
2547 // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need
2548 // a new symtab entry.
2549 const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {
2550 .strong => new_global: {
2551 const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);
2552 if (gop.found_existing) return error.MultipleDefinitions;
2553 const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse
2554 elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2555 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2556 // The symbol did not already exist, so we'll use the "new global" path.
2557 break :new_global gop.value_ptr;
2558 };
2559 gop.value_ptr.* = old_kv.value;
2560 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2561 .node = opts.node,
2562 .value = opts.value,
2563 .size = opts.size,
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);
2524 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };
2525 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(opts.name, adapter);
2526 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));
2527
2528 if (gop.found_existing) {
2529 // There's already a symbol with this name, we just need to potentially set its value, and
2530 // to update its bind and visibility.
2531 const new_bind: Symbol.Global.Bind = new_bind: {
2532 if (opts.shndx == .UNDEF) {
2533 // We are not defining the symbol, so we won't set its value and we should only
2534 // update its bind if it is undefined and we won't weaken it.
2535 if (!Symbol.Id.global(gsi).defined(elf) and
2536 gsi.bind(elf) == .weak and
2537 opts.bind == .strong)
2538 {
2539 // Since the symbol is not only undefined but is now also strong, we now *need*
2540 // to see a definition for it at some point.
2541 if (!elf.dso_globals.contains(Symbol.Id.global(gsi).name(elf))) {
2542 elf.unknown_globals.putAssumeCapacityNoClobber(gsi, {});
2543 }
2544 break :new_bind .strong;
2545 } else {
2546 break :new_bind gsi.bind(elf);
2547 }
26362548 }
2637 const gop = elf.globals.weak_undef.getOrPutAssumeCapacity(opts.name.strtab);
2638 if (gop.found_existing) {
2639 // The existing symbol is okay, we just merge our visibility in.
2640 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2641 return .global(opts.name.strtab);
2549 // We're providing a definition. If there is no other definition yet, that's easy, but
2550 // if there is, we need to figure out which to keep.
2551 if (!Symbol.Id.global(gsi).defined(elf)) {
2552 _ = elf.unknown_globals.swapRemove(gsi); // previously undef so might be here
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 },
26422579 }
2643 break :new_global gop.value_ptr;
2644 },
2645 };
2580 };
2581 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility, new_bind);
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
26472590 const force_local_bind: bool = switch (opts.visibility) {
26482591 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
......@@ -2657,7 +2600,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
26572600 };
26582601
26592602 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: {
26612604 break :t dso_global.type;
26622605 } else .NOTYPE,
26632606 else => |t| t,
......@@ -2678,7 +2621,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
26782621 // ...then populate the newly-valid symbol pointer
26792622 const sym = @field(elf.symPtr(sym_index), @tagName(class));
26802623 sym.* = .{
2681 .name = @backingInt(opts.name.strtab),
2624 .name = @backingInt(strtab_name),
26822625 .value = @intCast(opts.value),
26832626 .size = @intCast(opts.size),
26842627 .info = .{ .type = @"type", .bind = bind },
......@@ -2691,19 +2634,30 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
26912634 },
26922635 }
26932636
2694 const old_head: String(.strtab) = old_head: {
2695 const node = opts.node.unwrap() orelse break :old_head .empty;
2696 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2697 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2698 gop.value_ptr.* = opts.name.strtab;
2637 const old_head: Symbol.Global.Index.Optional = old_head: {
2638 const node = opts.node.unwrap() orelse break :old_head .none;
2639 const node_globals_gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2640 const old_head: Symbol.Global.Index.Optional = if (node_globals_gop.found_existing)
2641 .wrap(node_globals_gop.value_ptr.*)
2642 else
2643 .none;
2644 node_globals_gop.value_ptr.* = gsi;
26992645 break :old_head old_head;
27002646 };
27012647
2702 new_global_ptr.* = .{
2648 gsi.ptr(elf).* = .{
27032649 .symtab_index = sym_index,
27042650 .dynsym_index = dynsym_index: {
2705 if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;
2706 if (force_local_bind) break :dynsym_index 0;
2651 if (force_local_bind) {
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);
27072661 switch (elf.shdrPtr(elf.shndx.dynsym)) {
27082662 inline else => |shdr, class| {
27092663 const Sym = class.ElfN().Sym;
......@@ -2714,7 +2668,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
27142668 // ...then populate the newly-valid symbol pointer
27152669 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
27162670 sym.* = .{
2717 .name = @backingInt(opts.name.dynstr),
2671 .name = @backingInt(dynstr_name),
27182672 .value = @intCast(opts.value),
27192673 .size = @intCast(opts.size),
27202674 .info = .{ .type = @"type", .bind = bind },
......@@ -2725,76 +2679,76 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
27252679 std.mem.byteSwapAllFields(Sym, sym);
27262680 }
27272681 elf.appendDynsymHashEntry(dynsym_index);
2728 break :dynsym_index dynsym_index;
2682 break :dynsym_index .wrap(dynsym_index);
27292683 },
27302684 }
27312685 },
2732 .prev_in_node = .empty,
2686 .prev_in_node = .none,
27332687 .next_in_node = old_head,
27342688 };
27352689
2736 if (old_head != .empty) {
2737 const old_head_ptr = elf.globalByName(old_head).?;
2690 if (old_head.unwrap()) |old_head_gsi| {
2691 const old_head_ptr = old_head_gsi.ptr(elf);
27382692 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
2739 assert(old_head_ptr.prev_in_node == .empty);
2740 old_head_ptr.prev_in_node = opts.name.strtab;
2693 assert(old_head_ptr.prev_in_node == .none);
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, {});
27412702 }
27422703
27432704 if (force_local_bind) {
2744 elf.moveDemotedGlobal(new_global_ptr);
2705 elf.moveDemotedGlobal(gsi);
27452706 }
27462707
27472708 switch (@"type") {
27482709 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2749 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
2710 elf.classifySymbolValue(.global(gsi)) == .dynamic)
27502711 {
27512712 // 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);
27532714 },
27542715 else => {},
27552716 }
27562717
2757 return .global(opts.name.strtab);
2718 return gsi;
27582719}
2759fn setGlobalSymbolValue(
2760 elf: *Elf,
2761 global_name: String(.strtab),
2762 global_ptr: *Symbol.Global,
2763 new: struct {
2764 node: MappedFile.Node.Index.Optional,
2765 value: u64,
2766 size: u64,
2767 type: std.elf.STT,
2768 shndx: Section.Index,
2769 },
2770) void {
2720fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
2721 node: MappedFile.Node.Index.Optional,
2722 value: u64,
2723 size: u64,
2724 type: std.elf.STT,
2725 shndx: Section.Index,
2726}) void {
2727 const global_ptr = gsi.ptr(elf);
2728
27712729 assert(new.shndx != .UNDEF);
27722730 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2773 if (global_ptr.next_in_node != .empty) {
2774 const next = elf.globalByName(global_ptr.next_in_node).?;
2775 assert(next.prev_in_node == global_name);
2776 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);
2777 next.prev_in_node = global_ptr.prev_in_node;
2778 }
2779 if (global_ptr.prev_in_node != .empty) {
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;
2731 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
2732 assert(next_gsi.ptr(elf).prev_in_node == Symbol.Global.Index.Optional.wrap(gsi));
2733 next_gsi.ptr(elf).prev_in_node = global_ptr.prev_in_node;
2734 }
2735 if (global_ptr.prev_in_node.unwrap()) |prev_gsi| {
2736 assert(prev_gsi.ptr(elf).next_in_node == Symbol.Global.Index.Optional.wrap(gsi));
2737 prev_gsi.ptr(elf).next_in_node = global_ptr.next_in_node;
27842738 } else {
27852739 // We're the start of the linked list, so we need to change the head.
2786 if (global_ptr.next_in_node == .empty) {
2787 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);
2740 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
2741 elf.node_global_symbols.getPtr(old_node).?.* = next_gsi;
27882742 } 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);
27902744 }
27912745 }
27922746 } else {
2793 assert(global_ptr.next_in_node == .empty);
2794 assert(global_ptr.prev_in_node == .empty);
2747 assert(global_ptr.next_in_node == .none);
2748 assert(global_ptr.prev_in_node == .none);
27952749 }
27962750
2797 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {
2751 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
27982752 // This is a quite rare case: there was a definition for this symbol in a shared library
27992753 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
28002754 // definition which replaces it. We know that our definition cannot be preempted because we
......@@ -2808,27 +2762,25 @@ fn setGlobalSymbolValue(
28082762 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
28092763 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
28102764 } else {
2811 _ = elf.want_copied_globals.swapRemove(global_name);
2765 _ = elf.want_copied_globals.swapRemove(gsi);
28122766 }
28132767
28142768 global_ptr.symtab_index.ptr(elf).node = new.node;
28152769
2816 const old_head: String(.strtab) = old_head: {
2817 const new_node = new.node.unwrap() orelse break :old_head .empty;
2770 const old_head: Symbol.Global.Index.Optional = old_head: {
2771 const new_node = new.node.unwrap() orelse break :old_head .none;
28182772 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2819 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2820 gop.value_ptr.* = global_name;
2773 const old_head: Symbol.Global.Index.Optional = if (gop.found_existing) .wrap(gop.value_ptr.*) else .none;
2774 gop.value_ptr.* = gsi;
28212775 break :old_head old_head;
28222776 };
28232777
2824 global_ptr.prev_in_node = .empty;
2778 global_ptr.prev_in_node = .none;
28252779 global_ptr.next_in_node = old_head;
28262780
2827 if (old_head != .empty) {
2828 const old_head_ptr = elf.globalByName(old_head).?;
2829 assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);
2830 assert(old_head_ptr.prev_in_node == .empty);
2831 old_head_ptr.prev_in_node = global_name;
2781 if (old_head.unwrap()) |old_head_gsi| {
2782 assert(old_head_gsi.ptr(elf).prev_in_node == .none);
2783 old_head_gsi.ptr(elf).prev_in_node = .wrap(gsi);
28322784 }
28332785
28342786 // Now for the easy bit where we actually update the symtab entry.
......@@ -2846,7 +2798,7 @@ fn setGlobalSymbolValue(
28462798 }
28472799
28482800 // ...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)) {
28502802 inline else => |sym| {
28512803 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
28522804 elf.targetStore(&sym.size, @intCast(new.size));
......@@ -2862,9 +2814,9 @@ fn setGlobalSymbolValue(
28622814 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
28632815 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
28642816 // 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| {
28662818 if (!elf.pltEntryIsDead(plt_index) and
2867 elf.classifySymbolValue(.global(global_name)) != .dynamic)
2819 elf.classifySymbolValue(.global(gsi)) != .dynamic)
28682820 {
28692821 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
28702822 assert(elf.pltEntryIsDead(plt_index));
......@@ -2874,81 +2826,155 @@ fn setGlobalSymbolValue(
28742826 // If this symbol was previously undefined, relocations targeting it may have been lowered to
28752827 // runtime relocations which we have now discovered we do not need, so delete those. This does
28762828 // 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) {
2878 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
2829 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(gsi)) != .dynamic) {
2830 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);
28792831 }
28802832
28812833 // Finally, update the symbol value, re-applying target relocations. Also note that because we
28822834 // possibly removed the PLT entry above, some relocations which were previously targeting the
28832835 // 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);
28852837}
28862838/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
28872839/// undefined---their visibility values are combined to determine the resulting visibility, which
28882840/// 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
28902849 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
28912850 inline else => |sym| elf.targetLoad(&sym.other).visibility,
28922851 };
2893 // The combined visibility is essentially the "strictest" of the two, with most strict being
2894 // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2895 const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {
2896 .INTERNAL => .{ .INTERNAL, false },
2852
2853 // We will combine the old and new visibilities. The combined visibility is basically whichever
2854 // is "stricter", with most strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
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 },
28972864 .HIDDEN => switch (other_visibility) {
2898 .INTERNAL => .{ .INTERNAL, false },
2899 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },
2865 .INTERNAL => .{ .INTERNAL, .yes_already },
2866 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, .yes_already },
29002867 },
29012868 .PROTECTED => switch (other_visibility) {
2902 .INTERNAL => .{ .INTERNAL, true },
2903 .HIDDEN => .{ .HIDDEN, true },
2904 .PROTECTED, .DEFAULT => .{ .PROTECTED, false },
2869 .INTERNAL => .{ .INTERNAL, .yes_new },
2870 .HIDDEN => .{ .HIDDEN, .yes_new },
2871 .PROTECTED, .DEFAULT => .{ .PROTECTED, .no },
29052872 },
29062873 .DEFAULT => switch (other_visibility) {
2907 .INTERNAL => .{ .INTERNAL, true },
2908 .HIDDEN => .{ .HIDDEN, true },
2909 .PROTECTED => .{ .PROTECTED, false },
2910 .DEFAULT => .{ .DEFAULT, false },
2874 .INTERNAL => .{ .INTERNAL, .yes_new },
2875 .HIDDEN => .{ .HIDDEN, .yes_new },
2876 .PROTECTED => .{ .PROTECTED, .no },
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 },
29112910 },
2912 };
2913 // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared
2914 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
2915 // putting the global in this state for the first time---let's call it "demoting" the global to
2916 // STB_LOCAL---we need to update its bind in the symtab.
2917 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;
2918 switch (elf.symPtr(global_ptr.symtab_index)) {
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,
2911
2912 .yes_already => {
2913 // Update the visibility in the symtab entry...
2914 switch (elf.symPtr(global_ptr.symtab_index)) {
2915 inline else => |sym| {
2916 assert(elf.targetLoad(&sym.info).bind == .LOCAL); // symbol should be demoted
2917 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
29292918 },
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,
29302955 };
2931 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2932 elf.targetStore(&sym.info, new_info);
2933 // also update dynsym
2934 if (global_ptr.dynsym_index != 0) {
2935 const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
2936 elf.targetStore(&dynsym.other, .{ .visibility = new_visibility });
2937 elf.targetStore(&dynsym.info, new_info);
2956
2957 // If there was a copy relocation for this symbol, it is now unnecessary. See equivalent
2958 // logic in `setGlobalSymbolValue` for an explanation of this case.
2959 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
2960 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2961 // TODO: once we can delete `MappedFile` nodes, delete `copied_global_kv.value.node`
2962 } else {
2963 _ = elf.want_copied_globals.swapRemove(gsi);
29382964 }
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);
29392969 },
29402970 }
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 }
29462971}
29472972/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
29482973/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
29492974/// 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 {
29512976 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
2977 const global_ptr = gsi.ptr(elf);
29522978 switch (elf.shdrPtr(.symtab)) {
29532979 inline else => |shdr, class| {
29542980 // `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 {
29692995 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
29702996 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
29752998 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));
2976 const other_global_ptr = elf.globalByName(other_name).?;
2977 assert(other_global_ptr.symtab_index == dest_index);
2999 const other_gsi = elf.globalByName(other_name.slice(elf)).?;
3000 assert(other_gsi.ptr(elf).symtab_index == dest_index);
29783001
29793002 // First swap the symtab entries...
29803003 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 {
29823005 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
29833006 // ...then update the `elf.globals` tracking.
29843007 global_ptr.symtab_index = dest_index;
2985 other_global_ptr.symtab_index = src_index;
3008 other_gsi.ptr(elf).symtab_index = src_index;
29863009 }
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,
2989 // we'll move another symbol into its place just like we did above.
2990 if (global_ptr.dynsym_index != 0) {
2991 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
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;
3027 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
3028 const old_size = elf.targetLoad(&dynsym_shdr.size);
3029 const new_size = old_size - ent_size;
3030 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
30053031
3006 if (free_dynsym_index != 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.
3032 elf.popDynsymHashEntry(remove_dynsym_index);
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));
3013 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
3038 elf.clearDynsymHashEntry(dynsym_index);
30143039
3015 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
3016 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
3017 const moved_global_ptr = elf.globalByName(moved_name).?;
3040 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
3041 const dest_dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
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);
3022 moved_global_ptr.dynsym_index = free_dynsym_index;
3047 dest_dynsym_ptr.* = src_dynsym_ptr.*;
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 any
3027 // relocation entries targeting it.
3028 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
3029 }
3052 elf.populateDynsymHashEntry(dynsym_index);
30303053
3031 // Now that we've given that symbol a new home, actually decrease the section size.
3032 elf.targetStore(&dynsym_shdr.size, new_size);
3054 // Since that symbol's dynsym index has changed, we'll have to update any
3055 // relocation entries targeting it.
3056 elf.changed_symtab_index.putAssumeCapacity(moved_gsi, {});
30333057 }
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);
30343061 },
30353062 }
30363063}
......@@ -3049,18 +3076,102 @@ const Symbol = struct {
30493076 const Global = struct {
30503077 /// The current index of the symtab entry for this global symbol.
30513078 symtab_index: Symbol.Index,
3052 /// The current index of the dynsym entry for this global symbol. If the global has been
3053 /// demoted to STB_LOCAL, it does not have a dynsym entry and this field is set to 0.
3054 dynsym_index: u32,
3079 /// As well as the index of this global symbol's `.dynsym` entry, in the event that the
3080 /// symbol is *not* in `.dynsym`, we store a special value here indicating why that is.
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
30563118 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
3057 ///
3058 /// If `node` is `.none`, this is `.empty`.
3059 next_in_node: String(.strtab),
3119 next_in_node: Symbol.Global.Index.Optional,
30603120 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
3061 ///
3062 /// If `node` is `.none`, this is `.empty`.
3063 prev_in_node: String(.strtab),
3121 prev_in_node: Symbol.Global.Index.Optional,
3122
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 };
30643175 };
30653176
30663177 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
......@@ -3107,12 +3218,12 @@ const Symbol = struct {
31073218 fn local(lsi: Symbol.LocalIndex) Symbol.Id {
31083219 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };
31093220 }
3110 fn global(name: String(.strtab)) Symbol.Id {
3111 return .{ .kind = .global, .raw = @intCast(@backingInt(name)) };
3221 fn global(gsi: Symbol.Global.Index) Symbol.Id {
3222 return .{ .kind = .global, .raw = @intCast(@backingInt(gsi)) };
31123223 }
31133224 fn unwrap(s: Symbol.Id) union(enum) {
31143225 local: Symbol.LocalIndex,
3115 global: String(.strtab),
3226 global: Symbol.Global.Index,
31163227 } {
31173228 return switch (s.kind) {
31183229 .local => .{ .local = @fromBackingInt(s.raw) },
......@@ -3127,10 +3238,10 @@ const Symbol = struct {
31273238 return @bitCast(s);
31283239 }
31293240
3130 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
3241 fn index(s: Symbol.Id, elf: *Elf) Symbol.Index {
31313242 return switch (s.unwrap()) {
31323243 .local => |lsi| lsi.index(),
3133 .global => |name| elf.globalByName(name).?.symtab_index,
3244 .global => |gsi| gsi.ptr(elf).symtab_index,
31343245 };
31353246 }
31363247
......@@ -3143,6 +3254,18 @@ const Symbol = struct {
31433254 };
31443255 }
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
31463269 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
31473270 // Update the symbol value in `.symtab`
31483271 const sym_index = sym_id.index(elf);
......@@ -3153,10 +3276,9 @@ const Symbol = struct {
31533276 // Update the symbol value in `.dynsym` if applicable
31543277 switch (sym_id.unwrap()) {
31553278 .local => {},
3156 .global => |name| {
3157 const g = elf.globalByName(name).?;
3158 if (g.dynsym_index != 0) {
3159 switch (elf.dynsymPtr(g.dynsym_index)) {
3279 .global => |gsi| {
3280 if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {
3281 switch (elf.dynsymPtr(dynsym_index)) {
31603282 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
31613283 }
31623284 }
......@@ -3255,7 +3377,7 @@ const Symbol = struct {
32553377 }
32563378 switch (s.unwrap()) {
32573379 .local => {},
3258 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {
3380 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {
32593381 return copied_global.node.hasMoved(&elf.mf);
32603382 },
32613383 }
......@@ -3264,12 +3386,10 @@ const Symbol = struct {
32643386 };
32653387};
32663388
3267fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
3268 if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;
3269 if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;
3270 if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;
3271 if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;
3272 return null;
3389fn globalByName(elf: *Elf, name: []const u8) ?Symbol.Global.Index {
3390 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };
3391 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;
3392 return @fromBackingInt(@intCast(index_raw));
32733393}
32743394
32753395fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
......@@ -3307,11 +3427,11 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
33073427 assert(shndx != .UNDEF);
33083428 assert(visibility == .DEFAULT);
33093429 },
3310 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
3430 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
33113431 // An unprotected symbol in a DSO which is not an executable is subject to runtime
33123432 // preemption, so a dynamic relocation is required for it even if we have a definition.
33133433 return .dynamic;
3314 } else if (elf.copied_globals.contains(name)) {
3434 } else if (elf.copied_globals.contains(gsi)) {
33153435 // This becomes a locally-defined symbol in `.data`.
33163436 return if (runtime_load_addr) .static_relative else .static;
33173437 },
......@@ -3449,9 +3569,9 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb
34493569}
34503570fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
34513571 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
3452 const symbol = elf.addGlobalSymbolAssumeCapacity(.{
3572 const gsi = elf.addGlobalSymbolAssumeCapacity(.{
34533573 .node = .none,
3454 .name = try .string(elf, opts.name),
3574 .name = opts.name,
34553575 .lib_name = opts.lib_name,
34563576 .value = 0,
34573577 .size = 0,
......@@ -3470,8 +3590,9 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
34703590 .shndx = .UNDEF,
34713591 }) catch |err| switch (err) {
34723592 error.MultipleDefinitions => unreachable, // shndx is undef
3593 else => |e| return e,
34733594 };
3474 return symbol;
3595 return .global(gsi);
34753596}
34763597pub fn addReloc(
34773598 elf: *Elf,
......@@ -3836,12 +3957,9 @@ fn create(
38363957 .soname = .empty,
38373958 },
38383959 .symtab = .empty,
3839 .globals = .{
3840 .strong_def = .empty,
3841 .weak_def = .empty,
3842 .strong_undef = .empty,
3843 .weak_undef = .empty,
3844 },
3960 .globals = .empty,
3961 .globals_by_name = .empty,
3962 .unknown_globals = .empty,
38453963 .copied_globals = .empty,
38463964 .want_copied_globals = .empty,
38473965 .node_global_symbols = .empty,
......@@ -3915,10 +4033,9 @@ pub fn deinit(elf: *Elf) void {
39154033 elf.shdrs.deinit(gpa);
39164034 elf.phdrs.deinit(gpa);
39174035 elf.symtab.deinit(gpa);
3918 elf.globals.strong_def.deinit(gpa);
3919 elf.globals.weak_def.deinit(gpa);
3920 elf.globals.strong_undef.deinit(gpa);
3921 elf.globals.weak_undef.deinit(gpa);
4036 elf.globals.deinit(gpa);
4037 elf.globals_by_name.deinit(gpa);
4038 elf.unknown_globals.deinit(gpa);
39224039 elf.copied_globals.deinit(gpa);
39234040 elf.want_copied_globals.deinit(gpa);
39244041 elf.node_global_symbols.deinit(gpa);
......@@ -4978,7 +5095,7 @@ fn initHeaders(
49785095 // Despite the name, `__dso_handle` is necessary even in static binaries.
49795096 _ = elf.addGlobalSymbolAssumeCapacity(.{
49805097 .node = .wrap(Section.Index.text.get(elf).ni),
4981 .name = try .string(elf, "__dso_handle"),
5098 .name = "__dso_handle",
49825099 .value = Section.Index.text.vaddr(elf),
49835100 .size = 0,
49845101 .type = .NOTYPE,
......@@ -4987,10 +5104,11 @@ fn initHeaders(
49875104 .shndx = .text,
49885105 }) catch |err| switch (err) {
49895106 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5107 else => |e| return e,
49905108 };
49915109 _ = elf.addGlobalSymbolAssumeCapacity(.{
49925110 .node = .wrap(elf.shndx.plt.get(elf).ni),
4993 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),
5111 .name = "_PROCEDURE_LINKAGE_TABLE_",
49945112 .value = elf.shndx.plt.vaddr(elf),
49955113 .size = 0,
49965114 .type = .NOTYPE,
......@@ -4999,10 +5117,11 @@ fn initHeaders(
49995117 .shndx = elf.shndx.plt,
50005118 }) catch |err| switch (err) {
50015119 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5120 else => |e| return e,
50025121 };
50035122 _ = elf.addGlobalSymbolAssumeCapacity(.{
50045123 .node = .wrap(elf.shndx.got.get(elf).ni),
5005 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
5124 .name = "_GLOBAL_OFFSET_TABLE_",
50065125 .value = switch (machine) {
50075126 .AARCH64,
50085127 .LOONGARCH,
......@@ -5023,10 +5142,11 @@ fn initHeaders(
50235142 .shndx = elf.shndx.got,
50245143 }) catch |err| switch (err) {
50255144 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5145 else => |e| return e,
50265146 };
50275147 _ = elf.addGlobalSymbolAssumeCapacity(.{
50285148 .node = .none,
5029 .name = try .string(elf, "__init_array_start"),
5149 .name = "__init_array_start",
50305150 .value = 0,
50315151 .size = 0,
50325152 .type = .NOTYPE,
......@@ -5035,10 +5155,11 @@ fn initHeaders(
50355155 .shndx = .ABS,
50365156 }) catch |err| switch (err) {
50375157 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5158 else => |e| return e,
50385159 };
50395160 _ = elf.addGlobalSymbolAssumeCapacity(.{
50405161 .node = .none,
5041 .name = try .string(elf, "__init_array_end"),
5162 .name = "__init_array_end",
50425163 .value = 0,
50435164 .size = 0,
50445165 .type = .NOTYPE,
......@@ -5047,10 +5168,11 @@ fn initHeaders(
50475168 .shndx = .ABS,
50485169 }) catch |err| switch (err) {
50495170 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5171 else => |e| return e,
50505172 };
50515173 _ = elf.addGlobalSymbolAssumeCapacity(.{
50525174 .node = .none,
5053 .name = try .string(elf, "__fini_array_start"),
5175 .name = "__fini_array_start",
50545176 .value = 0,
50555177 .size = 0,
50565178 .type = .NOTYPE,
......@@ -5059,10 +5181,11 @@ fn initHeaders(
50595181 .shndx = .ABS,
50605182 }) catch |err| switch (err) {
50615183 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5184 else => |e| return e,
50625185 };
50635186 _ = elf.addGlobalSymbolAssumeCapacity(.{
50645187 .node = .none,
5065 .name = try .string(elf, "__fini_array_end"),
5188 .name = "__fini_array_end",
50665189 .value = 0,
50675190 .size = 0,
50685191 .type = .NOTYPE,
......@@ -5071,10 +5194,11 @@ fn initHeaders(
50715194 .shndx = .ABS,
50725195 }) catch |err| switch (err) {
50735196 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5197 else => |e| return e,
50745198 };
50755199 _ = elf.addGlobalSymbolAssumeCapacity(.{
50765200 .node = .none,
5077 .name = try .string(elf, "__preinit_array_start"),
5201 .name = "__preinit_array_start",
50785202 .value = 0,
50795203 .size = 0,
50805204 .type = .NOTYPE,
......@@ -5083,10 +5207,11 @@ fn initHeaders(
50835207 .shndx = .ABS,
50845208 }) catch |err| switch (err) {
50855209 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5210 else => |e| return e,
50865211 };
50875212 _ = elf.addGlobalSymbolAssumeCapacity(.{
50885213 .node = .none,
5089 .name = try .string(elf, "__preinit_array_end"),
5214 .name = "__preinit_array_end",
50905215 .value = 0,
50915216 .size = 0,
50925217 .type = .NOTYPE,
......@@ -5095,11 +5220,12 @@ fn initHeaders(
50955220 .shndx = .ABS,
50965221 }) catch |err| switch (err) {
50975222 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5223 else => |e| return e,
50985224 };
50995225 if (have_dynamic) {
51005226 _ = elf.addGlobalSymbolAssumeCapacity(.{
51015227 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
5102 .name = try .string(elf, "_DYNAMIC"),
5228 .name = "_DYNAMIC",
51035229 .value = elf.shndx.dynamic.vaddr(elf),
51045230 .size = 0,
51055231 .type = .NOTYPE,
......@@ -5108,6 +5234,7 @@ fn initHeaders(
51085234 .shndx = elf.shndx.dynamic,
51095235 }) catch |err| switch (err) {
51105236 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5237 else => |e| return e,
51115238 };
51125239 }
51135240 } else {
......@@ -6665,9 +6792,9 @@ fn loadObject(
66656792 ),
66666793 .LOCAL => continue,
66676794 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6668 si.* = elf.addGlobalSymbolAssumeCapacity(.{
6795 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{
66696796 .node = .none,
6670 .name = try .string(elf, name),
6797 .name = name,
66716798 .value = input_sym.value,
66726799 .size = input_sym.size,
66736800 .type = sym_type,
......@@ -6680,7 +6807,8 @@ fn loadObject(
66806807 .shndx = .UNDEF,
66816808 }) catch |err| switch (err) {
66826809 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
6683 };
6810 else => |e| return e,
6811 });
66846812 continue;
66856813 },
66866814 };
......@@ -6705,9 +6833,9 @@ fn loadObject(
67056833 si.* = .local(lsi);
67066834 },
67076835 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6708 si.* = elf.addGlobalSymbolAssumeCapacity(.{
6836 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{
67096837 .node = .wrap(input_section_node),
6710 .name = try .string(elf, name),
6838 .name = name,
67116839 .value = input_sym.value,
67126840 .size = input_sym.size,
67136841 .type = sym_type,
......@@ -6724,7 +6852,8 @@ fn loadObject(
67246852 "multiple definitions of '{s}'",
67256853 .{name},
67266854 ),
6727 };
6855 else => |e| return e,
6856 });
67286857 },
67296858 }
67306859 }
......@@ -7002,9 +7131,13 @@ fn loadDso(
70027131 {
70037132 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
70047133 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);
7005 if (elf.copied_globals.get(name)) |copied_global| {
7006 // We have a copy relocation for this global, but the amount of space we
7007 // reserved for it could be too small or underaligned!
7134
7135 // If there is already a copy relocation for this symbol, the amount of
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
70087141 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
70097142 try copied_global.node.resizeLeaf(
70107143 gpa,
......@@ -7012,11 +7145,10 @@ fn loadDso(
70127145 gop.value_ptr.alignment.forward(gop.value_ptr.size),
70137146 );
70147147 try copied_global.node.realign(gpa, &elf.mf, gop.value_ptr.alignment);
7015 const global_ptr = elf.globalByName(name).?;
7016 switch (elf.symPtr(global_ptr.symtab_index)) {
7148 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
70177149 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
70187150 }
7019 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
7151 switch (elf.dynsymPtr(gsi.ptr(elf).dynsym_index.unwrap().?)) {
70207152 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
70217153 }
70227154 }
......@@ -7032,19 +7164,20 @@ fn loadDso(
70327164
70337165 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
70347166 // its type now.
7035 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
7036 elf.globals.weak_undef.getPtr(name) orelse
7037 continue;
7167 const gsi = elf.globalByName(name.slice(elf)) orelse continue;
7168 if (Symbol.Id.global(gsi).defined(elf)) 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)) {
70427175 // We just found a DSO definition of a symbol for which we wanted a copy
70437176 // relocation, so add one if we can!
7044 _ = try elf.maybeAddCopyRelocation(name);
7177 _ = try elf.maybeAddCopyRelocation(gsi);
70457178 }
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));
70487181 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
70497182
70507183 switch (elf.targetLoad(&sym_ptr.other).visibility) {
......@@ -7064,7 +7197,7 @@ fn loadDso(
70647197 .type = new_type,
70657198 });
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));
70687201 elf.targetStore(&dynsym_ptr.info, .{
70697202 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
70707203 .type = new_type,
......@@ -7072,10 +7205,10 @@ fn loadDso(
70727205
70737206 if (new_type == .FUNC) {
70747207 // 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);
70767209 // ...and therefore, we need to re-apply that symbol's relocations, as
70777210 // some might be targeting its PLT entry.
7078 Symbol.Id.global(name).applyTargetRelocs(elf);
7211 Symbol.Id.global(gsi).applyTargetRelocs(elf);
70797212 }
70807213 }
70817214 }
......@@ -7177,18 +7310,16 @@ fn createInitFiniArraySection(
71777310 });
71787311 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
71797312 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
7180 // These symbols definitely already have strong definitions, because we added them alongside the
7181 // other linker-defined symbols, all the way back in `initHeaders`.
7182 const start_sym_name = try elf.string(.strtab, "__" ++ 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).?, .{
7313 // These symbols definitely already exist with strong definitions, because we added them
7314 // alongside the other linker-defined symbols, all the way back in `initHeaders`.
7315 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_start").?, .{
71857316 .node = .wrap(shndx.get(elf).ni),
71867317 .value = shndx.vaddr(elf),
71877318 .size = 0,
71887319 .type = .NOTYPE,
71897320 .shndx = shndx.*,
71907321 });
7191 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{
7322 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_end").?, .{
71927323 .node = .wrap(shndx.get(elf).ni),
71937324 .value = shndx.vaddr(elf),
71947325 .size = 0,
......@@ -7204,8 +7335,8 @@ fn updateInitFiniArraySectionSize(
72047335 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
72057336 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
72067337 };
7207 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");
7208 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
7338 const end_sym_gsi = elf.globalByName("__" ++ name ++ "_end").?;
7339 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);
72097340}
72107341
72117342pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
......@@ -8265,7 +8396,7 @@ fn addSymbolRelocAssumeCapacity(
82658396 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
82668397 .type = dynamic_reloc_type,
82678398 .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().?,
82698400 .addend = addend,
82708401 }).toOptional();
82718402 },
......@@ -8508,21 +8639,25 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85088639 const sym_value = sym_id.value(elf);
85098640 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
85108641 }
8511 break :val switch (sym_id.unwrap()) {
8512 // For global symbols, just target the right dynsym with no addend.
8513 .global => |name| .{ .reloc = .{
8514 .type = .tpOff(elf),
8515 .dynsym_index = elf.globalByName(name).?.dynsym_index,
8516 .addend = 0,
8517 } },
8518 // For local symbols, target the null symbol (index 0) so we get the offset to the
8519 // base of our TLS block, and then use `addend` to offset to the right symbol.
8520 .local => .{ .reloc = .{
8521 .type = .tpOff(elf),
8522 .dynsym_index = 0,
8523 .addend = @intCast(sym_id.value(elf)),
8524 } },
8525 };
8642 switch (sym_id.unwrap()) {
8643 .global => |gsi| if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {
8644 // When there is a dynsym entry, just target that with no addend.
8645 break :val .{ .reloc = .{
8646 .type = .tpOff(elf),
8647 .dynsym_index = dynsym_index,
8648 .addend = 0,
8649 } };
8650 },
8651 .local => {},
8652 }
8653 // Otherwise, we know the offset of the symbol into our own TLS block, so target the
8654 // null symbol (index 0) so we get the offset to the base of that block, and use the
8655 // addend to offset to the correct symbol.
8656 break :val .{ .reloc = .{
8657 .type = .tpOff(elf),
8658 .dynsym_index = 0,
8659 .addend = @intCast(sym_id.value(elf)),
8660 } };
85268661 },
85278662 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
85288663 .static => .{ .unsigned = sym.value(elf) },
......@@ -8533,7 +8668,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85338668 } },
85348669 .dynamic => .{ .reloc = .{
85358670 .type = .globDat(elf),
8536 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
8671 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
85378672 .addend = 0,
85388673 } },
85398674 },
......@@ -8542,7 +8677,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85428677 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
85438678 .dynamic => .{ .reloc = .{
85448679 .type = .dtpOff(elf),
8545 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
8680 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
85468681 .addend = 0,
85478682 } },
85488683 },
......@@ -8555,7 +8690,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85558690 .type = .dtpMod(elf),
85568691 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
85578692 .static, .static_relative => 0,
8558 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
8693 .dynamic => sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
85598694 },
85608695 .addend = 0,
85618696 } },
......@@ -8641,8 +8776,8 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
86418776/// If this function creates a new copy relocation, it will also update relocations targeting the
86428777/// global where needed---the caller does not need to do this.
86438778///
8644/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global.
8645fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
8779/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `gsi` refers to an *undefined* global.
8780fn maybeAddCopyRelocation(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
86468781 assert(elf.shndx.dynamic != .UNDEF);
86478782
86488783 // Only dynamic executables may contain `R_*_COPY` relocations.
......@@ -8650,23 +8785,22 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
86508785
86518786 const gpa = elf.base.comp.gpa;
86528787
8653 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse
8654 elf.globals.weak_undef.getPtr(global_name).?;
8788 assert(!Symbol.Id.global(gsi).defined(elf));
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 {
86598793 // We do not have a definition to provide the correct size for the symbol. If a definition
86608794 // 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, {});
86628796 return false;
86638797 };
86648798
86658799 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);
86688802 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
86718805 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
86728806
......@@ -8676,7 +8810,7 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
86768810 .size = dso_global.alignment.forward(dso_global.size),
86778811 .alignment = dso_global.alignment,
86788812 }),
8679 .{ .copied_global = global_name },
8813 .{ .copied_global = gsi },
86808814 );
86818815 errdefer comptime unreachable;
86828816
......@@ -8684,7 +8818,7 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
86848818 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
86858819 .type = .copy(elf),
86868820 .offset = vaddr,
8687 .raw_sym_index = global_ptr.dynsym_index,
8821 .raw_sym_index = dynsym_index,
86888822 .addend = 0,
86898823 });
86908824 gop.value_ptr.* = .{
......@@ -8692,18 +8826,18 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
86928826 .rela_index = rela_index,
86938827 };
86948828
8695 switch (elf.symPtr(global_ptr.symtab_index)) {
8829 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
86968830 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
86978831 }
8698 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
8832 switch (elf.dynsymPtr(dynsym_index)) {
86998833 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
87008834 }
87018835
87028836 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
87038837 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
87048838 // and then update the symbol's address (and re-apply relocations targeting it of course).
8705 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
8706 Symbol.Id.global(global_name).flushMoved(elf, vaddr);
8839 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);
8840 Symbol.Id.global(gsi).flushMoved(elf, vaddr);
87078841
87088842 return true;
87098843}
......@@ -9242,14 +9376,11 @@ fn flushInner(
92429376
92439377 try elf.flushFiles();
92449378
9245 if (comp.config.output_mode == .Exe) {
9246 var any_undef = false;
9247 for (elf.globals.strong_undef.keys()) |name| {
9248 if (elf.dso_globals.contains(name)) continue;
9249 any_undef = true;
9250 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
9379 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {
9380 for (elf.unknown_globals.keys()) |gsi| {
9381 diags.addError("undefined global symbol '{s}'", .{Symbol.Id.global(gsi).name(elf).slice(elf)});
92519382 }
9252 if (any_undef) return error.AlreadyReported;
9383 return error.AlreadyReported;
92539384 }
92549385
92559386 try elf.prepareDynamic();
......@@ -9289,7 +9420,7 @@ fn flushInner(
92899420 elf.flushDynamic();
92909421
92919422 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) {
92939424 .default => switch (comp.config.output_mode) {
92949425 .Exe => continue :name .enabled,
92959426 .Lib, .Obj => continue :name .disabled,
......@@ -9298,9 +9429,8 @@ fn flushInner(
92989429 .enabled => "_start",
92999430 .named => |named| named,
93009431 };
9301 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);
9302 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
9303 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
9432 const gsi = elf.globalByName(sym_name) orelse break :entry 0;
9433 break :entry Symbol.Id.global(gsi).value(elf);
93049434 };
93059435 switch (elf.ehdrPtr()) {
93069436 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 {
93699499 break :task;
93709500 }
93719501 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);
93739505 defer idle_prog_node.end();
93749506
9375 const global_name = kv.key;
9376 const global = elf.globalByName(global_name).?;
9377 const sym_id: Symbol.Id = .global(global_name);
9378 const sym = global.symtab_index.ptr(elf);
9507 const sym_id: Symbol.Id = .global(gsi);
9508 const symtab_index = gsi.ptr(elf).symtab_index;
93799509
93809510 switch (elf.ehdrType()) {
93819511 .REL => {
93829512 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
93839513 // all of the output relocations.
9384 const symtab_index = @backingInt(global.symtab_index);
9385 var ri = sym.first_target_reloc;
9514 var ri = symtab_index.ptr(elf).first_target_reloc;
93869515 while (ri != .none) {
93879516 const reloc = ri.get(elf);
93889517 assert(reloc.target == sym_id);
93899518 // In relocatables, every symbol relocation has an output relocation.
93909519 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));
93929521 ri = reloc.next;
93939522 }
93949523 },
......@@ -9401,29 +9530,32 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
94019530 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
94029531 // don't care about the dynsym index). The only exception is a copy reloc
94039532 // could exist (and be the *reason* the symbol value is statically known).
9404 if (elf.copied_globals.get(global_name)) |copied| {
9405 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);
9533 if (elf.copied_globals.get(gsi)) |copied| {
9534 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
9535 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);
94069536 }
94079537 },
94089538 .dynamic => {
9409 assert(!elf.copied_globals.contains(global_name)); // value would be statically known
9539 assert(!elf.copied_globals.contains(gsi)); // value would be statically known
9540
9541 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
94109542
94119543 // Update symbol relocs:
9412 var ri = sym.first_target_reloc;
9544 var ri = symtab_index.ptr(elf).first_target_reloc;
94139545 while (ri != .none) {
94149546 const reloc = ri.get(elf);
94159547 assert(reloc.target == sym_id);
94169548 // There may or may not be a runtime relocation for this symbol reloc.
94179549 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);
94199551 }
94209552 ri = reloc.next;
94219553 }
94229554
94239555 // 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| {
94259557 // 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);
94279559 }
94289560
94299561 // Update relocs for any relevant GOT entries:
......@@ -9882,13 +10014,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
988210014 }
988310015
988410016 // Update global symbols targeting this section
9885 if (elf.node_global_symbols.get(ni)) |first_name| {
9886 assert(first_name != .empty);
9887 var name = first_name;
9888 while (name != .empty) {
9889 const old_sym_addr = Symbol.Id.global(name).value(elf);
9890 Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr);
9891 name = elf.globalByName(name).?.next_in_node;
10017 if (elf.node_global_symbols.get(ni)) |first_gsi| {
10018 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
10019 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
10020 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
10021 Symbol.Id.global(gsi).flushMoved(elf, old_sym_addr - old_addr + addr);
989210022 }
989310023 }
989410024
......@@ -9950,16 +10080,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
995010080 }
995110081
995210082 // Update global symbols
9953 if (elf.node_global_symbols.get(ni)) |first_name| {
9954 assert(first_name != .empty);
9955 var name = first_name;
9956 while (name != .empty) {
9957 const old_sym_addr = Symbol.Id.global(name).value(elf);
9958 Symbol.Id.global(name).flushMoved(
10083 if (elf.node_global_symbols.get(ni)) |first_gsi| {
10084 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
10085 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
10086 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
10087 Symbol.Id.global(gsi).flushMoved(
995910088 elf,
996010089 old_sym_addr - old_section_addr + new_section_addr,
996110090 );
9962 name = elf.globalByName(name).?.next_in_node;
996310091 }
996410092 }
996510093
......@@ -9984,12 +10112,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
998410112 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {
998510113 const new_addr = elf.computeNodeVAddr(ni);
998610114 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
9987 if (elf.node_global_symbols.get(ni)) |first_name| {
9988 assert(first_name != .empty);
9989 var name = first_name;
9990 while (name != .empty) {
9991 Symbol.Id.global(name).flushMoved(elf, new_addr);
9992 name = elf.globalByName(name).?.next_in_node;
10115 if (elf.node_global_symbols.get(ni)) |first_gsi| {
10116 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
10117 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
10118 Symbol.Id.global(gsi).flushMoved(elf, new_addr);
999310119 }
999410120 }
999510121 elf.flushMovedNodeRelocs(ni, new_addr, .{
......@@ -10722,7 +10848,8 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1072210848 }
1072310849}
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 {
1072610853 const target_endian = elf.targetEndian();
1072710854
1072810855 // 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
1073010857 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
1073110858 .type = .jumpSlot(elf),
1073210859 .offset = 0, // populated later
10733 .raw_sym_index = dynsym_index,
10860 .raw_sym_index = gsi.ptr(elf).dynsym_index.unwrap().?,
1073410861 .addend = 0,
1073510862 }));
1073610863
......@@ -10751,13 +10878,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1075110878
1075210879 if (plt_index < elf.plt.count()) {
1075310880 // 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);
1075510882 return;
1075610883 }
1075710884
1075810885 // We added a new entry, so we now need to extend the PLT sections.
1075910886 assert(plt_index == elf.plt.count());
10760 elf.plt.putAssumeCapacityNoClobber(global_name, {});
10887 elf.plt.putAssumeCapacityNoClobber(gsi, {});
1076110888
1076210889 switch (elf.ehdrMachine()) {
1076310890 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
......@@ -10929,8 +11056,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
1092911056 // its relocations are probably going through the PLT, so we don't bother with
1093011057 // specific tracking for PLT relocations---instead just re-apply all relocations
1093111058 // targeting symbols with PLT entries.
10932 for (elf.plt.keys()) |name| {
10933 Symbol.Id.global(name).applyTargetRelocs(elf);
11059 for (elf.plt.keys()) |gsi| {
11060 Symbol.Id.global(gsi).applyTargetRelocs(elf);
1093411061 }
1093511062 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
1093611063 // 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
1098611113 // its relocations are probably going through the PLT, so we don't bother with
1098711114 // specific tracking for PLT relocations---instead just re-apply all relocations
1098811115 // targeting symbols with PLT entries.
10989 for (elf.plt.keys()) |name| {
10990 Symbol.Id.global(name).applyTargetRelocs(elf);
11116 for (elf.plt.keys()) |gsi| {
11117 Symbol.Id.global(gsi).applyTargetRelocs(elf);
1099111118 }
1099211119 // We also need to update all of the references from `.plt` to `.got.plt`.
1099311120 // 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
1105311180 // its relocations are probably going through the PLT, so we don't bother with
1105411181 // specific tracking for PLT relocations---instead just re-apply all relocations
1105511182 // targeting symbols with PLT entries.
11056 for (elf.plt.keys()) |name| {
11057 Symbol.Id.global(name).applyTargetRelocs(elf);
11183 for (elf.plt.keys()) |gsi| {
11184 Symbol.Id.global(gsi).applyTargetRelocs(elf);
1105811185 }
1105911186 // Update the offsets of the relocation entries in `.rela.plt`.
1106011187 const rela_plt_shndx = elf.shndx.rela_plt;
......@@ -11118,7 +11245,7 @@ fn updateExportInner(
1111811245 const name = @"export".opts.name.toSlice(ip);
1111911246 _ = elf.addGlobalSymbolAssumeCapacity(.{
1112011247 .node = exported_lsi.index().ptr(elf).node,
11121 .name = try .string(elf, name),
11248 .name = name,
1112211249 .value = cur_value,
1112311250 .size = cur_size,
1112411251 .type = @"type",
......@@ -11140,8 +11267,8 @@ fn updateExportInner(
1114011267 // get these errors on every non-initial incremental update. Hack around that by
1114111268 // only emitting this error if the symbol we're conflicting with comes from an input
1114211269 // section (as opposed to the ZCU).
11143 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
11144 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
11270 const conflicting_global = elf.globalByName(name).?;
11271 if (conflicting_global.ptr(elf).symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
1114511272 if (elf.getNode(conflicting_node) == .input_section) {
1114611273 return elf.base.comp.link_diags.fail(
1114711274 "multiple definitions of '{s}'",
......@@ -11150,6 +11277,7 @@ fn updateExportInner(
1115011277 }
1115111278 }
1115211279 },
11280 else => |e| return e,
1115311281 };
1115411282}
1115511283
......@@ -11214,7 +11342,7 @@ pub fn printNode(
1121411342 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
1121511343 });
1121611344 },
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)}),
1121811346 .nav => |nmi| {
1121911347 const zcu = elf.base.comp.zcu.?;
1122011348 const ip = &zcu.intern_pool;
......@@ -11402,7 +11530,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato
1140211530fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
1140311531 const index = switch (sym.unwrap()) {
1140411532 .local => return null,
11405 .global => |name| elf.plt.getIndex(name) orelse return null,
11533 .global => |gsi| elf.plt.getIndex(gsi) orelse return null,
1140611534 };
1140711535 if (elf.pltEntryIsDead(index)) return null;
1140811536 const plt = elf.targetPltInfo();