authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-22 23:53:09+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-22 23:53:09+02:00
log77d6113cfbf54691ca16b01d6fe1e543384d0dca
tree8eadba79feec0a204dc962ca1473fc9f0a8c4e21
parentb3eb800e2fcc1c930209d1f42be0050bd3d9032f
parent82d389279cad3773d456860a5eedca494d301f9b

Merge pull request 'Elf2: symbol versioning (and a few bugfixes)' (#36924) from mlugg/elf2-goodies into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36924

5 files changed, 2395 insertions(+), 1022 deletions(-)

lib/std/elf.zig+9
...@@ -1565,6 +1565,7 @@ pub const Verdaux = extern struct {...@@ -1565,6 +1565,7 @@ pub const Verdaux = extern struct {
1565 name: Word,1565 name: Word,
1566 next: Word,1566 next: Word,
1567};1567};
1568/// Deprecated, use `std.elf.Verneed`
1568pub const Elf32_Verneed = extern struct {1569pub const Elf32_Verneed = extern struct {
1569 vn_version: Half,1570 vn_version: Half,
1570 vn_cnt: Half,1571 vn_cnt: Half,
...@@ -1572,6 +1573,7 @@ pub const Elf32_Verneed = extern struct {...@@ -1572,6 +1573,7 @@ pub const Elf32_Verneed = extern struct {
1572 vn_aux: Word,1573 vn_aux: Word,
1573 vn_next: Word,1574 vn_next: Word,
1574};1575};
1576/// Deprecated, use `std.elf.Verneed`
1575pub const Elf64_Verneed = extern struct {1577pub const Elf64_Verneed = extern struct {
1576 vn_version: Half,1578 vn_version: Half,
1577 vn_cnt: Half,1579 vn_cnt: Half,
...@@ -1579,6 +1581,13 @@ pub const Elf64_Verneed = extern struct {...@@ -1579,6 +1581,13 @@ pub const Elf64_Verneed = extern struct {
1579 vn_aux: Word,1581 vn_aux: Word,
1580 vn_next: Word,1582 vn_next: Word,
1581};1583};
1584pub const Verneed = extern struct {
1585 version: Half,
1586 cnt: Half,
1587 file: Word,
1588 aux: Word,
1589 next: Word,
1590};
1582pub const Vernaux = extern struct {1591pub const Vernaux = extern struct {
1583 hash: Word,1592 hash: Word,
1584 flags: Half,1593 flags: Half,
src/Compilation/Config.zig+8
...@@ -413,6 +413,12 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -413,6 +413,12 @@ pub fn resolve(options: Options) ResolveError!Config {
413 // assume no LLD.413 // assume no LLD.
414 if (options.use_new_linker == true) break :b false;414 if (options.use_new_linker == true) break :b false;
415415
416 if (options.use_new_linker == null and
417 target_util.preferNewLinkerOverLld(target))
418 {
419 break :b false;
420 }
421
416 // If we have no zig code to compile, no need for the self-hosted linker.422 // If we have no zig code to compile, no need for the self-hosted linker.
417 if (!options.have_zcu) break :b true;423 if (!options.have_zcu) break :b true;
418424
...@@ -448,6 +454,8 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -448,6 +454,8 @@ pub fn resolve(options: Options) ResolveError!Config {
448454
449 if (options.use_new_linker) |x| break :b x;455 if (options.use_new_linker) |x| break :b x;
450456
457 if (target_util.preferNewLinkerOverLld(target)) break :b true;
458
451 break :b options.incremental;459 break :b options.incremental;
452 };460 };
453461
src/link/Dwarf2.zig+3-6
...@@ -1855,12 +1855,9 @@ pub fn genDebugInfoHeader(...@@ -1855,12 +1855,9 @@ pub fn genDebugInfoHeader(
1855 try dwarf.abbrevCode(dih_nw, .module);1855 try dwarf.abbrevCode(dih_nw, .module);
1856 try dwarf.strx(dih_nw, mod.fully_qualified_name);1856 try dwarf.strx(dih_nw, mod.fully_qualified_name);
1857 try dih_w.writeUleb128(0);1857 try dih_w.writeUleb128(0);
1858 try dwarf.genModuleDependency(1858 if (zcu.builtin_modules.get(mod.getBuiltinOptions(comp.config).hash())) |builtin_mod| {
1859 dih_nw,1859 try dwarf.genModuleDependency(dih_nw, "builtin", builtin_mod, module_offset);
1860 "builtin",1860 }
1861 zcu.builtin_modules.get(mod.getBuiltinOptions(comp.config).hash()).?,
1862 module_offset,
1863 );
1864 try dwarf.genModuleDependency(dih_nw, "root", zcu.root_mod, module_offset);1861 try dwarf.genModuleDependency(dih_nw, "root", zcu.root_mod, module_offset);
1865 try dwarf.genModuleDependency(dih_nw, "std", zcu.std_mod, module_offset);1862 try dwarf.genModuleDependency(dih_nw, "std", zcu.std_mod, module_offset);
1866 for (mod.deps.keys(), mod.deps.values()) |name, dep|1863 for (mod.deps.keys(), mod.deps.values()) |name, dep|
src/link/Elf2.zig+2364-1016
...@@ -52,6 +52,9 @@ shndx: struct {...@@ -52,6 +52,9 @@ shndx: struct {
52 tdata: Section.Index,52 tdata: Section.Index,
53 rela_dyn: Section.Index,53 rela_dyn: Section.Index,
54 rela_plt: Section.Index,54 rela_plt: Section.Index,
55 gnu_version: Section.Index,
56 gnu_version_d: Section.Index,
57 gnu_version_r: Section.Index,
55 debug_abbrev: Section.Index,58 debug_abbrev: Section.Index,
56 debug_addr: Section.Index,59 debug_addr: Section.Index,
57 eh_frame_hdr: Section.Index,60 eh_frame_hdr: Section.Index,
...@@ -75,55 +78,63 @@ dynamic: struct {...@@ -75,55 +78,63 @@ dynamic: struct {
75 soname: String(.dynstr),78 soname: String(.dynstr),
76},79},
77symtab: std.ArrayList(Symbol),80symtab: std.ArrayList(Symbol),
78globals: struct {81globals: std.ArrayList(Symbol.Global),
79 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),82/// Accessed with `Symbol.Global.VersionedName.Adapter`. Items map 1--1 to `globals`.
80 weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),83globals_by_name: std.array_hash_map.Custom(void, void, void, true),
81 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),84/// Accessed with `Symbol.Global.DefaultVersionAdapter`.
82 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),85default_version_globals: std.array_hash_map.Custom(Symbol.Global.Index, void, void, true),
83},86/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We
84/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).87/// maintain this set to allow efficient reporting of "undefined global symbol" errors.
85copied_globals: std.array_hash_map.Auto(String(.strtab), struct {88unknown_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
89/// Key is a global which has multiple definitions, one an actual definition for that global, and
90/// the other because it is an alias for another global (see `Symbol.Global.Index.resolveAlias`).
91defined_alias_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
92/// Key is an undef global for which we have created a "copy relocation" (`R_*_COPY`).
93copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, struct {
86 node: MappedFile.Node.Index,94 node: MappedFile.Node.Index,
87 /// The index of this global's runtime relocation in `.rela.dyn`.95 /// The index of this global's runtime relocation in `.rela.dyn`.
88 rela_index: Section.RelaIndex,96 rela_index: Section.RelaIndex,
89}),97}),
90/// Key is the name of an undef global for which we would *like* to create a copy relocation98/// When rearranging entries in the dynamic symbol table, we need to map a dynamic symbol index to
91/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.99/// the corresponding `Symbol.Global.Index` which "owns" that dynsym entry. Usually this is usually
100/// by simply looking up the dynamic symbol's name in `globals_by_name`, but versioned symbols may
101/// have different names in the dynamic symbol table than the normal symbol table. If they do, they
102/// are added to this map, which is checked in `Elf.globalByDynsym` before doing the aforementioned
103/// name lookup.
92///104///
93/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we105/// Key is index into `.dynsym`.
94/// will remove it from this map and call `maybeAddCopyRelocation`.106versioned_dynsym_owners: std.array_hash_map.Auto(u32, Symbol.Global.Index),
95want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),107/// Accessed with `VerdefAdapter`. Entries in `.gnu.version_d` are contiguous verdef/verdaux pairs
96/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol108/// (see `VerdefEntry`), and the "base" version is not in this map, so a given `index` in this map
97/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.109/// refers to the version defined at offset `(index + 1) * @sizeOf(VerdefEntry)` into the section.
110verdef: std.array_hash_map.Custom(void, void, void, true),
111/// Accessed with `VerneedAdapter`. Entries in `.gnu.version_r` are either `std.elf.Verneed` or
112/// `std.elf.Vernaux`---these types are the same size, and the entry at a given `index` in this map
113/// refers to the verneed/vernaux at offset `index * @sizeOf(VerneedEntry)`. The stored key
114/// indicates whether the entry is a verneed or a vernaux.
115verneed: std.array_hash_map.Custom(VerneedAdapter.StoredKey, void, void, true),
116/// Index into `Elf.verneed` of the last entry which represented a file. Tracked so that new files
117/// can be added to the linked list in the verneed section.
118last_verneed_file_index: usize,
119/// The next unused symbol version ID, referenced by entries in the `.gnu.version` section and
120/// defined by entries in the `.gnu.version_d` and `.gnu.version_r` sections. Initially 2, because
121/// 0 and 1 are reserved. When `verdefId` or `verneedId` adds a new version, they will assign it
122/// the ID stored here (and increment this field).
98///123///
99/// Value is never `.empty`.124/// Note that documentation and ELF headers call these "version indices" rather than "version IDs",
125/// but that's a misnomer: they are opaque IDs with no requirement to be contiguous or to be defined
126/// in order. That said, dynamic linker implementations may internally create a version lookup table
127/// indexed by the ID, so for efficiency it's good to keep them dense.
128next_version_id: u16,
129/// Key is a node which is a valid `Symbol.node` value, value is the first global symbol in that
130/// node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
100///131///
101/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,132/// 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.133/// 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)),134node_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:135/// See the definition of `DsoGlobals`.
105///136dso_globals: DsoGlobals,
106/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has137
107/// type `STT_FUNC`, in which case we will create a PLT entry.
108///
109/// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these
110/// symbols, we know whether the symbol has type `STT_OBJECT` and we know its size and alignment,
111/// so we can emit a copy relocation for that symbol instead of using a text relocation.
112///
113/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
114/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
115dso_globals: std.array_hash_map.Auto(String(.strtab), struct {
116 type: std.elf.STT,
117 size: u64,
118 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and so
119 /// becomes a part of the executable's address space despite being defined by a different DSO,
120 /// we need to know its alignment requirement so that we don't break other code. This isn't
121 /// actually stored on the symbol---instead we compute a maximum alignment from the alignment of
122 /// the section containing the symbol, and the symbol's offset within the section. I know this
123 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy
124 /// relocations suck.
125 alignment: Alignment,
126}),
127shstrtab: StringTable,138shstrtab: StringTable,
128strtab: StringTable,139strtab: StringTable,
129dynstr: StringTable,140dynstr: StringTable,
...@@ -132,7 +143,7 @@ dynstr: StringTable,...@@ -132,7 +143,7 @@ dynstr: StringTable,
132///143///
133/// Value is the output relocation in `.rela.dyn` for the GOT entry.144/// Value is the output relocation in `.rela.dyn` for the GOT entry.
134got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),145got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
135/// Key is the name of a global.146/// Key is a global with a PLT entry.
136///147///
137/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into148/// 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 runtime149/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
...@@ -140,7 +151,7 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),...@@ -140,7 +151,7 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
140///151///
141/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is152/// 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.153/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
143plt: std.array_hash_map.Auto(String(.strtab), void),154plt: std.array_hash_map.Auto(Symbol.Global.Index, void),
144/// The `.plt` section contains zero or more symbol relocations starting at this index.155/// The `.plt` section contains zero or more symbol relocations starting at this index.
145plt_first_symbol_reloc: SymbolReloc.Index,156plt_first_symbol_reloc: SymbolReloc.Index,
146/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.157/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.
...@@ -203,12 +214,13 @@ got_relocs: std.ArrayList(GotReloc),...@@ -203,12 +214,13 @@ got_relocs: std.ArrayList(GotReloc),
203tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),214tls_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`.215/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
205section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),216section_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 relocation217/// 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.218/// entries which target that symbol must be updated to reference the correct symbol index.
208///219///
209/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it220/// * In an `ET_REL`, this means the index in `.symtab` has changed.
210/// refers to the index in `.dynsym`.221/// * 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),222/// * Otherwise this is always empty.
223changed_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`224/// 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`225/// 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`.226/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
...@@ -271,14 +283,14 @@ const Node = union(enum) {...@@ -271,14 +283,14 @@ const Node = union(enum) {
271 section_manual_size: Section.Index,283 section_manual_size: Section.Index,
272 /// May contain relocations.284 /// May contain relocations.
273 input_section: InputSection.Index,285 input_section: InputSection.Index,
274 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for286 /// 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.287 /// emitted a copy relocation.
276 ///288 ///
277 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.289 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
278 ///290 ///
279 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because291 /// 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`.292 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `updateGlobalDynamic`.
281 copied_global: String(.strtab),293 copied_global: Symbol.Global.Index,
282 /// May contain relocations.294 /// May contain relocations.
283 nav: NavMapIndex,295 nav: NavMapIndex,
284 /// May contain relocations.296 /// May contain relocations.
...@@ -1726,9 +1738,7 @@ const SymbolReloc = struct {...@@ -1726,9 +1738,7 @@ const SymbolReloc = struct {
1726 break :type reloc.target.value(elf) +% addend -% tls_size;1738 break :type reloc.target.value(elf) +% addend -% tls_size;
1727 },1739 },
1728 },1740 },
1729 .size => switch (elf.symPtr(reloc.target.index(elf))) {1741 .size => reloc.target.size(elf),
1730 inline else => |sym| elf.targetLoad(&sym.size),
1731 },
1732 .special => return reloc.type.action.special.applyInner(1742 .special => return reloc.type.action.special.applyInner(
1733 elf,1743 elf,
1734 reloc.target,1744 reloc.target,
...@@ -2179,53 +2189,152 @@ const GotReloc = struct {...@@ -2179,53 +2189,152 @@ const GotReloc = struct {
2179 }2189 }
2180};2190};
21812191
2182fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {2192/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
2183 const gpa = elf.base.comp.gpa;2193/// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one
2194/// of these symbols ends up being referenced by the ELF module we are producing, we need this
2195/// information for a few reasons:
2196///
2197/// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a
2198/// PLT entry for it.
2199///
2200/// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy
2201/// relocation for it if necessary.
2202///
2203/// * If an undefined versioned symbol has an external definition, we can add the required entry to
2204/// `.gnu.version_r` because we know the soname of the DSO defining it.
2205///
2206/// * Similarly, if an undefined unversioned symbol matches an external definition of a default
2207/// symbol version, we know to emit the default version for the dynamic symbol table entry even
2208/// though that symbol version was not explicitly requested.
2209///
2210/// * If an undefined symbol does *not* have any external definition, when emitting a dynamic
2211/// executable, we can emit an "undefined global symbol" link error for it.
2212const DsoGlobals = struct {
2213 string_bytes: std.ArrayList(u8),
21842214
2185 try elf.symtab.ensureUnusedCapacity(gpa, len);2215 /// Contains all global symbols defined in any needed DSO.
2216 ///
2217 /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`.
2218 symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true),
21862219
2187 // If adding locals, we may need to move one global out of the way for each local. If adding2220 /// Contains all default-version global symbols defined in any needed DSO.
2188 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals2221 ///
2189 // around to keep `.dynsym` compact. Either way, the maximum is N.2222 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
2190 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);2223 ///
2224 /// Stored key is an index into `DsoGlobals.symbols`.
2225 default_sym_vers: std.array_hash_map.Custom(u32, void, void, true),
21912226
2192 {2227 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2193 // Ensure the symtab section's node is big enough2228 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2194 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {2229 const String = enum(u32) {
2195 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),2230 _,
2231 fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 {
2232 const overlong = dso_globals.string_bytes.items[@backingInt(s)..];
2233 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
2234 }
2235 const Optional = enum(u32) {
2236 none = std.math.maxInt(u32),
2237 _,
2238 fn unwrap(os: DsoGlobals.String.Optional) ?DsoGlobals.String {
2239 return switch (os) {
2240 .none => null,
2241 _ => @fromBackingInt(@backingInt(os)),
2242 };
2243 }
2244 fn wrap(s: DsoGlobals.String) DsoGlobals.String.Optional {
2245 return @bitCast(s);
2246 }
2196 };2247 };
2197 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_node_size);2248 };
2198 }
2199
2200 switch (kind) {
2201 .all_local => {},
2202 .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);
22072249
2208 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);2250 const Symbol = struct {
2251 name: DsoGlobals.String,
2252 /// `.none` if the symbol is unversioned.
2253 version: DsoGlobals.String.Optional,
2254 type: std.elf.STT,
2255 soname: Elf.String(.dynstr),
2256 size: u64,
2257 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and
2258 /// so becomes a part of the executable's address space despite being defined by a different
2259 /// DSO, we need to know its alignment requirement so that we don't break other code. This
2260 /// isn't actually stored on the symbol---instead we compute a maximum alignment from the
2261 /// alignment of the section containing the symbol, and the symbol's offset within the
2262 /// section. I know this sounds like a terrible hack, but it is *genuinely* how you're
2263 /// supposed to do this. Copy relocations suck.
2264 alignment: Alignment,
2265
2266 const Key = struct {
2267 name: []const u8,
2268 version: ?[]const u8,
2269 };
22092270
2210 if (elf.shndx.dynsym != .UNDEF) {2271 const Adapter = struct {
2211 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {2272 dso_globals: *const DsoGlobals,
2212 inline else => |shdr, class| .{2273 pub fn eql(ctx: Adapter, lhs_key: Key, rhs_symbol: DsoGlobals.Symbol, _: usize) bool {
2213 elf.targetLoad(&shdr.size),2274 const dso_globals = ctx.dso_globals;
2214 @sizeOf(class.ElfN().Sym),2275 const rhs_name = rhs_symbol.name.slice(dso_globals);
2215 },2276 if (!std.mem.eql(u8, lhs_key.name, rhs_name)) return false;
2216 };2277 if (lhs_key.version) |lhs_version| {
2217 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));2278 const rhs_version = rhs_symbol.version.unwrap() orelse return false;
2279 if (!std.mem.eql(u8, lhs_version, rhs_version.slice(dso_globals))) return false;
2280 } else {
2281 if (rhs_symbol.version != .none) return false;
2282 }
2283 return true;
2284 }
2285
2286 pub fn hash(ctx: Adapter, key: Key) u32 {
2287 _ = ctx;
2288 var h: std.hash.Wyhash = .init(key.name.len);
2289 h.update(key.name);
2290 if (key.version) |version| {
2291 h.update(&.{1});
2292 h.update(version);
2293 } else {
2294 h.update(&.{0});
2295 }
2296 return @truncate(h.final());
2297 }
2298 };
22182299
2219 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;2300 const DefaultVersionAdapter = struct {
2220 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, dynsym_need_size);2301 dso_globals: *const DsoGlobals,
2302 pub fn eql(ctx: DefaultVersionAdapter, lhs_name: []const u8, rhs_symbol_index: u32, _: usize) bool {
2303 const rhs_symbol = &ctx.dso_globals.symbols.keys()[rhs_symbol_index];
2304 return std.mem.eql(u8, lhs_name, rhs_symbol.name.slice(ctx.dso_globals));
2305 }
2306 pub fn hash(ctx: DefaultVersionAdapter, name: []const u8) u32 {
2307 _ = ctx;
2308 return std.array_hash_map.hashString(name);
2309 }
2310 };
2311 };
22212312
2222 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);2313 fn find(dso_globals: *const DsoGlobals, name: []const u8, version: ?[]const u8) ?*const DsoGlobals.Symbol {
2314 if (dso_globals.symbols.getIndexAdapted(@as(DsoGlobals.Symbol.Key, .{
2315 .name = name,
2316 .version = version,
2317 }), @as(DsoGlobals.Symbol.Adapter, .{
2318 .dso_globals = dso_globals,
2319 }))) |index| {
2320 return &dso_globals.symbols.keys()[index];
2321 } else {
2322 return null;
2323 }
2324 }
22232325
2224 try elf.ensureUnusedPltCapacity(len);2326 fn findDefaultVersion(dso_globals: *const DsoGlobals, name: []const u8) ?*const DsoGlobals.Symbol {
2225 }2327 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
2226 },2328 .dso_globals = dso_globals,
2329 };
2330 if (dso_globals.default_sym_vers.getIndexAdapted(name, adapter)) |default_index| {
2331 const symbol_index = dso_globals.default_sym_vers.keys()[default_index];
2332 return &dso_globals.symbols.keys()[symbol_index];
2333 } else {
2334 return null;
2335 }
2227 }2336 }
2228}2337};
22292338
2230fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {2339fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
2231 const gpa = elf.base.comp.gpa;2340 const gpa = elf.base.comp.gpa;
...@@ -2283,10 +2392,10 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {...@@ -2283,10 +2392,10 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
2283 chains[0] = 0;2392 chains[0] = 0;
2284 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {2393 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
2285 const dynsym_index: u32 = @intCast(dynsym_index_usize);2394 const dynsym_index: u32 = @intCast(dynsym_index_usize);
2286 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {2395 const dynsym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2287 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),2396 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2288 };2397 };
2289 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;2398 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.2399 // Make this symbol the head of that bucket, and chain to the old head.
2291 chain.* = buckets[b];2400 chain.* = buckets[b];
2292 elf.targetStore(&buckets[b], dynsym_index);2401 elf.targetStore(&buckets[b], dynsym_index);
...@@ -2326,10 +2435,10 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {...@@ -2326,10 +2435,10 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2326 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];2435 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))];2436 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
23282437
2329 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {2438 const dynsym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2330 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),2439 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2331 };2440 };
2332 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;2441 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.2442 // Make this symbol the head of that bucket, and chain to the old head.
2334 chains[dynsym_index] = buckets[b];2443 chains[dynsym_index] = buckets[b];
2335 elf.targetStore(&buckets[b], dynsym_index);2444 elf.targetStore(&buckets[b], dynsym_index);
...@@ -2367,10 +2476,10 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {...@@ -2367,10 +2476,10 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2367 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];2476 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))];2477 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
23692478
2370 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {2479 const dynsym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2371 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),2480 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2372 };2481 };
2373 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;2482 const b = std.elf.hash.calculate(dynsym_name.slice(elf)) % buckets.len;
23742483
2375 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);2484 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
2376 elf.targetStore(&chains[dynsym_index], 0);2485 elf.targetStore(&chains[dynsym_index], 0);
...@@ -2393,34 +2502,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {...@@ -2393,34 +2502,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2393 }2502 }
2394}2503}
23952504
2396fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
2397 const gpa = elf.base.comp.gpa;
2398
2399 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
2400
2401 try elf.plt.ensureUnusedCapacity(gpa, len);
2402 const need_plt_count = elf.plt.count() + len;
2403
2404 const plt = elf.targetPltInfo();
2405
2406 // Ensure the `.plt` section's node is big enough:
2407 {
2408 const need_size: usize = plt.entry_size * (1 + need_plt_count);
2409 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2410 }
2411
2412 // If there is a `.got.plt` section, ensure its node is big enough
2413 if (plt.got_plt) |got_plt| {
2414 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
2415 try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2416 }
2417
2418 // If there is a `.plt.sec` section, ensure its node is big enough
2419 if (plt.plt_sec) |plt_sec| {
2420 const need_size: usize = plt_sec.entry_size * need_plt_count;
2421 try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2422 }
2423}
2424/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at2505/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
2425/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.2506/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
2426fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {2507fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
...@@ -2443,13 +2524,23 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {...@@ -2443,13 +2524,23 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
24432524
2444const AddLocalSymbolOptions = struct {2525const AddLocalSymbolOptions = struct {
2445 node: MappedFile.Node.Index.Optional,2526 node: MappedFile.Node.Index.Optional,
2446 name: String(.strtab),2527 name: []const u8,
2447 value: u64,2528 value: u64,
2448 size: u64,2529 size: u64,
2449 type: std.elf.STT,2530 type: std.elf.STT,
2450 shndx: Section.Index,2531 shndx: Section.Index,
2451};2532};
2452fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.LocalIndex {2533fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex {
2534 const gpa = elf.base.comp.gpa;
2535
2536 try elf.symtab.ensureUnusedCapacity(gpa, 1);
2537 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, 1);
2538 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {
2539 inline else => |shdr, class| elf.targetLoad(&shdr.size) + @sizeOf(class.ElfN().Sym),
2540 });
2541
2542 const name = try elf.string(.strtab, opts.name);
2543
2453 switch (elf.shdrPtr(.symtab)) {2544 switch (elf.shdrPtr(.symtab)) {
2454 inline else => |shdr, class| {2545 inline else => |shdr, class| {
2455 const ent_size = @sizeOf(class.ElfN().Sym);2546 const ent_size = @sizeOf(class.ElfN().Sym);
...@@ -2478,12 +2569,12 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2478,12 +2569,12 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2478 // ...then the `elf.symtab` metadata...2569 // ...then the `elf.symtab` metadata...
2479 new_index.ptr(elf).* = target_index.ptr(elf).*;2570 new_index.ptr(elf).* = target_index.ptr(elf).*;
2480 // ...then update the `elf.globals` tracking.2571 // ...then update the `elf.globals` tracking.
2481 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));2572 const gsi = elf.globalBySym(new_index);
2482 elf.globalByName(global_name).?.symtab_index = new_index;2573 gsi.ptr(elf).symtab_index = new_index;
24832574
2484 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {2575 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.2576 // This symbol's index is changing, so queue an update of relocs targeting it.
2486 elf.changed_symtab_index.putAssumeCapacity(global_name, {});2577 elf.changed_symtab_index.putAssumeCapacity(gsi, {});
2487 }2578 }
2488 }2579 }
24892580
...@@ -2493,7 +2584,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2493,7 +2584,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2493 };2584 };
24942585
2495 target_sym.* = .{2586 target_sym.* = .{
2496 .name = @backingInt(opts.name),2587 .name = @backingInt(name),
2497 .value = @intCast(opts.value),2588 .value = @intCast(opts.value),
2498 .size = @intCast(opts.size),2589 .size = @intCast(opts.size),
2499 .info = .{ .type = opts.type, .bind = .LOCAL },2590 .info = .{ .type = opts.type, .bind = .LOCAL },
...@@ -2509,140 +2600,112 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2509,140 +2600,112 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2509 }2600 }
2510}2601}
25112602
2512const AddGlobalSymbolOptions = struct {2603fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2513 const Name = struct {2604 MultipleDefinitions,
2514 strtab: String(.strtab),2605 MultipleDefaultVersions,
2515 dynstr: String(.dynstr),2606 UndefinedDefaultVersion,
2516 fn string(elf: *Elf, slice: []const u8) Error!Name {2607} || Error)!Symbol.Global.Index {
2517 return .{2608 _ = opts.lib_name; // TODO
2518 .strtab = try elf.string(.strtab, slice),2609
2519 .dynstr = switch (elf.shndx.dynsym) {2610 const gpa = elf.base.comp.gpa;
2520 .UNDEF => .empty,2611
2521 else => try elf.string(.dynstr, slice),2612 try elf.symtab.ensureUnusedCapacity(gpa, 1);
2522 },2613 try elf.globals.ensureUnusedCapacity(gpa, 1);
2523 };2614 try elf.globals_by_name.ensureUnusedCapacity(gpa, 1);
2615 try elf.node_global_symbols.ensureUnusedCapacity(gpa, 1);
2616
2617 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {
2618 inline else => |shdr, class| elf.targetLoad(&shdr.size) + @sizeOf(class.ElfN().Sym),
2619 });
2620
2621 const name = parseVersionedSymbolName(opts.name);
2622
2623 if (name.version != null and name.is_default_version) {
2624 if (opts.shndx == .UNDEF) {
2625 return error.UndefinedDefaultVersion;
2524 }2626 }
2525 };2627 }
25262628
2527 node: MappedFile.Node.Index.Optional,2629 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(@as(Symbol.Global.VersionedName, .{
2528 name: Name,2630 .name = name.name,
2529 lib_name: ?[]const u8 = null,2631 .version = name.version,
2530 value: u64,2632 }), @as(Symbol.Global.VersionedName.Adapter, .{ .elf = elf }));
2531 size: u64,2633 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));
2532 type: std.elf.STT,
2533 bind: enum { strong, weak },
2534 visibility: std.elf.STV,
2535 shndx: Section.Index,
2536};
2537fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{MultipleDefinitions}!Symbol.Id {
2538 _ = opts.lib_name; // TODO
25392634
2540 if (elf.shndx.dynsym == .UNDEF) {2635 if (gop.found_existing) {
2541 assert(opts.name.dynstr == .empty);2636 var force_apply_relocs: bool = false;
2542 } else {2637
2543 assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));2638 if (name.version != null and name.is_default_version) {
2544 }2639 const default_gop = try elf.default_version_globals.getOrPutAdapted(
25452640 elf.base.comp.gpa,
2546 // We break from this `switch` only if this symbol name did not previously exist at all and so2641 name.name,
2547 // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need2642 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2548 // a new symtab entry.2643 );
2549 const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {2644 if (!default_gop.found_existing) {
2550 .strong => new_global: {2645 // This symbol is becoming a default version.
2551 const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);2646 default_gop.key_ptr.* = gsi;
2552 if (gop.found_existing) return error.MultipleDefinitions;2647 const new_strtab_name = try elf.string(.strtab, opts.name);
2553 const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse2648 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2554 elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse2649 inline else => |sym| elf.targetStore(&sym.name, @backingInt(new_strtab_name)),
2555 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {2650 }
2556 // The symbol did not already exist, so we'll use the "new global" path.2651 force_apply_relocs = true;
2557 break :new_global gop.value_ptr;2652 } else if (default_gop.key_ptr.* != gsi) {
2558 };2653 return error.MultipleDefaultVersions;
2559 gop.value_ptr.* = old_kv.value;2654 }
2560 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{2655 }
2561 .node = opts.node,2656
2562 .value = opts.value,2657 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {
2563 .size = opts.size,2658 force_apply_relocs = true;
2564 .type = opts.type,2659 }
2565 .shndx = opts.shndx,2660
2566 });2661 // There's already a symbol with this name, we just need to potentially set its value, and
2567 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);2662 // to update its bind and visibility.
2568 return .global(opts.name.strtab);2663 if (opts.shndx == .UNDEF) {
2569 },2664 // We are not defining the symbol, so we won't set its value and we should only
2570 .weak => new_global: {2665 // update its bind if it is undefined and we won't weaken it.
2571 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {2666 if (!gsi.defined(elf) and
2572 // The existing definition holds, we just merge our visibility in.2667 gsi.ptr(elf).status.bind == .weak and
2573 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);2668 opts.bind == .strong)
2574 return .global(opts.name.strtab);2669 {
2575 }2670 elf.setGlobalSymbolBind(gsi, .strong);
2576 const gop = elf.globals.weak_def.getOrPutAssumeCapacity(opts.name.strtab);2671 }
2577 if (gop.found_existing) {2672 } else if (!gsi.defined(elf)) {
2578 // The existing definition holds, we just merge our visibility in.2673 elf.setGlobalSymbolBind(gsi, opts.bind);
2579 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);2674 try elf.setGlobalSymbolValue(gsi, .{
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,2675 .node = opts.node,
2590 .value = opts.value,2676 .value = opts.value,
2591 .size = opts.size,2677 .size = opts.size,
2592 .type = opts.type,2678 .type = opts.type,
2593 .shndx = opts.shndx,2679 .shndx = opts.shndx,
2594 });2680 });
2595 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);2681 force_apply_relocs = true;
2596 return .global(opts.name.strtab);2682 } else switch (opts.bind) {
2597 },2683 .strong => switch (gsi.ptr(elf).status.bind) {
2598 } else switch (opts.bind) {2684 .strong => return error.MultipleDefinitions,
2599 .strong => new_global: {2685 .weak => {
2600 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {2686 elf.setGlobalSymbolBind(gsi, .strong);
2601 // The existing definition holds, we just merge our visibility in.2687 try elf.setGlobalSymbolValue(gsi, .{
2602 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);2688 .node = opts.node,
2603 return .global(opts.name.strtab);2689 .value = opts.value,
2604 }2690 .size = opts.size,
2605 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {2691 .type = opts.type,
2606 // The existing definition holds, we just merge our visibility in.2692 .shndx = opts.shndx,
2607 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);2693 });
2608 return .global(opts.name.strtab);2694 force_apply_relocs = true;
2609 }2695 },
2610 const gop = elf.globals.strong_undef.getOrPutAssumeCapacity(opts.name.strtab);2696 },
2611 if (gop.found_existing) {2697 .weak => {},
2612 // The existing symbol is okay, we just merge our visibility in.2698 }
2613 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);2699
2614 return .global(opts.name.strtab);2700 try elf.updateGlobalDynamic(gsi, force_apply_relocs);
2615 }2701
2616 const old_kv = elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {2702 return gsi;
2617 // The symbol did not already exist, so we'll use the "new global" path.2703 }
2618 break :new_global gop.value_ptr;2704
2619 };2705 assert(elf.globals.items.len == @backingInt(gsi));
2620 gop.value_ptr.* = old_kv.value;2706 assert(elf.globals.addOneAssumeCapacity() == gsi.ptr(elf)); // populated later
2621 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);2707
2622 return .global(opts.name.strtab);2708 const strtab_name = try elf.string(.strtab, opts.name);
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 }
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);
2642 }
2643 break :new_global gop.value_ptr;
2644 },
2645 };
26462709
2647 const force_local_bind: bool = switch (opts.visibility) {2710 const force_local_bind: bool = switch (opts.visibility) {
2648 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,2711 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
...@@ -2656,13 +2719,6 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2656,13 +2719,6 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2656 .weak => .WEAK,2719 .weak => .WEAK,
2657 };2720 };
26582721
2659 const @"type": std.elf.STT = switch (opts.type) {
2660 .NOTYPE => if (elf.dso_globals.get(opts.name.strtab)) |dso_global| t: {
2661 break :t dso_global.type;
2662 } else .NOTYPE,
2663 else => |t| t,
2664 };
2665
2666 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));2722 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
2667 elf.symtab.appendAssumeCapacity(.{2723 elf.symtab.appendAssumeCapacity(.{
2668 .node = opts.node,2724 .node = opts.node,
...@@ -2678,10 +2734,10 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2678,10 +2734,10 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2678 // ...then populate the newly-valid symbol pointer2734 // ...then populate the newly-valid symbol pointer
2679 const sym = @field(elf.symPtr(sym_index), @tagName(class));2735 const sym = @field(elf.symPtr(sym_index), @tagName(class));
2680 sym.* = .{2736 sym.* = .{
2681 .name = @backingInt(opts.name.strtab),2737 .name = @backingInt(strtab_name),
2682 .value = @intCast(opts.value),2738 .value = @intCast(opts.value),
2683 .size = @intCast(opts.size),2739 .size = @intCast(opts.size),
2684 .info = .{ .type = @"type", .bind = bind },2740 .info = .{ .type = opts.type, .bind = bind },
2685 .other = .{ .visibility = opts.visibility },2741 .other = .{ .visibility = opts.visibility },
2686 .shndx = opts.shndx.toSection().?,2742 .shndx = opts.shndx.toSection().?,
2687 };2743 };
...@@ -2691,264 +2747,640 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2691,264 +2747,640 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2691 },2747 },
2692 }2748 }
26932749
2694 const old_head: String(.strtab) = old_head: {2750 const old_head: Symbol.Global.Index.Optional = old_head: {
2695 const node = opts.node.unwrap() orelse break :old_head .empty;2751 const node = opts.node.unwrap() orelse break :old_head .none;
2696 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);2752 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;2753 const old_head: Symbol.Global.Index.Optional = if (node_globals_gop.found_existing)
2698 gop.value_ptr.* = opts.name.strtab;2754 .wrap(node_globals_gop.value_ptr.*)
2755 else
2756 .none;
2757 node_globals_gop.value_ptr.* = gsi;
2699 break :old_head old_head;2758 break :old_head old_head;
2700 };2759 };
27012760
2702 new_global_ptr.* = .{2761 gsi.ptr(elf).* = .{
2703 .symtab_index = sym_index,2762 .status = .{
2704 .dynsym_index = dynsym_index: {2763 .bind = opts.bind,
2705 if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;2764 .want_static_value = false,
2706 if (force_local_bind) break :dynsym_index 0;2765 .owns_dynsym = false,
2707 switch (elf.shdrPtr(elf.shndx.dynsym)) {2766 .any_static_target_relocs = false,
2708 inline else => |shdr, class| {2767 .any_static_relative_target_relocs = false,
2709 const Sym = class.ElfN().Sym;2768 .any_dynamic_target_relocs = false,
2710 // Increase the dynamic symbol table size...
2711 const old_size = elf.targetLoad(&shdr.size);
2712 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2713 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2714 // ...then populate the newly-valid symbol pointer
2715 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2716 sym.* = .{
2717 .name = @backingInt(opts.name.dynstr),
2718 .value = @intCast(opts.value),
2719 .size = @intCast(opts.size),
2720 .info = .{ .type = @"type", .bind = bind },
2721 .other = .{ .visibility = opts.visibility },
2722 .shndx = opts.shndx.toSection().?,
2723 };
2724 if (elf.targetEndian() != std.lang.Endian.native) {
2725 std.mem.byteSwapAllFields(Sym, sym);
2726 }
2727 elf.appendDynsymHashEntry(dynsym_index);
2728 break :dynsym_index dynsym_index;
2729 },
2730 }
2731 },2769 },
2732 .prev_in_node = .empty,2770 .dynsym_index = 0,
2771 .symtab_index = sym_index,
2772 .prev_in_node = .none,
2733 .next_in_node = old_head,2773 .next_in_node = old_head,
2734 };2774 };
27352775
2736 if (old_head != .empty) {2776 if (old_head.unwrap()) |old_head_gsi| {
2737 const old_head_ptr = elf.globalByName(old_head).?;2777 const old_head_ptr = old_head_gsi.ptr(elf);
2738 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);2778 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
2739 assert(old_head_ptr.prev_in_node == .empty);2779 assert(old_head_ptr.prev_in_node == .none);
2740 old_head_ptr.prev_in_node = opts.name.strtab;2780 old_head_ptr.prev_in_node = .wrap(gsi);
2741 }2781 }
27422782
2743 if (force_local_bind) {2783 if (force_local_bind) {
2744 elf.moveDemotedGlobal(new_global_ptr);2784 elf.moveDemotedGlobal(gsi);
2745 }2785 }
27462786
2747 switch (@"type") {2787 if (name.version != null and name.is_default_version) {
2748 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and2788 const default_gop = try elf.default_version_globals.getOrPutAdapted(
2749 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)2789 elf.base.comp.gpa,
2750 {2790 name.name,
2751 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.2791 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2752 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);2792 );
2753 },2793 if (!default_gop.found_existing) {
2754 else => {},2794 // This symbol is becoming a default version.
2795 default_gop.key_ptr.* = gsi;
2796 } else if (default_gop.key_ptr.* != gsi) {
2797 return error.MultipleDefaultVersions;
2798 }
2755 }2799 }
27562800
2757 return .global(opts.name.strtab);2801 // We must pass `force_apply_relocs` as `true` here, because even though `gsi` itself was only
2802 // just created (so there are no relocations targeting it yet), it is possible that another
2803 // symbol is now an alias of it, in which case that symbol *does* need target relocations to be
2804 // re-applied.
2805 try elf.updateGlobalDynamic(gsi, true);
2806
2807 return gsi;
2758}2808}
2759fn setGlobalSymbolValue(2809
2760 elf: *Elf,2810/// Updates the dynsym index associated with the given global symbol; populates that dynsym entry if
2761 global_name: String(.strtab),2811/// necessary; removes unnecessary dynamic relocations; creates/removes PLT entries as needed; and
2762 global_ptr: *Symbol.Global,2812/// creates/removes copy relocations as needed. Put simply, updates all state related to dynamic
2763 new: struct {2813/// linking for the given symbol.
2764 node: MappedFile.Node.Index.Optional,2814///
2765 value: u64,2815/// As well as on creation, this function should be called whenever any of the following properties
2766 size: u64,2816/// of a global symbol changes:
2767 type: std.elf.STT,2817///
2768 shndx: Section.Index,2818/// * visibility
2769 },2819/// * type
2770) void {2820/// * whether a copy relocation is required
2771 assert(new.shndx != .UNDEF);2821/// * whether a PLT entry is required
2772 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {2822/// * whether it is defined
2773 if (global_ptr.next_in_node != .empty) {2823/// * whether there is a matching symbol in `dso_globals`
2774 const next = elf.globalByName(global_ptr.next_in_node).?;2824/// * for versioned symbols, whether it is the default symbol version
2775 assert(next.prev_in_node == global_name);2825/// * for unversioned symbols, this symbol's default version in `dso_globals`
2776 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);2826///
2777 next.prev_in_node = global_ptr.prev_in_node;2827/// This function may re-apply relocations targeting the global. If the caller has itself performed
2778 }2828/// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as
2779 if (global_ptr.prev_in_node != .empty) {2829/// `true` to guarantee this function to do so.
2780 const prev = elf.globalByName(global_ptr.prev_in_node).?;2830fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void {
2781 assert(prev.next_in_node == global_name);2831 const gpa = elf.base.comp.gpa;
2782 assert(prev.symtab_index.ptr(elf).node.unwrap().? == old_node);2832
2783 prev.next_in_node = global_ptr.next_in_node;2833 const gsi = orig_gsi.resolveAlias(elf);
2834
2835 const maybe_alias_gsi = gsi.findAliaser(elf);
2836
2837 var apply_relocs: bool = force_apply_relocs;
2838
2839 _ = elf.defined_alias_globals.swapRemove(gsi); // not an alias
2840
2841 if (maybe_alias_gsi) |alias_gsi| {
2842 if (gsi.defined(elf) and
2843 alias_gsi.defined(elf) and
2844 gsi.ptr(elf).status.bind == .strong and
2845 alias_gsi.ptr(elf).status.bind == .strong)
2846 {
2847 // `gsi` and `alias_gsi` both have strong definitions. For now we've arbitrarily decided
2848 // that `gsi`'s definition wins, but this needs to cause a "multiple definitions" error!
2849 try elf.defined_alias_globals.put(gpa, alias_gsi, {});
2784 } else {2850 } else {
2785 // We're the start of the linked list, so we need to change the head.2851 _ = elf.defined_alias_globals.swapRemove(alias_gsi);
2786 if (global_ptr.next_in_node == .empty) {2852 }
2787 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);2853
2788 } else {2854 _ = elf.unknown_globals.swapRemove(alias_gsi);
2789 elf.node_global_symbols.getPtr(old_node).?.* = global_ptr.next_in_node;2855 if (try elf.deleteOwnedDynsym(alias_gsi)) {
2790 }2856 apply_relocs = true;
2791 }2857 }
2792 } else {
2793 assert(global_ptr.next_in_node == .empty);
2794 assert(global_ptr.prev_in_node == .empty);
2795 }2858 }
27962859
2797 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {2860 const maybe_dso_global: ?*const DsoGlobals.Symbol = dso_global: {
2798 // This is a quite rare case: there was a definition for this symbol in a shared library2861 if (gsi.defined(elf)) {
2799 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*2862 break :dso_global null;
2800 // definition which replaces it. We know that our definition cannot be preempted because we2863 }
2801 // are the executable (only executables can have copy relocations!), so we definitely do not2864 if (elf.dso_globals.find(gsi.name(elf), gsi.version(elf))) |dso_global| {
2802 // need the copy relocation.2865 break :dso_global dso_global;
2866 }
2867 if (gsi.version(elf) == null) {
2868 if (elf.dso_globals.findDefaultVersion(gsi.name(elf))) |dso_global| {
2869 break :dso_global dso_global;
2870 }
2871 }
2872 break :dso_global null;
2873 };
28032874
2804 // All we actually need to do is remove the entry from `copied_globals` (already done), and2875 const visibility: std.elf.STV, const sym_type: std.elf.STT = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2805 // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations2876 inline else => |sym| info: {
2806 // targeting this symbol, but we were going to do that at the end of this function anyway.2877 // If the symbol is undefined with type `STT_NOTYPE`, but there is a type available in
2807 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);2878 // an input DSO, then use that symbol type!
2808 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we2879 if (maybe_dso_global) |dso_global| {
2809 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.2880 if (elf.targetLoad(&sym.shndx) == std.elf.SHN_UNDEF and
2810 } else {2881 elf.targetLoad(&sym.info).type == .NOTYPE and
2811 _ = elf.want_copied_globals.swapRemove(global_name);2882 dso_global.type != .NOTYPE)
2812 }2883 {
2884 elf.targetStore(&sym.info, .{
2885 .type = dso_global.type,
2886 .bind = elf.targetLoad(&sym.info).bind,
2887 });
2888 }
2889 }
28132890
2814 global_ptr.symtab_index.ptr(elf).node = new.node;2891 break :info .{
2892 elf.targetLoad(&sym.other).visibility,
2893 elf.targetLoad(&sym.info).type,
2894 };
2895 },
2896 };
28152897
2816 const old_head: String(.strtab) = old_head: {2898 const omit_dynsym: bool = omit: {
2817 const new_node = new.node.unwrap() orelse break :old_head .empty;2899 if (elf.shndx.dynsym == .UNDEF) break :omit true;
2818 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);2900 break :omit switch (visibility) {
2819 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;2901 .HIDDEN, .INTERNAL => true,
2820 gop.value_ptr.* = global_name;2902 .DEFAULT, .PROTECTED => false,
2821 break :old_head old_head;2903 };
2822 };2904 };
2905 if (omit_dynsym) {
2906 if (try elf.deleteOwnedDynsym(gsi)) {
2907 apply_relocs = true;
2908 }
2909 if (gsi.ptr(elf).dynsym_index != 0) {
2910 gsi.ptr(elf).dynsym_index = 0;
2911 try elf.changed_symtab_index.put(gpa, gsi, {});
2912 }
2913 if (maybe_alias_gsi) |alias_gsi| {
2914 if (alias_gsi.ptr(elf).dynsym_index != 0) {
2915 alias_gsi.ptr(elf).dynsym_index = 0;
2916 try elf.changed_symtab_index.put(gpa, alias_gsi, {});
2917 }
2918 }
28232919
2824 global_ptr.prev_in_node = .empty;2920 if (!gsi.defined(elf) and
2825 global_ptr.next_in_node = old_head;2921 gsi.ptr(elf).status.bind == .strong and
2922 maybe_dso_global == null)
2923 {
2924 try elf.unknown_globals.put(gpa, gsi, {});
2925 } else {
2926 _ = elf.unknown_globals.swapRemove(gsi);
2927 }
28262928
2827 if (old_head != .empty) {2929 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
2828 const old_head_ptr = elf.globalByName(old_head).?;2930 gsi.deleteDynamicTargetRelocs(elf);
2829 assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);2931 if (maybe_alias_gsi) |alias_gsi| {
2830 assert(old_head_ptr.prev_in_node == .empty);2932 alias_gsi.deleteDynamicTargetRelocs(elf);
2831 old_head_ptr.prev_in_node = global_name;2933 }
2832 }
28332934
2834 // Now for the easy bit where we actually update the symtab entry.2935 if (apply_relocs) {
2835 switch (elf.symPtr(global_ptr.symtab_index)) {2936 Symbol.Id.global(gsi).applyTargetRelocs(elf);
2836 inline else => |sym| {2937 if (maybe_alias_gsi) |alias_gsi| {
2837 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.2938 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
2838 elf.targetStore(&sym.size, @intCast(new.size));2939 }
2839 elf.targetStore(&sym.shndx, new.shndx.toSection().?);2940 }
2840 const old_bind = elf.targetLoad(&sym.info).bind;2941 return;
2841 elf.targetStore(&sym.info, .{
2842 .type = new.type,
2843 .bind = old_bind,
2844 });
2845 },
2846 }2942 }
28472943
2848 // ...and also the dynsym entry if there is one.2944 // We need an owned dynsym entry. We can either reuse an existing one or create a new one.
2849 if (global_ptr.dynsym_index != 0) switch (elf.dynsymPtr(global_ptr.dynsym_index)) {2945 const new_dynsym_index: u32 = owned_dynsym_index: {
2850 inline else => |sym| {2946 if (gsi.ptr(elf).status.owns_dynsym) {
2851 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.2947 break :owned_dynsym_index gsi.ptr(elf).dynsym_index;
2852 elf.targetStore(&sym.size, @intCast(new.size));2948 }
2853 elf.targetStore(&sym.shndx, new.shndx.toSection().?);2949
2854 const old_bind = elf.targetLoad(&sym.info).bind;2950 // We're going to add a new entry to dynsym; ensure there's space in the relevant sections.
2855 elf.targetStore(&sym.info, .{2951 const dynsym_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2856 .type = new.type,2952 inline else => |shdr, class| .{
2857 .bind = old_bind,2953 elf.targetLoad(&shdr.size),
2858 });2954 @sizeOf(class.ElfN().Sym),
2859 },2955 },
2956 };
2957 const need_dynsym_len: u32 = @intCast(@divExact(dynsym_size, dynsym_ent_size) + 1);
2958
2959 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_dynsym_len * dynsym_ent_size);
2960 try elf.shndx.gnu_version.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_dynsym_len * 2);
2961 try elf.ensureDynsymHashCapacity(need_dynsym_len);
2962
2963 const dynstr_name: String(.dynstr) = name: {
2964 // Explicitly reserve space first because that reservation could invalidate the name slice.
2965 try elf.ensureAdditionalStringCapacity(.dynstr, gsi.name(elf).len);
2966 break :name elf.stringAssumeCapacity(.dynstr, gsi.name(elf));
2967 };
2968
2969 const dynsym_index: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2970 inline else => |shdr, class| new_dynsym_index: {
2971 const Sym = class.ElfN().Sym;
2972
2973 // Increase the dynamic symbol table size...
2974 const old_size = elf.targetLoad(&shdr.size);
2975 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2976 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2977
2978 // ...and the same for the symbol version table.
2979 const versym_shdr = @field(elf.shdrPtr(elf.shndx.gnu_version), @tagName(class));
2980 assert(elf.targetLoad(&versym_shdr.size) == dynsym_index * 2);
2981 elf.targetStore(&versym_shdr.size, (dynsym_index + 1) * 2);
2982
2983 // Populate the symbol name (the other fields are set later).
2984 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2985 elf.targetStore(&sym.name, @backingInt(dynstr_name));
2986
2987 break :new_dynsym_index dynsym_index;
2988 },
2989 };
2990
2991 elf.appendDynsymHashEntry(dynsym_index);
2992
2993 if (gsi.version(elf) != null) {
2994 try elf.versioned_dynsym_owners.putNoClobber(gpa, dynsym_index, gsi);
2995 }
2996
2997 break :owned_dynsym_index dynsym_index;
2860 };2998 };
28612999
2862 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to3000 gsi.ptr(elf).status.owns_dynsym = true;
2863 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.3001
2864 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.3002 if (gsi.ptr(elf).dynsym_index != new_dynsym_index) {
2865 if (elf.plt.getIndex(global_name)) |plt_index| {3003 gsi.ptr(elf).dynsym_index = new_dynsym_index;
2866 if (!elf.pltEntryIsDead(plt_index) and3004 try elf.changed_symtab_index.put(gpa, gsi, {});
2867 elf.classifySymbolValue(.global(global_name)) != .dynamic)3005 }
2868 {3006 if (maybe_alias_gsi) |alias_gsi| {
2869 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));3007 if (alias_gsi.ptr(elf).dynsym_index != new_dynsym_index) {
2870 assert(elf.pltEntryIsDead(plt_index));3008 alias_gsi.ptr(elf).dynsym_index = new_dynsym_index;
3009 try elf.changed_symtab_index.put(gpa, alias_gsi, {});
2871 }3010 }
2872 }3011 }
28733012
2874 // If this symbol was previously undefined, relocations targeting it may have been lowered to3013 {
2875 // runtime relocations which we have now discovered we do not need, so delete those. This does3014 const dynsym_versym: std.elf.Versym = versym: {
2876 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.3015 // If there's a definition, just use the version in the symbol name.
2877 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(global_name)) != .dynamic) {3016 if (gsi.defined(elf)) {
2878 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);3017 if (gsi.version(elf)) |orig_version_slice| {
2879 }3018 // We want to add the version to dynstr, but we need to explicitly reserve space
3019 // first because that reservation could invalidate the version slice.
3020 try elf.ensureAdditionalStringCapacity(.dynstr, orig_version_slice.len);
3021 const version_dynstr = elf.stringAssumeCapacity(.dynstr, gsi.version(elf).?);
3022 break :versym .{
3023 .VERSION = try elf.verdefId(version_dynstr),
3024 .HIDDEN = !gsi.isDefaultDefinition(elf),
3025 };
3026 }
3027 break :versym .GLOBAL;
3028 }
28803029
2881 // Finally, update the symbol value, re-applying target relocations. Also note that because we3030 // ...but if it's undefined, we need to consult the matching input DSO global.
2882 // possibly removed the PLT entry above, some relocations which were previously targeting the3031 if (maybe_dso_global) |dso_global| {
2883 // PLT will now instead target the symbol itself.3032 if (dso_global.version.unwrap()) |version| {
2884 Symbol.Id.global(global_name).flushMoved(elf, new.value);3033 const version_dynstr = try elf.string(.dynstr, version.slice(&elf.dso_globals));
3034 break :versym .{
3035 .VERSION = try elf.verneedId(dso_global.soname, version_dynstr),
3036 .HIDDEN = false,
3037 };
3038 }
3039 }
3040
3041 break :versym .LOCAL;
3042 };
3043
3044 elf.targetStore(&elf.versymSlice()[new_dynsym_index], dynsym_versym);
3045 }
3046
3047 // Populate the dynsym info from the symtab.
3048 switch (elf.dynsymPtr(new_dynsym_index)) {
3049 inline else => |dynsym, class| {
3050 const sym = @field(elf.symPtr(gsi.ptr(elf).symtab_index), @tagName(class));
3051 // No byte swaps needed here because src values are already in target endian.
3052 dynsym.* = .{
3053 .name = dynsym.name,
3054 .value = sym.value,
3055 .size = sym.size,
3056 .info = sym.info,
3057 .other = sym.other,
3058 .shndx = sym.shndx,
3059 };
3060 },
3061 }
3062
3063 const need_plt_entry: bool = plt: {
3064 switch (sym_type) {
3065 .FUNC, .GNU_IFUNC => {},
3066 else => {
3067 // Only functions can have PLT entries.
3068 break :plt false;
3069 },
3070 }
3071 if (visibility == .PROTECTED) {
3072 // No need for a PLT entry, since any definition is guaranteed to be defined locally.
3073 break :plt false;
3074 }
3075 if (gsi.defined(elf) and elf.base.comp.config.output_mode == .Exe) {
3076 // No need for a PLT entry, since there's already a non-preemptible local definition.
3077 break :plt false;
3078 }
3079 break :plt true;
3080 };
3081 if (need_plt_entry) {
3082 if (try elf.ensurePltEntry(gsi)) {
3083 apply_relocs = true;
3084 }
3085 } else {
3086 if (elf.plt.getIndex(gsi)) |plt_index| {
3087 if (!elf.pltEntryIsDead(plt_index)) {
3088 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
3089 assert(elf.pltEntryIsDead(plt_index));
3090 apply_relocs = true;
3091 }
3092 }
3093 }
3094
3095 const want_copy_rel: bool = copy: {
3096 if (elf.base.comp.config.output_mode != .Exe) {
3097 // Only executables can contain copy relocations.
3098 break :copy false;
3099 }
3100 if (gsi.defined(elf)) {
3101 // No need for a copy relocation when there's a local definition (which we know is not
3102 // preemptible because this is the executable).
3103 break :copy false;
3104 }
3105 if (!gsi.ptr(elf).status.want_static_value) {
3106 // Nobody's actually requesting a copy relocation, so no need to make one... unless the
3107 // alias is actually the one who needs it!
3108 const alias_gsi = maybe_alias_gsi orelse break :copy false;
3109 if (!alias_gsi.ptr(elf).status.want_static_value) break :copy false;
3110 }
3111 const dso_global = maybe_dso_global orelse {
3112 // We cannot create a copy relocation until we see the symbol in an input DSO.
3113 break :copy false;
3114 };
3115 if (dso_global.type != .OBJECT) {
3116 // We can only create copy relocations for `STT_OBJECT` symbols.
3117 break :copy false;
3118 }
3119 // A copy relocation was requested and is possible!
3120 break :copy true;
3121 };
3122 if (want_copy_rel) {
3123 const dso_global = maybe_dso_global.?;
3124
3125 const shndx: Section.Index = .data;
3126
3127 try shndx.ensureAligned(elf, dso_global.alignment);
3128
3129 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3130 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
3131 }
3132 switch (elf.dynsymPtr(new_dynsym_index)) {
3133 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
3134 }
3135
3136 const gop = try elf.copied_globals.getOrPut(gpa, gsi);
3137 if (gop.found_existing) {
3138 if (dso_global.alignment.compare(.gt, gop.value_ptr.node.alignment(&elf.mf))) {
3139 try gop.value_ptr.node.realign(gpa, &elf.mf, dso_global.alignment);
3140 }
3141 try gop.value_ptr.node.ensureMinimumSize(gpa, &elf.mf, dso_global.size);
3142 } else {
3143 errdefer assert(elf.copied_globals.pop().?.key == gsi);
3144 try elf.nodes.ensureUnusedCapacity(gpa, 1);
3145 const node = elf.addNodeAssumeCapacity(
3146 try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
3147 .size = dso_global.alignment.forward(dso_global.size),
3148 .alignment = dso_global.alignment,
3149 }),
3150 .{ .copied_global = gsi },
3151 );
3152 const vaddr = elf.computeNodeVAddr(node);
3153 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3154 .type = .copy(elf),
3155 .offset = vaddr,
3156 .raw_sym_index = new_dynsym_index,
3157 .addend = 0,
3158 });
3159 gop.value_ptr.* = .{
3160 .node = node,
3161 .rela_index = rela_index,
3162 };
3163
3164 // We have added a copy relocation so we now own the canonical address of the symbol.
3165 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3166 inline else => |sym| elf.targetStore(&sym.value, @intCast(vaddr)),
3167 }
3168 switch (elf.dynsymPtr(new_dynsym_index)) {
3169 inline else => |sym| elf.targetStore(&sym.value, @intCast(vaddr)),
3170 }
3171 apply_relocs = true;
3172 }
3173 } else {
3174 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3175 // We previously made a copy relocation, but now have deemed it unnecessary.
3176 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
3177 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the
3178 // space), we should delete `copied_global_kv.value.node`, which is an
3179 // "orphaned" `copied_global` node.
3180 apply_relocs = true;
3181 }
3182 }
3183
3184 if (!gsi.defined(elf) and
3185 gsi.ptr(elf).status.bind == .strong and
3186 maybe_dso_global == null)
3187 {
3188 try elf.unknown_globals.put(gpa, gsi, {});
3189 } else {
3190 _ = elf.unknown_globals.swapRemove(gsi);
3191 }
3192
3193 if (elf.classifySymbolValue(.global(gsi)) != .dynamic) {
3194 gsi.deleteDynamicTargetRelocs(elf);
3195 if (maybe_alias_gsi) |alias_gsi| {
3196 alias_gsi.deleteDynamicTargetRelocs(elf);
3197 }
3198 }
3199
3200 if (apply_relocs) {
3201 Symbol.Id.global(gsi).applyTargetRelocs(elf);
3202 if (maybe_alias_gsi) |alias_gsi| {
3203 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
3204 }
3205 }
2885}3206}
3207/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` with the
3208/// `force_apply_relocs` argument set to `true`. This ensures that the dynamic symbol table is kept
3209/// up-to-date, unnecessary PLT entries or copy relocations are removed, and that relocations are
3210/// applied when necessary.
3211fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
3212 node: MappedFile.Node.Index.Optional,
3213 value: u64,
3214 size: u64,
3215 type: std.elf.STT,
3216 shndx: Section.Index,
3217}) Error!void {
3218 assert(new.shndx != .UNDEF);
3219
3220 const global_ptr = gsi.ptr(elf);
3221
3222 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
3223 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
3224 assert(next_gsi.ptr(elf).prev_in_node == Symbol.Global.Index.Optional.wrap(gsi));
3225 next_gsi.ptr(elf).prev_in_node = global_ptr.prev_in_node;
3226 }
3227 if (global_ptr.prev_in_node.unwrap()) |prev_gsi| {
3228 assert(prev_gsi.ptr(elf).next_in_node == Symbol.Global.Index.Optional.wrap(gsi));
3229 prev_gsi.ptr(elf).next_in_node = global_ptr.next_in_node;
3230 } else {
3231 // We're the start of the linked list, so we need to change the head.
3232 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
3233 elf.node_global_symbols.getPtr(old_node).?.* = next_gsi;
3234 } else {
3235 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == gsi);
3236 }
3237 }
3238 } else {
3239 assert(global_ptr.next_in_node == .none);
3240 assert(global_ptr.prev_in_node == .none);
3241 }
3242
3243 global_ptr.symtab_index.ptr(elf).node = new.node;
3244
3245 const old_head: Symbol.Global.Index.Optional = old_head: {
3246 const new_node = new.node.unwrap() orelse break :old_head .none;
3247 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
3248 const old_head: Symbol.Global.Index.Optional = if (gop.found_existing) .wrap(gop.value_ptr.*) else .none;
3249 gop.value_ptr.* = gsi;
3250 break :old_head old_head;
3251 };
3252
3253 global_ptr.prev_in_node = .none;
3254 global_ptr.next_in_node = old_head;
3255
3256 if (old_head.unwrap()) |old_head_gsi| {
3257 assert(old_head_gsi.ptr(elf).prev_in_node == .none);
3258 old_head_gsi.ptr(elf).prev_in_node = .wrap(gsi);
3259 }
3260
3261 // Now for the easy bit where we actually update the symtab entry.
3262 switch (elf.symPtr(global_ptr.symtab_index)) {
3263 inline else => |sym| {
3264 elf.targetStore(&sym.value, @intCast(new.value));
3265 elf.targetStore(&sym.size, @intCast(new.size));
3266 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
3267 const old_bind = elf.targetLoad(&sym.info).bind;
3268 elf.targetStore(&sym.info, .{
3269 .type = new.type,
3270 .bind = old_bind,
3271 });
3272 },
3273 }
3274}
3275
3276fn setGlobalSymbolBind(
3277 elf: *Elf,
3278 gsi: Symbol.Global.Index,
3279 bind: Symbol.Global.Bind,
3280) void {
3281 gsi.ptr(elf).status.bind = bind;
3282 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3283 inline else => |sym| {
3284 const old_info = elf.targetLoad(&sym.info);
3285 elf.targetStore(&sym.info, .{
3286 .type = old_info.type,
3287 .bind = switch (old_info.bind) {
3288 .LOCAL => .LOCAL, // demoted global
3289 else => switch (bind) {
3290 .strong => .GLOBAL,
3291 .weak => .WEAK,
3292 },
3293 },
3294 });
3295 },
3296 }
3297}
3298
2886/// When the same global symbol appears in two inputs---even if one symbol is defined and the other3299/// 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, which3300/// undefined---their visibility values are combined to determine the resulting visibility.
2888/// can also affect the bind of the symbol we output.3301///
2889fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visibility: std.elf.STV, bind: enum { strong, weak }) void {3302/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes
2890 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {3303/// to symbol visibility can affect the dynamic symbol table entry. If this function returns `true`,
3304/// then that `updateGlobalDynamic` call must have the `force_apply_relocs` argument set to `true`,
3305/// because this symbol's classification has changed (it is no longer a dynamic symbol) so
3306/// relocations targeting the symbol must be re-applied.
3307fn mergeGlobalSymbolVisibility(
3308 elf: *Elf,
3309 gsi: Symbol.Global.Index,
3310 other_visibility: std.elf.STV,
3311) bool {
3312 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2891 inline else => |sym| elf.targetLoad(&sym.other).visibility,3313 inline else => |sym| elf.targetLoad(&sym.other).visibility,
2892 };3314 };
2893 // The combined visibility is essentially the "strictest" of the two, with most strict being3315
2894 // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.3316 // The new visibility is basically whichever of the old and new is "stricter", with the most
2895 const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {3317 // strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2896 .INTERNAL => .{ .INTERNAL, false },3318 const new_visibility: std.elf.STV = switch (old_visibility) {
3319 .INTERNAL => .INTERNAL,
2897 .HIDDEN => switch (other_visibility) {3320 .HIDDEN => switch (other_visibility) {
2898 .INTERNAL => .{ .INTERNAL, false },3321 .INTERNAL => .INTERNAL,
2899 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },3322 .HIDDEN, .PROTECTED, .DEFAULT => .HIDDEN,
2900 },3323 },
2901 .PROTECTED => switch (other_visibility) {3324 .PROTECTED => switch (other_visibility) {
2902 .INTERNAL => .{ .INTERNAL, true },3325 .INTERNAL => .INTERNAL,
2903 .HIDDEN => .{ .HIDDEN, true },3326 .HIDDEN => .HIDDEN,
2904 .PROTECTED, .DEFAULT => .{ .PROTECTED, false },3327 .PROTECTED, .DEFAULT => .PROTECTED,
2905 },3328 },
2906 .DEFAULT => switch (other_visibility) {3329 .DEFAULT => switch (other_visibility) {
2907 .INTERNAL => .{ .INTERNAL, true },3330 .INTERNAL => .INTERNAL,
2908 .HIDDEN => .{ .HIDDEN, true },3331 .HIDDEN => .HIDDEN,
2909 .PROTECTED => .{ .PROTECTED, false },3332 .PROTECTED => .PROTECTED,
2910 .DEFAULT => .{ .DEFAULT, false },3333 .DEFAULT => .DEFAULT,
2911 },3334 },
2912 };3335 };
2913 // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared3336 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2914 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are3337 inline else => |sym| elf.targetStore(
2915 // putting the global in this state for the first time---let's call it "demoting" the global to3338 &sym.other,
2916 // STB_LOCAL---we need to update its bind in the symtab.3339 .{ .visibility = new_visibility },
2917 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;3340 ),
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,
2929 },
2930 };
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);
2938 }
2939 },
2940 }3341 }
2941 if (demote_to_local) {3342
2942 // When demoting a global to STB_LOCAL, we need to move its symtab index so that it is with3343 if (elf.ehdrType() == .REL) {
2943 // the STB_LOCAL symbols instead of the global symbols.3344 return false;
2944 elf.moveDemotedGlobal(global_ptr);
2945 }3345 }
3346
3347 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
3348 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
3349 // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output.
3350 const old_demoted: bool = switch (old_visibility) {
3351 .INTERNAL, .HIDDEN => true,
3352 .DEFAULT, .PROTECTED => false,
3353 };
3354 const new_demoted: bool = switch (new_visibility) {
3355 .INTERNAL, .HIDDEN => true,
3356 .DEFAULT, .PROTECTED => false,
3357 };
3358 if (!old_demoted and new_demoted) {
3359 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3360 inline else => |sym| elf.targetStore(&sym.info, .{
3361 .type = elf.targetLoad(&sym.info).type,
3362 .bind = .LOCAL,
3363 }),
3364 }
3365 elf.moveDemotedGlobal(gsi);
3366 }
3367
3368 const old_protected: bool = switch (old_visibility) {
3369 .PROTECTED, .INTERNAL, .HIDDEN => true,
3370 .DEFAULT => false,
3371 };
3372 const new_protected: bool = switch (new_visibility) {
3373 .PROTECTED, .INTERNAL, .HIDDEN => true,
3374 .DEFAULT => false,
3375 };
3376 return old_protected != new_protected;
2946}3377}
2947/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),3378/// 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 ELF3379/// 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.3380/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
2950fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {3381fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {
2951 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module3382 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
3383 const global_ptr = gsi.ptr(elf);
2952 switch (elf.shdrPtr(.symtab)) {3384 switch (elf.shdrPtr(.symtab)) {
2953 inline else => |shdr, class| {3385 inline else => |shdr, class| {
2954 // `shdr.info` stores the index of the first global symbol. We are going to swap the3386 // `shdr.info` stores the index of the first global symbol. We are going to swap the
...@@ -2969,12 +3401,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2969,12 +3401,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2969 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));3401 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2970 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));3402 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
29713403
2972 const this_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&src_sym_ptr.name));3404 const other_gsi = elf.globalBySym(dest_index);
2973 assert(elf.globalByName(this_name).? == global_ptr);3405 assert(other_gsi.ptr(elf).symtab_index == dest_index);
2974
2975 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);
29783406
2979 // First swap the symtab entries...3407 // First swap the symtab entries...
2980 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);3408 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
...@@ -2982,57 +3410,113 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2982,57 +3410,113 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2982 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));3410 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2983 // ...then update the `elf.globals` tracking.3411 // ...then update the `elf.globals` tracking.
2984 global_ptr.symtab_index = dest_index;3412 global_ptr.symtab_index = dest_index;
2985 other_global_ptr.symtab_index = src_index;3413 other_gsi.ptr(elf).symtab_index = src_index;
2986 }3414 }
3415 },
3416 }
3417}
3418/// If `gsi` owns a dynsym entry, deletes that entry from the `.dynsym` section and marks `gsi` as
3419/// no longer owning it. All state depending on the dynamic symbol is also deleted. To keep the
3420/// dynamic symbol table compact, the last entry in the table is swapped into the place of the
3421/// removed one (all associated bookkeeping is handled by this function).
3422///
3423/// Returns `true` if a PLT entry or copy relocation for the global symbol was removed, indicating
3424/// that the caller must at some point re-apply relocations targeting `gsi`.
3425fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Error!bool {
3426 if (!gsi.ptr(elf).status.owns_dynsym) {
3427 return false;
3428 }
29873429
2988 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,3430 const gpa = elf.base.comp.gpa;
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));
29923431
2993 const ent_size = @sizeOf(class.ElfN().Sym);3432 const dynsym_index = gsi.ptr(elf).dynsym_index;
2994 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);3433 assert(dynsym_index != 0);
3434 gsi.ptr(elf).status.owns_dynsym = false;
29953435
2996 // We're going to decrease the size of `.dynsym`, thereby removing its last index.3436 var apply_relocs: bool = false;
2997 const old_size = elf.targetLoad(&dynsym_shdr.size);3437 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
2998 const new_size = old_size - ent_size;3438 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2999 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));3439 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the
3440 // space), we should delete `copied_global_kv.value.node`, which is an
3441 // "orphaned" `copied_global` node.
3442 apply_relocs = true;
3443 }
3444 if (elf.plt.getIndex(gsi)) |plt_index| {
3445 if (!elf.pltEntryIsDead(plt_index)) {
3446 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
3447 assert(elf.pltEntryIsDead(plt_index));
3448 apply_relocs = true;
3449 }
3450 }
3451
3452 switch (elf.shdrPtr(elf.shndx.dynsym)) {
3453 inline else => |dynsym_shdr, class| {
3454 const ent_size = @sizeOf(class.ElfN().Sym);
3455 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
30003456
3001 elf.popDynsymHashEntry(remove_dynsym_index);3457 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
3458 const old_size = elf.targetLoad(&dynsym_shdr.size);
3459 const new_size = old_size - ent_size;
3460 const pop_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
30023461
3003 const free_dynsym_index = global_ptr.dynsym_index;3462 elf.popDynsymHashEntry(pop_dynsym_index);
3004 global_ptr.dynsym_index = 0;
30053463
3006 if (free_dynsym_index != remove_dynsym_index) {3464 _ = elf.versioned_dynsym_owners.swapRemove(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.
30093465
3010 elf.clearDynsymHashEntry(free_dynsym_index);3466 if (dynsym_index != pop_dynsym_index) {
3467 // The demoted global wasn't the last entry, so move whatever entry we just
3468 // truncated out of dynsym into its place.
30113469
3012 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));3470 const moved_gsi = elf.globalByDynsym(pop_dynsym_index);
3013 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
30143471
3015 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));3472 elf.clearDynsymHashEntry(dynsym_index);
3016 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
3017 const moved_global_ptr = elf.globalByName(moved_name).?;
30183473
3019 dest_dynsym_ptr.* = src_dynsym_ptr.*;3474 const src_dynsym_ptr = @field(elf.dynsymPtr(pop_dynsym_index), @tagName(class));
3475 const dest_dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
30203476
3021 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);3477 const versym = elf.versymSlice();
3022 moved_global_ptr.dynsym_index = free_dynsym_index;
30233478
3024 elf.populateDynsymHashEntry(free_dynsym_index);3479 dest_dynsym_ptr.* = src_dynsym_ptr.*;
3480 versym[dynsym_index] = versym[pop_dynsym_index];
30253481
3026 // Since that symbol's dynsym index has changed, we'll have to update any3482 moved_gsi.ptr(elf).dynsym_index = @intCast(dynsym_index);
3027 // relocation entries targeting it.3483
3028 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});3484 elf.populateDynsymHashEntry(dynsym_index);
3485
3486 if (elf.versioned_dynsym_owners.getIndex(pop_dynsym_index)) |map_index| {
3487 elf.versioned_dynsym_owners.setKey(map_index, dynsym_index);
3029 }3488 }
30303489
3031 // Now that we've given that symbol a new home, actually decrease the section size.3490 // Since that symbol's dynsym index has changed, we'll have to update any
3032 elf.targetStore(&dynsym_shdr.size, new_size);3491 // relocation entries targeting it.
3492 try elf.changed_symtab_index.put(gpa, moved_gsi, {});
3493
3494 // If `moved_gsi` is a versioned symbol, then it may have an unversioned
3495 // alias with the same dynsym index.
3496 if (moved_gsi.version(elf) != null) {
3497 if (elf.globalByName(.{
3498 .name = moved_gsi.name(elf),
3499 .version = null,
3500 })) |unversioned_gsi| {
3501 if (unversioned_gsi.ptr(elf).dynsym_index == pop_dynsym_index) {
3502 assert(!unversioned_gsi.ptr(elf).status.owns_dynsym);
3503 unversioned_gsi.ptr(elf).dynsym_index = @intCast(dynsym_index);
3504 try elf.changed_symtab_index.put(gpa, unversioned_gsi, {});
3505 }
3506 }
3507 }
3033 }3508 }
3509
3510 // Now that we've given that symbol a new home, actually decrease the section size.
3511 elf.targetStore(&dynsym_shdr.size, new_size);
3512
3513 const versym_shdr = @field(elf.shdrPtr(elf.shndx.gnu_version), @tagName(class));
3514 assert(elf.targetLoad(&versym_shdr.size) == (pop_dynsym_index + 1) * 2);
3515 elf.targetStore(&versym_shdr.size, pop_dynsym_index * 2);
3034 },3516 },
3035 }3517 }
3518
3519 return apply_relocs;
3036}3520}
30373521
3038const Symbol = struct {3522const Symbol = struct {
...@@ -3047,20 +3531,336 @@ const Symbol = struct {...@@ -3047,20 +3531,336 @@ const Symbol = struct {
3047 first_target_reloc: SymbolReloc.Index,3531 first_target_reloc: SymbolReloc.Index,
30483532
3049 const Global = struct {3533 const Global = struct {
3534 status: packed struct(u8) {
3535 /// We cannot reliably recover this information from the symbol table, because symbols
3536 /// with visibility `STV_HIDDEN` are "demoted" to `STB_LOCAL` binding, yet we must still
3537 /// consider them strong or weak for the purposes of the link.
3538 bind: Symbol.Global.Bind,
3539 /// Initially set to `false`. If a relocation is added which targets this symbol and
3540 /// wants it to have a statically-known value, this flag is set to signal to
3541 /// `updateGlobalDynamic` that it may need to create a copy relocation.
3542 want_static_value: bool,
3543 /// Every entry in `.dynsym` is "owned" by exactly one `Global`. If this flag is set,
3544 /// then `Global.dynsym_index` is non-zero and refers to a dynsym entry owned by this
3545 /// global. Otherwise, if `Global.dynsym_index` is non-zero, this `Global` is an "alias"
3546 /// of a default version of this symbol (e.g. 'foo' is an alias of 'foo@@version') and
3547 /// `Global.dynsym_index` refers to that symbol's dynsym entry
3548 owns_dynsym: bool,
3549 any_static_target_relocs: bool,
3550 any_static_relative_target_relocs: bool,
3551 any_dynamic_target_relocs: bool,
3552 _: u2 = 0,
3553 },
3554
3555 dynsym_index: u32,
3556
3050 /// The current index of the symtab entry for this global symbol.3557 /// The current index of the symtab entry for this global symbol.
3051 symtab_index: Symbol.Index,3558 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,
30553559
3056 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.3560 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
3057 ///3561 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.3562 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
3061 ///3563 prev_in_node: Symbol.Global.Index.Optional,
3062 /// If `node` is `.none`, this is `.empty`.3564
3063 prev_in_node: String(.strtab),3565 /// The logical key of `Elf.globals_by_name`. Although versioned symbol names are stored in
3566 /// the form "foo@version" or "foo@@version" in the symbol table, it is generally convenient
3567 /// to consider the name and version as separate values.
3568 const VersionedName = struct {
3569 name: []const u8,
3570 version: ?[]const u8,
3571
3572 const Adapter = struct {
3573 elf: *Elf,
3574 pub fn eql(ctx: Adapter, lhs_key: VersionedName, _: void, rhs_index: usize) bool {
3575 const elf = ctx.elf;
3576 const rhs_gsi: Symbol.Global.Index = @fromBackingInt(@intCast(rhs_index));
3577
3578 if (!std.mem.eql(u8, lhs_key.name, rhs_gsi.name(elf))) return false;
3579
3580 if (lhs_key.version) |lhs_version| {
3581 const rhs_version = rhs_gsi.version(elf) orelse return false;
3582 return std.mem.eql(u8, lhs_version, rhs_version);
3583 } else {
3584 return rhs_gsi.version(elf) == null;
3585 }
3586 }
3587 pub fn hash(ctx: Adapter, key: VersionedName) u32 {
3588 _ = ctx;
3589 var h: std.hash.Wyhash = .init(0);
3590 h.update(key.name);
3591 if (key.version) |version| {
3592 h.update("@");
3593 h.update(version);
3594 }
3595 return @truncate(h.final());
3596 }
3597 };
3598 };
3599
3600 const DefaultVersionAdapter = struct {
3601 elf: *Elf,
3602 pub fn eql(ctx: DefaultVersionAdapter, lhs_name: []const u8, rhs_gsi: Symbol.Global.Index, _: usize) bool {
3603 const elf = ctx.elf;
3604 return std.mem.eql(u8, lhs_name, rhs_gsi.name(elf));
3605 }
3606 pub fn hash(ctx: DefaultVersionAdapter, name: []const u8) u32 {
3607 _ = ctx;
3608 return std.array_hash_map.hashString(name);
3609 }
3610 };
3611
3612 const Index = enum(u32) {
3613 _,
3614
3615 fn ptr(gsi: Symbol.Global.Index, elf: *Elf) *Global {
3616 return &elf.globals.items[@backingInt(gsi)];
3617 }
3618
3619 /// Does not traverse aliases: if 'foo' is an alias for 'foo@@version', then `false`
3620 /// will be returned for 'foo', even thought 'foo@@version' is defined.
3621 fn defined(gsi: Symbol.Global.Index, elf: *Elf) bool {
3622 return switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3623 inline else => |sym| elf.targetLoad(&sym.shndx) != std.elf.SHN_UNDEF,
3624 };
3625 }
3626
3627 fn rawName(gsi: Symbol.Global.Index, elf: *Elf) String(.strtab) {
3628 return switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3629 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
3630 };
3631 }
3632 fn name(gsi: Symbol.Global.Index, elf: *Elf) []const u8 {
3633 return parseVersionedSymbolName(gsi.rawName(elf).slice(elf)).name;
3634 }
3635 fn version(gsi: Symbol.Global.Index, elf: *Elf) ?[]const u8 {
3636 return parseVersionedSymbolName(gsi.rawName(elf).slice(elf)).version;
3637 }
3638 fn isDefaultDefinition(gsi: Symbol.Global.Index, elf: *Elf) bool {
3639 return parseVersionedSymbolName(gsi.rawName(elf).slice(elf)).is_default_version;
3640 }
3641
3642 fn dynsymIndex(gsi: Symbol.Global.Index, elf: *Elf) ?u32 {
3643 const dynsym_index = gsi.ptr(elf).dynsym_index;
3644 if (dynsym_index == 0) {
3645 return null;
3646 } else {
3647 return dynsym_index;
3648 }
3649 }
3650
3651 /// One global symbol may be an "alias" of another, which means that the global's value
3652 /// is defined to be equal to the value of the other. The original global's symbol table
3653 /// entry is effectively ignored while it is acting as an alias, and it will not own an
3654 /// entry in the dynamic symbol table.
3655 ///
3656 /// This function will return the symbol for which `gsi` is an alias. If `gsi` is not an
3657 /// alias, then `gsi` itself is returned.
3658 ///
3659 /// At the time of writing, there is only one situation in which one global may alias
3660 /// another: if an unversioned symbol "foo" is undefined, and a default-versioned symbol
3661 /// "foo@@version" is defined, then "foo" is considered an alias for "foo@@version". We
3662 /// cannot simply combine the globals, because it is possible that a future incremental
3663 /// update would cause the globals to become separate again (e.g. due to a definition of
3664 /// "foo@@version" being replaced with a definition for "foo@version", which is the same
3665 /// symbol but no longer acting as a default version).
3666 ///
3667 /// See also `findAliaser`, which maps in the opposite direction, i.e. given a global,
3668 /// returns all globals which alias it.
3669 fn resolveAlias(gsi: Symbol.Global.Index, elf: *Elf) Symbol.Global.Index {
3670 if (elf.ehdrType() == .REL) {
3671 // It's too early to combine 'foo@@v' with 'foo'---let the final static linker
3672 // invocation do that.
3673 return gsi;
3674 }
3675
3676 if (gsi.version(elf) != null) {
3677 // An explicitly versioned symbol is not overriden by a default version.
3678 return gsi;
3679 }
3680
3681 const default_gsi: Symbol.Global.Index = default_gsi: {
3682 if (elf.default_version_globals.getIndexAdapted(
3683 gsi.name(elf),
3684 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
3685 )) |index| {
3686 break :default_gsi elf.default_version_globals.keys()[index];
3687 }
3688 if (elf.dso_globals.findDefaultVersion(gsi.name(elf))) |dso_global| {
3689 if (elf.globalByName(.{
3690 .name = gsi.name(elf),
3691 .version = dso_global.version.unwrap().?.slice(&elf.dso_globals),
3692 })) |default_gsi| {
3693 break :default_gsi default_gsi;
3694 }
3695 }
3696 return gsi; // no default symbol version exists
3697 };
3698
3699 if (!gsi.defined(elf)) {
3700 // Always use the default version if the unversioned symbol isn't defined.
3701 return default_gsi;
3702 } else if (!default_gsi.defined(elf)) {
3703 // If the unverisoned symbol is defined and the default version is undefined,
3704 // then ignore the default version: local definitions always take priority.
3705 return gsi;
3706 }
3707
3708 // `gsi` and `default_gsi` are both defined: the winner depends on the symbol binds.
3709 // If both are strong, there will be an error, so the answer doesn't matter. In that
3710 // case, we arbitrarily choose to consider the versioned symbol the winner. This is
3711 // useful because it makes it easier for `updateGlobalDynamic` to detect this case
3712 // and track it in `elf.defined_alias_globals`.
3713 return switch (gsi.ptr(elf).status.bind) {
3714 .strong => switch (default_gsi.ptr(elf).status.bind) {
3715 .strong => default_gsi,
3716 .weak => gsi,
3717 },
3718 .weak => default_gsi,
3719 };
3720 }
3721
3722 /// Effectively the opposite of `resolveAlias`; given a global symbol, returns the index
3723 /// of the single other global which aliases it, if any. The signature of this function
3724 /// implies that each global may have at most one global which is an alias for it.
3725 fn findAliaser(gsi: Symbol.Global.Index, elf: *Elf) ?Symbol.Global.Index {
3726 if (gsi.version(elf) == null) {
3727 return null;
3728 }
3729 const unversioned_gsi = elf.globalByName(.{
3730 .name = gsi.name(elf),
3731 .version = null,
3732 }) orelse {
3733 return null;
3734 };
3735 assert(unversioned_gsi != gsi);
3736 if (unversioned_gsi.resolveAlias(elf) == gsi) {
3737 return unversioned_gsi;
3738 }
3739 return null;
3740 }
3741
3742 /// Like `dynsymIndex`, except also returns `null` if `gsi` is the unversioned alias for
3743 /// a default symbol version. This means that for each dynsym entry, there is exactly
3744 /// one `Global` which will return that dynsym index here.
3745 fn ownedDynsymIndex(gsi: Symbol.Global.Index, elf: *Elf) ?u32 {
3746 if (gsi.ptr(elf).status.owns_dynsym) {
3747 const dynsym_index = gsi.ptr(elf).dynsym_index;
3748 assert(dynsym_index != 0);
3749 return dynsym_index;
3750 }
3751 return null;
3752 }
3753
3754 /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation
3755 /// entries, and adds `R_*_RELATIVE` relocation entries as needed.
3756 ///
3757 /// Asserts we are creating a DSO.
3758 fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void {
3759 if (elf.shndx.dynamic == .UNDEF) return;
3760 const sym_id: Symbol.Id = .global(gsi);
3761 switch (elf.classifySymbolValue(sym_id)) {
3762 .static => {
3763 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
3764 !gsi.ptr(elf).status.any_static_relative_target_relocs)
3765 {
3766 return;
3767 }
3768 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
3769 gsi.ptr(elf).status.any_static_relative_target_relocs = false;
3770 gsi.ptr(elf).status.any_static_target_relocs = true;
3771 },
3772 .static_relative => {
3773 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
3774 !gsi.ptr(elf).status.any_static_target_relocs)
3775 {
3776 return;
3777 }
3778 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
3779 gsi.ptr(elf).status.any_static_target_relocs = false;
3780 gsi.ptr(elf).status.any_static_relative_target_relocs = true;
3781 },
3782 // TODO: this function needs to support a symbol becoming dynamic which wasn't
3783 // previously dynamic (and when it does, the function should be renamed to
3784 // `updateTargetOutputRelocs`). But for now, this isn't supported.
3785 .dynamic => unreachable,
3786 }
3787 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3788 while (ri != .none) {
3789 const reloc = ri.get(elf);
3790 assert(reloc.target == sym_id);
3791 reloc.deleteOutputRel(elf);
3792 ri = reloc.next;
3793 }
3794 switch (elf.classifySymbolValue(sym_id)) {
3795 .static => return,
3796 .static_relative => {},
3797 .dynamic => unreachable,
3798 }
3799 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
3800 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3801 while (ri != .none) {
3802 const reloc = ri.get(elf);
3803 ri = reloc.next;
3804 assert(reloc.target == sym_id);
3805 switch (reloc.type.target) {
3806 // Only relocations which resolve to absolute addresses require runtime
3807 // `R_*_RELATIVE` relocations.
3808 .special,
3809 .pltrel,
3810 .rel,
3811 .dtpoff,
3812 .tpoff,
3813 .size,
3814 => continue,
3815
3816 .abs, .pltabs => {},
3817 }
3818 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
3819 const node = reloc.node.unwrap().?;
3820 switch (elf.nodeWantsDsoRelocation(node)) {
3821 .no => continue,
3822 .yes_textrel => elf.textrel_count += 1,
3823 .yes => {},
3824 }
3825 // There is capacity for a relocation because we just deleted one earlier.
3826 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3827 .type = .relative(elf),
3828 .offset = elf.getNodeVAddr(node) + reloc.offset,
3829 .raw_sym_index = 0,
3830 .addend = 0,
3831 }).toOptional();
3832 }
3833 }
3834
3835 const Optional = enum(u32) {
3836 none = std.math.maxInt(u32),
3837 _,
3838
3839 fn wrap(gsi: Symbol.Global.Index) Symbol.Global.Index.Optional {
3840 return @bitCast(gsi);
3841 }
3842 fn unwrap(ogsi: Symbol.Global.Index.Optional) ?Symbol.Global.Index {
3843 return switch (ogsi) {
3844 .none => null,
3845 _ => @fromBackingInt(@backingInt(ogsi)),
3846 };
3847 }
3848 };
3849 };
3850
3851 const Bind = enum(u1) { strong, weak };
3852
3853 const AddOptions = struct {
3854 node: MappedFile.Node.Index.Optional,
3855 name: []const u8,
3856 lib_name: ?[]const u8 = null,
3857 value: u64,
3858 size: u64,
3859 type: std.elf.STT,
3860 bind: Symbol.Global.Bind,
3861 visibility: std.elf.STV,
3862 shndx: Section.Index,
3863 };
3064 };3864 };
30653865
3066 /// An index directly into the symtab. These values are not stable (global symbols are sometimes3866 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
...@@ -3107,12 +3907,12 @@ const Symbol = struct {...@@ -3107,12 +3907,12 @@ const Symbol = struct {
3107 fn local(lsi: Symbol.LocalIndex) Symbol.Id {3907 fn local(lsi: Symbol.LocalIndex) Symbol.Id {
3108 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };3908 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };
3109 }3909 }
3110 fn global(name: String(.strtab)) Symbol.Id {3910 fn global(gsi: Symbol.Global.Index) Symbol.Id {
3111 return .{ .kind = .global, .raw = @intCast(@backingInt(name)) };3911 return .{ .kind = .global, .raw = @intCast(@backingInt(gsi)) };
3112 }3912 }
3113 fn unwrap(s: Symbol.Id) union(enum) {3913 fn unwrap(s: Symbol.Id) union(enum) {
3114 local: Symbol.LocalIndex,3914 local: Symbol.LocalIndex,
3115 global: String(.strtab),3915 global: Symbol.Global.Index,
3116 } {3916 } {
3117 return switch (s.kind) {3917 return switch (s.kind) {
3118 .local => .{ .local = @fromBackingInt(s.raw) },3918 .local => .{ .local = @fromBackingInt(s.raw) },
...@@ -3127,10 +3927,20 @@ const Symbol = struct {...@@ -3127,10 +3927,20 @@ const Symbol = struct {
3127 return @bitCast(s);3927 return @bitCast(s);
3128 }3928 }
31293929
3130 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {3930 fn index(s: Symbol.Id, elf: *Elf) Symbol.Index {
3131 return switch (s.unwrap()) {3931 return switch (s.unwrap()) {
3132 .local => |lsi| lsi.index(),3932 .local => |lsi| lsi.index(),
3133 .global => |name| elf.globalByName(name).?.symtab_index,3933 .global => |gsi| gsi.ptr(elf).symtab_index,
3934 };
3935 }
3936
3937 /// Returns the `Symbol.Id` which determines the resolved value, size, etc of `s`.
3938 ///
3939 /// See `Symbol.Global.Index.resolveAlias` for details.
3940 fn resolveAlias(s: Symbol.Id, elf: *Elf) Symbol.Id {
3941 return switch (s.unwrap()) {
3942 .local => |lsi| .local(lsi),
3943 .global => |gsi| .global(gsi.resolveAlias(elf)),
3134 };3944 };
3135 }3945 }
31363946
...@@ -3138,37 +3948,53 @@ const Symbol = struct {...@@ -3138,37 +3948,53 @@ const Symbol = struct {
3138 /// global for which we have emitted a copy relocation, returns the virtual address of that3948 /// global for which we have emitted a copy relocation, returns the virtual address of that
3139 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.3949 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
3140 fn value(s: Symbol.Id, elf: *Elf) u64 {3950 fn value(s: Symbol.Id, elf: *Elf) u64 {
3141 return switch (elf.symPtr(s.index(elf))) {3951 return switch (elf.symPtr(s.resolveAlias(elf).index(elf))) {
3952 inline else => |sym| elf.targetLoad(&sym.value),
3953 };
3954 }
3955
3956 fn size(s: Symbol.Id, elf: *Elf) u64 {
3957 return switch (elf.symPtr(s.resolveAlias(elf).index(elf))) {
3142 inline else => |sym| elf.targetLoad(&sym.value),3958 inline else => |sym| elf.targetLoad(&sym.value),
3143 };3959 };
3144 }3960 }
31453961
3146 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {3962 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
3147 // Update the symbol value in `.symtab`
3148 const sym_index = sym_id.index(elf);3963 const sym_index = sym_id.index(elf);
3149 switch (elf.symPtr(sym_index)) {3964 switch (elf.symPtr(sym_index)) {
3150 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),3965 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3151 }3966 }
31523967
3153 // Update the symbol value in `.dynsym` if applicable
3154 switch (sym_id.unwrap()) {3968 switch (sym_id.unwrap()) {
3155 .local => {},3969 .local => {},
3156 .global => |name| {3970 .global => |gsi| if (gsi.ownedDynsymIndex(elf)) |dynsym_index| {
3157 const g = elf.globalByName(name).?;3971 switch (elf.dynsymPtr(dynsym_index)) {
3158 if (g.dynsym_index != 0) {3972 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3159 switch (elf.dynsymPtr(g.dynsym_index)) {
3160 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3161 }
3162 }3973 }
3163 },3974 },
3164 }3975 }
31653976
3166 // Re-apply relocations targeting this symbol3977 sym_id.applyTargetRelocs(elf);
3978
3979 switch (sym_id.unwrap()) {
3980 .local => {},
3981 .global => |gsi| if (gsi.findAliaser(elf)) |alias_gsi| {
3982 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
3983 },
3984 }
3985 }
3986
3987 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3167 if (elf.ehdrType() != .REL) {3988 if (elf.ehdrType() != .REL) {
3168 sym_id.applyTargetRelocs(elf);3989 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3990 while (ri != .none) {
3991 const reloc = ri.get(elf);
3992 assert(reloc.target == sym_id);
3993 reloc.apply(elf);
3994 ri = reloc.next;
3995 }
3169 }3996 }
31703997
3171 // Update GOT entries targeting this symbol
3172 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {3998 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
3173 elf.updateGotEntry(got_index);3999 elf.updateGotEntry(got_index);
3174 }4000 }
...@@ -3181,81 +4007,16 @@ const Symbol = struct {...@@ -3181,81 +4007,16 @@ const Symbol = struct {
3181 }4007 }
3182 }4008 }
31834009
3184 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3185 assert(elf.ehdrType() != .REL);
3186 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3187 while (ri != .none) {
3188 const reloc = ri.get(elf);
3189 assert(reloc.target == sym_id);
3190 reloc.apply(elf);
3191 ri = reloc.next;
3192 }
3193 }
3194
3195 /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic
3196 /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required.
3197 ///
3198 /// Asserts we are creating a DSO.
3199 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3200 assert(elf.ehdrType() != .REL);
3201 assert(elf.shndx.dynamic != .UNDEF);
3202 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3203 while (ri != .none) {
3204 const reloc = ri.get(elf);
3205 assert(reloc.target == sym_id);
3206 reloc.deleteOutputRel(elf);
3207 ri = reloc.next;
3208 }
3209 switch (elf.classifySymbolValue(sym_id)) {
3210 .static => return,
3211 .static_relative => {},
3212 .dynamic => unreachable,
3213 }
3214 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
3215 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3216 while (ri != .none) {
3217 const reloc = ri.get(elf);
3218 ri = reloc.next;
3219 assert(reloc.target == sym_id);
3220 switch (reloc.type.target) {
3221 // Only relocations which resolve to absolute addresses require runtime
3222 // `R_*_RELATIVE` relocations.
3223 .special,
3224 .pltrel,
3225 .rel,
3226 .dtpoff,
3227 .tpoff,
3228 .size,
3229 => continue,
3230
3231 .abs, .pltabs => {},
3232 }
3233 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
3234 const node = reloc.node.unwrap().?;
3235 switch (elf.nodeWantsDsoRelocation(node)) {
3236 .no => continue,
3237 .yes_textrel => elf.textrel_count += 1,
3238 .yes => {},
3239 }
3240 // There is capacity for a relocation because we just deleted one earlier.
3241 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3242 .type = .relative(elf),
3243 .offset = elf.getNodeVAddr(node) + reloc.offset,
3244 .raw_sym_index = 0,
3245 .addend = 0,
3246 }).toOptional();
3247 }
3248 }
3249
3250 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at4010 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
3251 /// some point due to a call to `flushMoved`.4011 /// some point due to a call to `flushMoved`.
3252 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {4012 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
3253 if (s.index(elf).ptr(elf).node.unwrap()) |node| {4013 const resolved = s.resolveAlias(elf);
4014 if (resolved.index(elf).ptr(elf).node.unwrap()) |node| {
3254 return node.hasMoved(&elf.mf);4015 return node.hasMoved(&elf.mf);
3255 }4016 }
3256 switch (s.unwrap()) {4017 switch (resolved.unwrap()) {
3257 .local => {},4018 .local => {},
3258 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {4019 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {
3259 return copied_global.node.hasMoved(&elf.mf);4020 return copied_global.node.hasMoved(&elf.mf);
3260 },4021 },
3261 }4022 }
...@@ -3264,12 +4025,33 @@ const Symbol = struct {...@@ -3264,12 +4025,33 @@ const Symbol = struct {
3264 };4025 };
3265};4026};
32664027
3267fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {4028fn globalByName(elf: *Elf, name: Symbol.Global.VersionedName) ?Symbol.Global.Index {
3268 if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;4029 const adapter: Symbol.Global.VersionedName.Adapter = .{ .elf = elf };
3269 if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;4030 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;
3270 if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;4031 return @fromBackingInt(@intCast(index_raw));
3271 if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;4032}
3272 return null;4033fn globalBySym(elf: *Elf, sym_index: Symbol.Index) Symbol.Global.Index {
4034 const raw_name: String(.strtab) = switch (elf.symPtr(sym_index)) {
4035 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
4036 };
4037 const name = parseVersionedSymbolName(raw_name.slice(elf));
4038 return elf.globalByName(.{ .name = name.name, .version = name.version }).?;
4039}
4040fn globalByDynsym(elf: *Elf, dynsym_index: u32) Symbol.Global.Index {
4041 // Versioned symbols have different names in the normal symbol table vs the dynamic symbol
4042 // table. In those cases, a hash map holds an "override":
4043 if (elf.versioned_dynsym_owners.get(dynsym_index)) |gsi| {
4044 assert(gsi.ownedDynsymIndex(elf).? == dynsym_index);
4045 return gsi;
4046 }
4047 // For all other symbols, we can just take the name of the dynamic symbol table entry, and look
4048 // up the global with that name.
4049 const name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
4050 inline else => |dynsym| @fromBackingInt(elf.targetLoad(&dynsym.name)),
4051 };
4052 const gsi = elf.globalByName(.{ .name = name.slice(elf), .version = null }).?;
4053 assert(gsi.ownedDynsymIndex(elf).? == dynsym_index);
4054 return gsi;
3273}4055}
32744056
3275fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {4057fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
...@@ -3295,23 +4077,25 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -3295,23 +4077,25 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3295 return .static;4077 return .static;
3296 }4078 }
32974079
3298 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) {4080 const resolved_sym = sym.resolveAlias(elf);
4081
4082 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) {
3299 inline else => |sym_ptr| .{4083 inline else => |sym_ptr| .{
3300 .fromSection(elf.targetLoad(&sym_ptr.shndx)),4084 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
3301 elf.targetLoad(&sym_ptr.other).visibility,4085 elf.targetLoad(&sym_ptr.other).visibility,
3302 },4086 },
3303 };4087 };
33044088
3305 switch (sym.unwrap()) {4089 switch (resolved_sym.unwrap()) {
3306 .local => {4090 .local => {
3307 assert(shndx != .UNDEF);4091 assert(shndx != .UNDEF);
3308 assert(visibility == .DEFAULT);4092 assert(visibility == .DEFAULT);
3309 },4093 },
3310 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {4094 .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 runtime4095 // 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.4096 // preemption, so a dynamic relocation is required for it even if we have a definition.
3313 return .dynamic;4097 return .dynamic;
3314 } else if (elf.copied_globals.contains(name)) {4098 } else if (elf.copied_globals.contains(gsi)) {
3315 // This becomes a locally-defined symbol in `.data`.4099 // This becomes a locally-defined symbol in `.data`.
3316 return if (runtime_load_addr) .static_relative else .static;4100 return if (runtime_load_addr) .static_relative else .static;
3317 },4101 },
...@@ -3321,7 +4105,9 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -3321,7 +4105,9 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3321 .UNDEF => switch (visibility) {4105 .UNDEF => switch (visibility) {
3322 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {4106 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
3323 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section4107 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
3324 // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`.4108 // This is a static PIE---we'll see a definition at some point, so there'd be no
4109 // point in adding an invalid dynamic relocation (the only valid dynamic relation
4110 // type in a static PIE is `R_*_RELATIVE`).
3325 return .static;4111 return .static;
3326 } else .dynamic, // external symbol4112 } else .dynamic, // external symbol
33274113
...@@ -3396,7 +4182,6 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.Sy...@@ -3396,7 +4182,6 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.Sy
3396fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {4182fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {
3397 const gpa = elf.base.comp.gpa;4183 const gpa = elf.base.comp.gpa;
33984184
3399 try elf.ensureUnusedSymbolCapacity(1, .all_local);
3400 try elf.nodes.ensureUnusedCapacity(gpa, 1);4185 try elf.nodes.ensureUnusedCapacity(gpa, 1);
3401 try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);4186 try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);
34024187
...@@ -3417,9 +4202,9 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol...@@ -3417,9 +4202,9 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol
3417 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch4202 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch
3418 unreachable;4203 unreachable;
3419 gop.value_ptr.* = .{4204 gop.value_ptr.* = .{
3420 .lsi = elf.addLocalSymbolAssumeCapacity(.{4205 .lsi = try elf.addLocalSymbol(.{
3421 .node = .wrap(node),4206 .node = .wrap(node),
3422 .name = try elf.string(.strtab, name),4207 .name = name,
3423 .value = 0,4208 .value = 0,
3424 .size = 0,4209 .size = 0,
3425 .type = sym_type,4210 .type = sym_type,
...@@ -3448,10 +4233,9 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb...@@ -3448,10 +4233,9 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb
3448 }).toTypeErased();4233 }).toTypeErased();
3449}4234}
3450fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {4235fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3451 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);4236 const gsi = elf.addGlobalSymbol(.{
3452 const symbol = elf.addGlobalSymbolAssumeCapacity(.{
3453 .node = .none,4237 .node = .none,
3454 .name = try .string(elf, opts.name),4238 .name = opts.name,
3455 .lib_name = opts.lib_name,4239 .lib_name = opts.lib_name,
3456 .value = 0,4240 .value = 0,
3457 .size = 0,4241 .size = 0,
...@@ -3470,8 +4254,14 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {...@@ -3470,8 +4254,14 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3470 .shndx = .UNDEF,4254 .shndx = .UNDEF,
3471 }) catch |err| switch (err) {4255 }) catch |err| switch (err) {
3472 error.MultipleDefinitions => unreachable, // shndx is undef4256 error.MultipleDefinitions => unreachable, // shndx is undef
4257 error.MultipleDefaultVersions => unreachable, // shndx is undef
4258 error.UndefinedDefaultVersion => return elf.base.comp.link_diags.fail(
4259 "symbol '{s}' specifies default version without defining it",
4260 .{opts.name},
4261 ),
4262 else => |e| return e,
3473 };4263 };
3474 return symbol;4264 return .global(gsi);
3475}4265}
3476pub fn addReloc(4266pub fn addReloc(
3477 elf: *Elf,4267 elf: *Elf,
...@@ -3618,8 +4408,21 @@ fn String(section: StringSection) type {...@@ -3618,8 +4408,21 @@ fn String(section: StringSection) type {
3618 };4408 };
3619}4409}
3620fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {4410fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {
4411 if (key.len == 0) {
4412 // Special case to allow calls from `initHeaders` before the strtab is initialized.
4413 return .empty;
4414 }
4415 const st: *StringTable = &@field(elf, @tagName(section));
4416 try st.ensureAdditionalCapacity(elf, section.shndx(elf), key.len);
4417 return @fromBackingInt(st.getAssumeCapacity(elf, section.shndx(elf), key));
4418}
4419fn ensureAdditionalStringCapacity(elf: *Elf, comptime section: StringSection, string_len: usize) Error!void {
3621 const st: *StringTable = &@field(elf, @tagName(section));4420 const st: *StringTable = &@field(elf, @tagName(section));
3622 return @fromBackingInt(try st.get(elf, section.shndx(elf), key));4421 try st.ensureAdditionalCapacity(elf, section.shndx(elf), string_len);
4422}
4423fn stringAssumeCapacity(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
4424 const st: *StringTable = &@field(elf, @tagName(section));
4425 return @fromBackingInt(st.getAssumeCapacity(elf, section.shndx(elf), key));
3623}4426}
3624/// Like `string`, but asserts that the string is already in `section`.4427/// Like `string`, but asserts that the string is already in `section`.
3625fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {4428fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
...@@ -3663,35 +4466,41 @@ const StringTable = struct {...@@ -3663,35 +4466,41 @@ const StringTable = struct {
3663 return st.map.getKeyAdapted(key, adapter).?;4466 return st.map.getKeyAdapted(key, adapter).?;
3664 }4467 }
36654468
3666 fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 {4469 fn ensureAdditionalCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, string_len: usize) Error!void {
4470 const gpa = elf.base.comp.gpa;
4471 const ni = shndx.get(elf).ni;
4472 const slice_const = ni.sliceConst(&elf.mf);
4473 try st.map.ensureUnusedCapacityContext(gpa, 1, .{ .slice = slice_const });
4474 const size: u64 = switch (elf.shdrPtr(shndx)) {
4475 inline else => |shdr| elf.targetLoad(&shdr.size),
4476 };
4477 try ni.ensureMinimumSize(gpa, &elf.mf, size + string_len + 1);
4478 }
4479
4480 fn getAssumeCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) u32 {
3667 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special4481 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
3668 // case the empty string.4482 // case the empty string.
3669 if (key.len == 0) return 0;4483 if (key.len == 0) return 0;
36704484
3671 const gpa = elf.base.comp.gpa;
3672 const ni = shndx.get(elf).ni;4485 const ni = shndx.get(elf).ni;
3673 const slice_const = ni.sliceConst(&elf.mf);4486 const slice_const = ni.sliceConst(&elf.mf);
3674 const gop = try st.map.getOrPutContextAdapted(4487 const gop = st.map.getOrPutAssumeCapacityAdapted(
3675 gpa,
3676 key,4488 key,
3677 StringTable.Adapter{ .slice = slice_const },4489 StringTable.Adapter{ .slice = slice_const },
3678 .{ .slice = slice_const },
3679 );4490 );
3680 if (gop.found_existing) return gop.key_ptr.*;4491 if (gop.found_existing) return gop.key_ptr.*;
3681 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {4492 const offset: u32 = switch (elf.shdrPtr(shndx)) {
3682 inline else => |shdr| {4493 inline else => |shdr| offset: {
3683 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));4494 const old_size = elf.targetLoad(&shdr.size);
3684 const new_size: u32 = @intCast(old_size + key.len + 1);4495 elf.targetStore(&shdr.size, @intCast(old_size + key.len + 1));
3685 elf.targetStore(&shdr.size, new_size);4496 break :offset @intCast(old_size);
3686 break :size .{ old_size, new_size };
3687 },4497 },
3688 };4498 };
3689 try ni.ensureMinimumSize(gpa, &elf.mf, new_size);4499 const slice = ni.slice(&elf.mf)[offset..];
3690 const slice = ni.slice(&elf.mf)[old_size..];
3691 @memcpy(slice[0..key.len], key);4500 @memcpy(slice[0..key.len], key);
3692 slice[key.len] = 0;4501 slice[key.len] = 0;
3693 gop.key_ptr.* = old_size;4502 gop.key_ptr.* = offset;
3694 return old_size;4503 return offset;
3695 }4504 }
3696};4505};
36974506
...@@ -3730,7 +4539,7 @@ fn create(...@@ -3730,7 +4539,7 @@ fn create(
3730 .big => .@"2MSB",4539 .big => .@"2MSB",
3731 };4540 };
3732 const osabi: std.elf.OSABI = switch (target.os.tag) {4541 const osabi: std.elf.OSABI = switch (target.os.tag) {
3733 else => if (target.abi.isGnu()) .GNU else .NONE,4542 else => .NONE, // might be changed to `.GNU` by `checkInputIdent`
3734 .freestanding, .other => .STANDALONE,4543 .freestanding, .other => .STANDALONE,
3735 .netbsd => .NETBSD,4544 .netbsd => .NETBSD,
3736 .illumos => .SOLARIS,4545 .illumos => .SOLARIS,
...@@ -3814,6 +4623,9 @@ fn create(...@@ -3814,6 +4623,9 @@ fn create(
3814 .tdata = .UNDEF,4623 .tdata = .UNDEF,
3815 .rela_dyn = .UNDEF,4624 .rela_dyn = .UNDEF,
3816 .rela_plt = .UNDEF,4625 .rela_plt = .UNDEF,
4626 .gnu_version = .UNDEF,
4627 .gnu_version_d = .UNDEF,
4628 .gnu_version_r = .UNDEF,
3817 .debug_abbrev = .UNDEF,4629 .debug_abbrev = .UNDEF,
3818 .debug_addr = .UNDEF,4630 .debug_addr = .UNDEF,
3819 .eh_frame_hdr = .UNDEF,4631 .eh_frame_hdr = .UNDEF,
...@@ -3836,16 +4648,25 @@ fn create(...@@ -3836,16 +4648,25 @@ fn create(
3836 .soname = .empty,4648 .soname = .empty,
3837 },4649 },
3838 .symtab = .empty,4650 .symtab = .empty,
3839 .globals = .{4651 .globals = .empty,
3840 .strong_def = .empty,4652 .globals_by_name = .empty,
3841 .weak_def = .empty,4653 .default_version_globals = .empty,
3842 .strong_undef = .empty,4654 .unknown_globals = .empty,
3843 .weak_undef = .empty,4655 .defined_alias_globals = .empty,
3844 },
3845 .copied_globals = .empty,4656 .copied_globals = .empty,
3846 .want_copied_globals = .empty,4657 .versioned_dynsym_owners = .empty,
4658 .verdef = .empty,
4659 .verneed = .empty,
4660 .last_verneed_file_index = 0,
4661 // This is initially 2 because that is the first user-defined version index.
4662 // 0 and 1 are reserved for `VER_NDX_LOCAL` and `VER_NDX_GLOBAL`.
4663 .next_version_id = 2,
3847 .node_global_symbols = .empty,4664 .node_global_symbols = .empty,
3848 .dso_globals = .empty,4665 .dso_globals = .{
4666 .string_bytes = .empty,
4667 .symbols = .empty,
4668 .default_sym_vers = .empty,
4669 },
3849 .shstrtab = .{ .map = .empty },4670 .shstrtab = .{ .map = .empty },
3850 .strtab = .{ .map = .empty },4671 .strtab = .{ .map = .empty },
3851 .dynstr = .{ .map = .empty },4672 .dynstr = .{ .map = .empty },
...@@ -3915,14 +4736,19 @@ pub fn deinit(elf: *Elf) void {...@@ -3915,14 +4736,19 @@ pub fn deinit(elf: *Elf) void {
3915 elf.shdrs.deinit(gpa);4736 elf.shdrs.deinit(gpa);
3916 elf.phdrs.deinit(gpa);4737 elf.phdrs.deinit(gpa);
3917 elf.symtab.deinit(gpa);4738 elf.symtab.deinit(gpa);
3918 elf.globals.strong_def.deinit(gpa);4739 elf.globals.deinit(gpa);
3919 elf.globals.weak_def.deinit(gpa);4740 elf.globals_by_name.deinit(gpa);
3920 elf.globals.strong_undef.deinit(gpa);4741 elf.default_version_globals.deinit(gpa);
3921 elf.globals.weak_undef.deinit(gpa);4742 elf.unknown_globals.deinit(gpa);
4743 elf.defined_alias_globals.deinit(gpa);
3922 elf.copied_globals.deinit(gpa);4744 elf.copied_globals.deinit(gpa);
3923 elf.want_copied_globals.deinit(gpa);4745 elf.versioned_dynsym_owners.deinit(gpa);
4746 elf.verdef.deinit(gpa);
4747 elf.verneed.deinit(gpa);
3924 elf.node_global_symbols.deinit(gpa);4748 elf.node_global_symbols.deinit(gpa);
3925 elf.dso_globals.deinit(gpa);4749 elf.dso_globals.string_bytes.deinit(gpa);
4750 elf.dso_globals.symbols.deinit(gpa);
4751 elf.dso_globals.default_sym_vers.deinit(gpa);
3926 elf.shstrtab.map.deinit(gpa);4752 elf.shstrtab.map.deinit(gpa);
3927 elf.strtab.map.deinit(gpa);4753 elf.strtab.map.deinit(gpa);
3928 elf.dynstr.map.deinit(gpa);4754 elf.dynstr.map.deinit(gpa);
...@@ -4015,6 +4841,9 @@ fn initHeaders(...@@ -4015,6 +4841,9 @@ fn initHeaders(
4015 shnum += 1; // .dynstr4841 shnum += 1; // .dynstr
4016 shnum += 1; // .dynsym4842 shnum += 1; // .dynsym
4017 shnum += 1; // .hash4843 shnum += 1; // .hash
4844 shnum += 1; // .gnu.version
4845 shnum += 1; // .gnu.version_d
4846 shnum += 1; // .gnu.version_r
4018 shnum += 1; // .rela.dyn4847 shnum += 1; // .rela.dyn
4019 shnum += 1; // .rela.plt4848 shnum += 1; // .rela.plt
4020 }4849 }
...@@ -4788,6 +5617,66 @@ fn initHeaders(...@@ -4788,6 +5617,66 @@ fn initHeaders(
4788 .addralign = addr_align,5617 .addralign = addr_align,
4789 .manual_size = true,5618 .manual_size = true,
4790 });5619 });
5620
5621 elf.shndx.gnu_version = try elf.addSection(elf.ni.rodata, .{
5622 .name = ".gnu.version",
5623 .type = .GNU_VERSYM,
5624 .flags = .{ .ALLOC = true },
5625 .link = elf.shndx.dynsym.toSection().?,
5626 .addralign = .@"2",
5627 .size = 2, // "null" dynsym entry
5628 .manual_size = true,
5629 });
5630 elf.targetStore(&elf.versymSlice()[0], .LOCAL); // "null" dynsym entry
5631
5632 elf.shndx.gnu_version_d = try elf.addSection(elf.ni.rodata, .{
5633 .name = ".gnu.version_d",
5634 .type = .GNU_VERDEF,
5635 .flags = .{ .ALLOC = true },
5636 .link = elf.shndx.dynstr.toSection().?,
5637 // It's completely undocumented outside of source code, but `sh_info` for this
5638 // section must hold the number of `Verdef` entries.
5639 .info = 1,
5640 .addralign = .@"4",
5641 .size = @sizeOf(VerdefEntry), // "file" entry
5642 .manual_size = true,
5643 });
5644 {
5645 const base_version_name: []const u8 = elf.options.soname orelse comp.root_name;
5646 const entry_ptr = &elf.verdefSlice()[0];
5647 entry_ptr.* = .{
5648 .def = .{
5649 .version = 1,
5650 .flags = std.elf.VER_FLG_BASE,
5651 .ndx = .GLOBAL,
5652 .cnt = 1,
5653 .hash = std.elf.hash.calculate(base_version_name),
5654 .aux = @offsetOf(VerdefEntry, "aux"),
5655 .next = @sizeOf(VerdefEntry),
5656 },
5657 .aux = .{
5658 .name = @backingInt(try elf.string(.dynstr, base_version_name)),
5659 .next = 0,
5660 },
5661 };
5662 if (elf.targetEndian() != std.lang.Endian.native) {
5663 std.mem.byteSwapAllFields(VerdefEntry, entry_ptr);
5664 }
5665 }
5666
5667 elf.shndx.gnu_version_r = try elf.addSection(elf.ni.rodata, .{
5668 .name = ".gnu.version_r",
5669 .type = .GNU_VERNEED,
5670 .flags = .{ .ALLOC = true },
5671 .link = elf.shndx.dynstr.toSection().?,
5672 // It's completely undocumented outside of source code, but `sh_info` for this
5673 // section must hold the number of `Verneed` entries.
5674 .info = 0,
5675 .addralign = .@"4",
5676 .size = 0,
5677 .manual_size = true,
5678 });
5679
4791 switch (elf.targetDynsymHashInfo()) {5680 switch (elf.targetDynsymHashInfo()) {
4792 inline else => |info| {5681 inline else => |info| {
4793 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{5682 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
...@@ -4974,11 +5863,10 @@ fn initHeaders(...@@ -4974,11 +5863,10 @@ fn initHeaders(
4974 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`5863 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
4975 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).5864 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).
49765865
4977 try elf.ensureUnusedSymbolCapacity(10, .maybe_global);
4978 // Despite the name, `__dso_handle` is necessary even in static binaries.5866 // Despite the name, `__dso_handle` is necessary even in static binaries.
4979 _ = elf.addGlobalSymbolAssumeCapacity(.{5867 _ = elf.addGlobalSymbol(.{
4980 .node = .wrap(Section.Index.text.get(elf).ni),5868 .node = .wrap(Section.Index.text.get(elf).ni),
4981 .name = try .string(elf, "__dso_handle"),5869 .name = "__dso_handle",
4982 .value = Section.Index.text.vaddr(elf),5870 .value = Section.Index.text.vaddr(elf),
4983 .size = 0,5871 .size = 0,
4984 .type = .NOTYPE,5872 .type = .NOTYPE,
...@@ -4987,10 +5875,13 @@ fn initHeaders(...@@ -4987,10 +5875,13 @@ fn initHeaders(
4987 .shndx = .text,5875 .shndx = .text,
4988 }) catch |err| switch (err) {5876 }) catch |err| switch (err) {
4989 error.MultipleDefinitions => unreachable, // no inputs are processed yet5877 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5878 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5879 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5880 else => |e| return e,
4990 };5881 };
4991 _ = elf.addGlobalSymbolAssumeCapacity(.{5882 _ = elf.addGlobalSymbol(.{
4992 .node = .wrap(elf.shndx.plt.get(elf).ni),5883 .node = .wrap(elf.shndx.plt.get(elf).ni),
4993 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),5884 .name = "_PROCEDURE_LINKAGE_TABLE_",
4994 .value = elf.shndx.plt.vaddr(elf),5885 .value = elf.shndx.plt.vaddr(elf),
4995 .size = 0,5886 .size = 0,
4996 .type = .NOTYPE,5887 .type = .NOTYPE,
...@@ -4999,10 +5890,13 @@ fn initHeaders(...@@ -4999,10 +5890,13 @@ fn initHeaders(
4999 .shndx = elf.shndx.plt,5890 .shndx = elf.shndx.plt,
5000 }) catch |err| switch (err) {5891 }) catch |err| switch (err) {
5001 error.MultipleDefinitions => unreachable, // no inputs are processed yet5892 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5893 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5894 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5895 else => |e| return e,
5002 };5896 };
5003 _ = elf.addGlobalSymbolAssumeCapacity(.{5897 _ = elf.addGlobalSymbol(.{
5004 .node = .wrap(elf.shndx.got.get(elf).ni),5898 .node = .wrap(elf.shndx.got.get(elf).ni),
5005 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),5899 .name = "_GLOBAL_OFFSET_TABLE_",
5006 .value = switch (machine) {5900 .value = switch (machine) {
5007 .AARCH64,5901 .AARCH64,
5008 .LOONGARCH,5902 .LOONGARCH,
...@@ -5023,10 +5917,13 @@ fn initHeaders(...@@ -5023,10 +5917,13 @@ fn initHeaders(
5023 .shndx = elf.shndx.got,5917 .shndx = elf.shndx.got,
5024 }) catch |err| switch (err) {5918 }) catch |err| switch (err) {
5025 error.MultipleDefinitions => unreachable, // no inputs are processed yet5919 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5920 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5921 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5922 else => |e| return e,
5026 };5923 };
5027 _ = elf.addGlobalSymbolAssumeCapacity(.{5924 _ = elf.addGlobalSymbol(.{
5028 .node = .none,5925 .node = .none,
5029 .name = try .string(elf, "__init_array_start"),5926 .name = "__init_array_start",
5030 .value = 0,5927 .value = 0,
5031 .size = 0,5928 .size = 0,
5032 .type = .NOTYPE,5929 .type = .NOTYPE,
...@@ -5035,10 +5932,13 @@ fn initHeaders(...@@ -5035,10 +5932,13 @@ fn initHeaders(
5035 .shndx = .ABS,5932 .shndx = .ABS,
5036 }) catch |err| switch (err) {5933 }) catch |err| switch (err) {
5037 error.MultipleDefinitions => unreachable, // no inputs are processed yet5934 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5935 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5936 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5937 else => |e| return e,
5038 };5938 };
5039 _ = elf.addGlobalSymbolAssumeCapacity(.{5939 _ = elf.addGlobalSymbol(.{
5040 .node = .none,5940 .node = .none,
5041 .name = try .string(elf, "__init_array_end"),5941 .name = "__init_array_end",
5042 .value = 0,5942 .value = 0,
5043 .size = 0,5943 .size = 0,
5044 .type = .NOTYPE,5944 .type = .NOTYPE,
...@@ -5047,10 +5947,13 @@ fn initHeaders(...@@ -5047,10 +5947,13 @@ fn initHeaders(
5047 .shndx = .ABS,5947 .shndx = .ABS,
5048 }) catch |err| switch (err) {5948 }) catch |err| switch (err) {
5049 error.MultipleDefinitions => unreachable, // no inputs are processed yet5949 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5950 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5951 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5952 else => |e| return e,
5050 };5953 };
5051 _ = elf.addGlobalSymbolAssumeCapacity(.{5954 _ = elf.addGlobalSymbol(.{
5052 .node = .none,5955 .node = .none,
5053 .name = try .string(elf, "__fini_array_start"),5956 .name = "__fini_array_start",
5054 .value = 0,5957 .value = 0,
5055 .size = 0,5958 .size = 0,
5056 .type = .NOTYPE,5959 .type = .NOTYPE,
...@@ -5059,10 +5962,13 @@ fn initHeaders(...@@ -5059,10 +5962,13 @@ fn initHeaders(
5059 .shndx = .ABS,5962 .shndx = .ABS,
5060 }) catch |err| switch (err) {5963 }) catch |err| switch (err) {
5061 error.MultipleDefinitions => unreachable, // no inputs are processed yet5964 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5965 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5966 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5967 else => |e| return e,
5062 };5968 };
5063 _ = elf.addGlobalSymbolAssumeCapacity(.{5969 _ = elf.addGlobalSymbol(.{
5064 .node = .none,5970 .node = .none,
5065 .name = try .string(elf, "__fini_array_end"),5971 .name = "__fini_array_end",
5066 .value = 0,5972 .value = 0,
5067 .size = 0,5973 .size = 0,
5068 .type = .NOTYPE,5974 .type = .NOTYPE,
...@@ -5071,10 +5977,13 @@ fn initHeaders(...@@ -5071,10 +5977,13 @@ fn initHeaders(
5071 .shndx = .ABS,5977 .shndx = .ABS,
5072 }) catch |err| switch (err) {5978 }) catch |err| switch (err) {
5073 error.MultipleDefinitions => unreachable, // no inputs are processed yet5979 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5980 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5981 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5982 else => |e| return e,
5074 };5983 };
5075 _ = elf.addGlobalSymbolAssumeCapacity(.{5984 _ = elf.addGlobalSymbol(.{
5076 .node = .none,5985 .node = .none,
5077 .name = try .string(elf, "__preinit_array_start"),5986 .name = "__preinit_array_start",
5078 .value = 0,5987 .value = 0,
5079 .size = 0,5988 .size = 0,
5080 .type = .NOTYPE,5989 .type = .NOTYPE,
...@@ -5083,10 +5992,13 @@ fn initHeaders(...@@ -5083,10 +5992,13 @@ fn initHeaders(
5083 .shndx = .ABS,5992 .shndx = .ABS,
5084 }) catch |err| switch (err) {5993 }) catch |err| switch (err) {
5085 error.MultipleDefinitions => unreachable, // no inputs are processed yet5994 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5995 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5996 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5997 else => |e| return e,
5086 };5998 };
5087 _ = elf.addGlobalSymbolAssumeCapacity(.{5999 _ = elf.addGlobalSymbol(.{
5088 .node = .none,6000 .node = .none,
5089 .name = try .string(elf, "__preinit_array_end"),6001 .name = "__preinit_array_end",
5090 .value = 0,6002 .value = 0,
5091 .size = 0,6003 .size = 0,
5092 .type = .NOTYPE,6004 .type = .NOTYPE,
...@@ -5095,11 +6007,14 @@ fn initHeaders(...@@ -5095,11 +6007,14 @@ fn initHeaders(
5095 .shndx = .ABS,6007 .shndx = .ABS,
5096 }) catch |err| switch (err) {6008 }) catch |err| switch (err) {
5097 error.MultipleDefinitions => unreachable, // no inputs are processed yet6009 error.MultipleDefinitions => unreachable, // no inputs are processed yet
6010 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6011 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
6012 else => |e| return e,
5098 };6013 };
5099 if (have_dynamic) {6014 if (have_dynamic) {
5100 _ = elf.addGlobalSymbolAssumeCapacity(.{6015 _ = elf.addGlobalSymbol(.{
5101 .node = .wrap(elf.shndx.dynamic.get(elf).ni),6016 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
5102 .name = try .string(elf, "_DYNAMIC"),6017 .name = "_DYNAMIC",
5103 .value = elf.shndx.dynamic.vaddr(elf),6018 .value = elf.shndx.dynamic.vaddr(elf),
5104 .size = 0,6019 .size = 0,
5105 .type = .NOTYPE,6020 .type = .NOTYPE,
...@@ -5108,6 +6023,9 @@ fn initHeaders(...@@ -5108,6 +6023,9 @@ fn initHeaders(
5108 .shndx = elf.shndx.dynamic,6023 .shndx = elf.shndx.dynamic,
5109 }) catch |err| switch (err) {6024 }) catch |err| switch (err) {
5110 error.MultipleDefinitions => unreachable, // no inputs are processed yet6025 error.MultipleDefinitions => unreachable, // no inputs are processed yet
6026 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6027 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
6028 else => |e| return e,
5111 };6029 };
5112 }6030 }
5113 } else {6031 } else {
...@@ -5898,6 +6816,39 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {...@@ -5898,6 +6816,39 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
5898 }6816 }
5899}6817}
59006818
6819fn versymSlice(elf: *Elf) []std.elf.Versym {
6820 const raw_slice = elf.shndx.gnu_version.get(elf).ni.slice(&elf.mf);
6821 const size = elf.shndx.gnu_version.size(elf);
6822 return @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
6823}
6824
6825/// Although `.gnu.version_d` (the `SHT_VERDEF` section) can have essentially any layout, we always
6826/// organize it as an array of contiguous entries where each entry is a single `std.elf.Verdef` with
6827/// a single `std.elf.Verdaux`. This type represents a single such entry; the section contents is
6828/// then effectively an array of these.
6829const VerdefEntry = extern struct {
6830 def: std.elf.Verdef,
6831 aux: std.elf.Verdaux,
6832};
6833fn verdefSlice(elf: *Elf) []VerdefEntry {
6834 const raw_slice = elf.shndx.gnu_version_d.get(elf).ni.slice(&elf.mf);
6835 const size = elf.shndx.gnu_version_d.size(elf);
6836 return @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
6837}
6838
6839const VerneedEntry = extern union {
6840 verneed: std.elf.Verneed,
6841 vernaux: std.elf.Vernaux,
6842 comptime {
6843 assert(@sizeOf(std.elf.Verneed) == @sizeOf(std.elf.Vernaux));
6844 }
6845};
6846fn verneedSlice(elf: *Elf) []VerneedEntry {
6847 const raw_slice = elf.shndx.gnu_version_r.get(elf).ni.slice(&elf.mf);
6848 const size = elf.shndx.gnu_version_r.size(elf);
6849 return @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
6850}
6851
5901fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {6852fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
5902 const comp = elf.base.comp;6853 const comp = elf.base.comp;
5903 const any_non_single_threaded = comp.config.any_non_single_threaded;6854 const any_non_single_threaded = comp.config.any_non_single_threaded;
...@@ -5945,8 +6896,8 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -5945,8 +6896,8 @@ fn mapInputSection(elf: *Elf, opts: struct {
5945 .EXEC, .DYN => name: {6896 .EXEC, .DYN => name: {
5946 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";6897 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
5947 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";6898 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";
5948 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
5949 if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro";6899 if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro";
6900 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
5950 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";6901 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
5951 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";6902 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
5952 // TODO: actually generate a bss section!6903 // TODO: actually generate a bss section!
...@@ -6019,7 +6970,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -6019,7 +6970,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
6019 const ip = &zcu.intern_pool;6970 const ip = &zcu.intern_pool;
6020 const nav = ip.getNav(nav_index);6971 const nav = ip.getNav(nav_index);
60216972
6022 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6023 try elf.nodes.ensureUnusedCapacity(gpa, 1);6973 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6024 try elf.navs.ensureUnusedCapacity(gpa, 1);6974 try elf.navs.ensureUnusedCapacity(gpa, 1);
60256975
...@@ -6083,9 +7033,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -6083,9 +7033,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
6083 .alignment = alignment,7033 .alignment = alignment,
6084 }), .{ .nav = nmi });7034 }), .{ .nav = nmi });
6085 nav_gop.value_ptr.* = .{7035 nav_gop.value_ptr.* = .{
6086 .lsi = elf.addLocalSymbolAssumeCapacity(.{7036 .lsi = try elf.addLocalSymbol(.{
6087 .node = .wrap(node),7037 .node = .wrap(node),
6088 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),7038 .name = nav.fqn.toSlice(ip),
6089 .value = 0,7039 .value = 0,
6090 .size = 0,7040 .size = 0,
6091 .type = elf.navType(nav.resolved.?),7041 .type = elf.navType(nav.resolved.?),
...@@ -6106,7 +7056,6 @@ fn uavMapIndex(...@@ -6106,7 +7056,6 @@ fn uavMapIndex(
6106 const gpa = elf.base.comp.gpa;7056 const gpa = elf.base.comp.gpa;
6107 const zcu = elf.base.comp.zcu.?;7057 const zcu = elf.base.comp.zcu.?;
61087058
6109 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6110 try elf.nodes.ensureUnusedCapacity(gpa, 1);7059 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6111 try elf.uavs.ensureUnusedCapacity(gpa, 1);7060 try elf.uavs.ensureUnusedCapacity(gpa, 1);
6112 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);7061 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
...@@ -6129,9 +7078,9 @@ fn uavMapIndex(...@@ -6129,9 +7078,9 @@ fn uavMapIndex(
6129 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;7078 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
6130 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;7079 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
6131 uav_gop.value_ptr.* = .{7080 uav_gop.value_ptr.* = .{
6132 .lsi = elf.addLocalSymbolAssumeCapacity(.{7081 .lsi = try elf.addLocalSymbol(.{
6133 .node = .wrap(node),7082 .node = .wrap(node),
6134 .name = try elf.string(.strtab, name),7083 .name = name,
6135 .value = 0,7084 .value = 0,
6136 .size = 0,7085 .size = 0,
6137 .type = .OBJECT,7086 .type = .OBJECT,
...@@ -6410,10 +7359,9 @@ fn loadObject(...@@ -6410,10 +7359,9 @@ fn loadObject(
6410 }7359 }
64117360
6412 elf.input_pending_index += 1;7361 elf.input_pending_index += 1;
6413 try elf.ensureUnusedSymbolCapacity(1, .all_local);7362 input.extra = .{ .file_symbol = try elf.addLocalSymbol(.{
6414 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
6415 .node = .none,7363 .node = .none,
6416 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),7364 .name = std.fs.path.stem(member orelse path.sub_path),
6417 .value = 0,7365 .value = 0,
6418 .size = 0,7366 .size = 0,
6419 .type = .FILE,7367 .type = .FILE,
...@@ -6641,7 +7589,6 @@ fn loadObject(...@@ -6641,7 +7589,6 @@ fn loadObject(
6641 ), 1) catch continue;7589 ), 1) catch continue;
6642 symmap.clearRetainingCapacity();7590 symmap.clearRetainingCapacity();
6643 try symmap.resize(gpa, symnum);7591 try symmap.resize(gpa, symnum);
6644 try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
6645 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);7592 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
6646 for (symmap.items) |*si| {7593 for (symmap.items) |*si| {
6647 si.* = .null;7594 si.* = .null;
...@@ -6665,9 +7612,9 @@ fn loadObject(...@@ -6665,9 +7612,9 @@ fn loadObject(
6665 ),7612 ),
6666 .LOCAL => continue,7613 .LOCAL => continue,
6667 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {7614 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6668 si.* = elf.addGlobalSymbolAssumeCapacity(.{7615 si.* = .global(elf.addGlobalSymbol(.{
6669 .node = .none,7616 .node = .none,
6670 .name = try .string(elf, name),7617 .name = name,
6671 .value = input_sym.value,7618 .value = input_sym.value,
6672 .size = input_sym.size,7619 .size = input_sym.size,
6673 .type = sym_type,7620 .type = sym_type,
...@@ -6680,7 +7627,14 @@ fn loadObject(...@@ -6680,7 +7627,14 @@ fn loadObject(
6680 .shndx = .UNDEF,7627 .shndx = .UNDEF,
6681 }) catch |err| switch (err) {7628 }) catch |err| switch (err) {
6682 error.MultipleDefinitions => unreachable, // shndx is .UNDEF7629 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
6683 };7630 error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF
7631 error.UndefinedDefaultVersion => return diags.failParse(
7632 path,
7633 "symbol '{s}' specifies default version without defining it",
7634 .{name},
7635 ),
7636 else => |e| return e,
7637 });
6684 continue;7638 continue;
6685 },7639 },
6686 };7640 };
...@@ -6694,9 +7648,9 @@ fn loadObject(...@@ -6694,9 +7648,9 @@ fn loadObject(
6694 .{ name, bind },7648 .{ name, bind },
6695 ),7649 ),
6696 .LOCAL => {7650 .LOCAL => {
6697 const lsi = elf.addLocalSymbolAssumeCapacity(.{7651 const lsi = try elf.addLocalSymbol(.{
6698 .node = .wrap(input_section_node),7652 .node = .wrap(input_section_node),
6699 .name = try elf.string(.strtab, name),7653 .name = name,
6700 .value = input_sym.value,7654 .value = input_sym.value,
6701 .size = input_sym.size,7655 .size = input_sym.size,
6702 .type = sym_type,7656 .type = sym_type,
...@@ -6705,9 +7659,9 @@ fn loadObject(...@@ -6705,9 +7659,9 @@ fn loadObject(
6705 si.* = .local(lsi);7659 si.* = .local(lsi);
6706 },7660 },
6707 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {7661 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6708 si.* = elf.addGlobalSymbolAssumeCapacity(.{7662 si.* = .global(elf.addGlobalSymbol(.{
6709 .node = .wrap(input_section_node),7663 .node = .wrap(input_section_node),
6710 .name = try .string(elf, name),7664 .name = name,
6711 .value = input_sym.value,7665 .value = input_sym.value,
6712 .size = input_sym.size,7666 .size = input_sym.size,
6713 .type = sym_type,7667 .type = sym_type,
...@@ -6724,7 +7678,14 @@ fn loadObject(...@@ -6724,7 +7678,14 @@ fn loadObject(
6724 "multiple definitions of '{s}'",7678 "multiple definitions of '{s}'",
6725 .{name},7679 .{name},
6726 ),7680 ),
6727 };7681 error.MultipleDefaultVersions => return diags.failParse(
7682 path,
7683 "multiple default versions of '{s}'",
7684 .{parseVersionedSymbolName(name).name},
7685 ),
7686 error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined)
7687 else => |e| return e,
7688 });
6728 },7689 },
6729 }7690 }
6730 }7691 }
...@@ -6877,73 +7838,199 @@ fn loadDso(...@@ -6877,73 +7838,199 @@ fn loadDso(
6877 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});7838 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
6878 if (ehdr.machine != elf.ehdrMachine().toElf())7839 if (ehdr.machine != elf.ehdrMachine().toElf())
6879 return diags.failParse(path, "bad machine", .{});7840 return diags.failParse(path, "bad machine", .{});
6880 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
6881 // We're going to need to know the alignment of every section later.7841 // We're going to need to know the alignment of every section later.
6882 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);7842 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
6883 defer gpa.free(section_aligns);7843 defer gpa.free(section_aligns);
6884 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {7844 const shdr: struct {
6885 var dynamic_sh: ?ElfN.Shdr = null;7845 dynamic: ElfN.Shdr,
6886 var dynsym_sh: ?ElfN.Shdr = null;7846 dynsym: ElfN.Shdr,
6887 for (section_aligns) |*section_align| {7847 dynstr: ElfN.Shdr,
7848 verdef: ?ElfN.Shdr,
7849 versym: ?ElfN.Shdr,
7850 } = shdr: {
7851 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
7852 const InputSh = struct {
7853 shndx: u32,
7854 sh: ElfN.Shdr,
7855 };
7856 var opt_dynamic: ?InputSh = null;
7857 var opt_dynsym: ?InputSh = null;
7858 var opt_verdef: ?InputSh = null;
7859 var opt_versym: ?InputSh = null;
7860 for (section_aligns, 0..) |*section_align, shndx| {
6888 const sh = try r.peekStruct(ElfN.Shdr, target_endian);7861 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
6889 try r.discardAll(ehdr.shentsize);7862 try r.discardAll(ehdr.shentsize);
6890 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(7863 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
6891 usize,7864 usize,
6892 @intCast(@max(sh.addralign, 1)),7865 @intCast(@max(sh.addralign, 1)),
6893 ));7866 ));
6894 switch (sh.type) {7867 const ptr: *?InputSh = switch (sh.type) {
6895 else => {},7868 else => continue,
6896 .DYNAMIC => dynamic_sh = sh,7869 .DYNAMIC => &opt_dynamic,
6897 .DYNSYM => dynsym_sh = sh,7870 .DYNSYM => &opt_dynsym,
7871 .GNU_VERDEF => &opt_verdef,
7872 .GNU_VERSYM => &opt_versym,
7873 };
7874 if (ptr.* != null) {
7875 return diags.failParse(path, "multiple SHT_{t} sections", .{sh.type});
6898 }7876 }
7877 ptr.* = .{ .shndx = @intCast(shndx), .sh = sh };
6899 }7878 }
6900 break :sh .{7879
6901 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),7880 const dynamic = opt_dynamic orelse {
6902 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),7881 return diags.failParse(path, "missing SHT_DYNAMIC section", .{});
6903 };7882 };
6904 };7883 const dynsym = opt_dynsym orelse {
6905 const dynstr_sh: ElfN.Shdr = sh: {7884 return diags.failParse(path, "missing SHT_DYNSYM section", .{});
6906 if (dynsym_sh.link >= ehdr.shnum) {7885 };
6907 return diags.failParse(path, "bad dynamic string table section index", .{});7886
7887 const dynstr_shndx = dynamic.sh.link;
7888 if (dynstr_shndx >= ehdr.shnum) {
7889 return diags.failParse(path, "SHT_DYNAMIC section does not link to a valid section", .{});
7890 }
7891 try fr.seekTo(ehdr.shoff + dynstr_shndx * ehdr.shentsize);
7892 const dynstr_sh = try r.peekStruct(ElfN.Shdr, target_endian);
7893 if (dynstr_sh.type != .STRTAB) {
7894 return diags.failParse(path, "SHT_DYNAMIC section does not link to a SHT_STRTAB section", .{});
7895 }
7896
7897 // Validate all the other shdr `link` fields. After this we won't need the shndx
7898 // values for anything else.
7899 if (dynsym.sh.link != dynstr_shndx) {
7900 return diags.failParse(path, "SHT_DYNSYM section does not link to the dynamic string table section", .{});
6908 }7901 }
6909 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);7902 if (opt_verdef) |verdef| if (verdef.sh.link != dynstr_shndx) {
6910 break :sh try r.peekStruct(ElfN.Shdr, target_endian);7903 return diags.failParse(path, "SHT_GNU_VERDEF section does not link to the dynamic string table section", .{});
7904 };
7905 if (opt_versym) |versym| if (versym.sh.link != dynsym.shndx) {
7906 return diags.failParse(path, "SHT_GNU_VERSYM section does not link to the dynamic symbol table section", .{});
7907 };
7908
7909 break :shdr .{
7910 .dynamic = dynamic.sh,
7911 .dynsym = dynsym.sh,
7912 .dynstr = dynstr_sh,
7913 .verdef = if (opt_verdef) |verdef| verdef.sh else null,
7914 .versym = if (opt_versym) |versym| versym.sh else null,
7915 };
6911 };7916 };
69127917
6913 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {7918 if (shdr.dynamic.entsize != @sizeOf(ElfN.Addr) * 2) {
6914 return diags.failParse(path, "bad dynamic section entsize", .{});7919 return diags.failParse(path, "bad SHT_DYNAMIC section entsize", .{});
6915 }7920 }
6916 const dynnum = std.math.divExact(7921 const dynnum = std.math.divExact(
6917 u32,7922 u32,
6918 @intCast(dynamic_sh.size),7923 @intCast(shdr.dynamic.size),
6919 @sizeOf(ElfN.Addr) * 2,7924 @sizeOf(ElfN.Addr) * 2,
6920 ) catch return diags.failParse(7925 ) catch return diags.failParse(
6921 path,7926 path,
6922 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",7927 "SHT_DYNAMIC section size (0x{x}) is not a multiple of entsize (0x{x})",
6923 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },7928 .{ shdr.dynamic.size, @sizeOf(ElfN.Addr) * 2 },
6924 );7929 );
69257930
6926 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {7931 if (shdr.dynsym.entsize < @sizeOf(ElfN.Sym)) {
6927 return diags.failParse(path, "bad dynsym entsize", .{});7932 return diags.failParse(path, "dynamic symbol table has invalid entsize", .{});
6928 }7933 }
6929 const symnum = std.math.divExact(7934 const symnum = std.math.divExact(
6930 u32,7935 u32,
6931 @intCast(dynsym_sh.size),7936 @intCast(shdr.dynsym.size),
6932 @intCast(dynsym_sh.entsize),7937 @intCast(shdr.dynsym.entsize),
6933 ) catch return diags.failParse(7938 ) catch return diags.failParse(
6934 path,7939 path,
6935 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",7940 "dynamic symbol table size (0x{x}) is not a multiple of entsize (0x{x})",
6936 .{ dynsym_sh.size, dynsym_sh.entsize },7941 .{ shdr.dynsym.size, shdr.dynsym.entsize },
6937 );7942 );
69387943
6939 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));7944 const dynstr = try gpa.alloc(u8, @intCast(shdr.dynstr.size));
6940 defer gpa.free(dynstr);7945 defer gpa.free(dynstr);
6941 try fr.seekTo(dynstr_sh.offset);7946 try fr.seekTo(shdr.dynstr.offset);
6942 try r.readSliceAll(dynstr);7947 try r.readSliceAll(dynstr);
69437948
7949 const versym: []std.elf.Versym = versym: {
7950 const versym_sh = shdr.versym orelse break :versym &.{};
7951 if (versym_sh.size != symnum * 2) return diags.failParse(
7952 path,
7953 "SHT_GNU_VERSYM section has invalid size (expected 0x{x}, got 0x{x})",
7954 .{ symnum * 2, versym_sh.size },
7955 );
7956 const versym = try gpa.alloc(std.elf.Versym, symnum);
7957 errdefer gpa.free(versym);
7958 try fr.seekTo(versym_sh.offset);
7959 try r.readSliceEndian(std.elf.Versym, versym, target_endian);
7960 break :versym versym;
7961 };
7962 defer gpa.free(versym);
7963
7964 const verdef: []DsoGlobals.String.Optional = verdef: {
7965 const verdef_sh = shdr.verdef orelse break :verdef &.{};
7966
7967 // This parsing is a bit of a mess because it appears that at this moment in history
7968 // nobody at GNU was yet aware of the concept of an "array".
7969
7970 const num_versions = verdef_sh.info;
7971 if (num_versions > std.math.maxInt(u16)) {
7972 return diags.failParse(path, "entry count of SHT_GNU_VERDEF section is too large", .{});
7973 }
7974
7975 // First pass: find the largest version index, because they're not actually indices,
7976 // but rather arbitrary IDs. But it's okay to allocate memory proportional to the
7977 // largest ID, because (a) glibc does this which suggests that the values should be
7978 // dense in practice, and more importantly (b) the IDs are only 16 bits long anyway
7979 // so this will never allocate more than 65k entries.
7980 var max_ver_ndx: u16 = 0;
7981 var verdef_offset: u64 = 0;
7982 for (0..num_versions) |_| {
7983 if (verdef_offset + @sizeOf(std.elf.Verdef) > verdef_sh.size) {
7984 return diags.failParse(path, "verdef entry exceeds section bounds", .{});
7985 }
7986 try fr.seekTo(verdef_sh.offset + verdef_offset);
7987 const def = try r.peekStruct(std.elf.Verdef, target_endian);
7988 if (def.cnt != 0 and def.flags & std.elf.VER_FLG_BASE == 0) {
7989 max_ver_ndx = @max(max_ver_ndx, @backingInt(def.ndx));
7990 }
7991 verdef_offset += def.next;
7992 }
7993
7994 const verdef = try gpa.alloc(DsoGlobals.String.Optional, @intCast(max_ver_ndx + 1));
7995 errdefer gpa.free(verdef);
7996
7997 @memset(verdef, .none);
7998
7999 // Second pass: for each version definition, look at its first verdaux entry to get
8000 // the actual version name.
8001 verdef_offset = 0;
8002 for (0..num_versions) |_| {
8003 try fr.seekTo(verdef_sh.offset + verdef_offset);
8004 const def = try r.peekStruct(std.elf.Verdef, target_endian);
8005 if (def.cnt != 0 and def.flags & std.elf.VER_FLG_BASE == 0) {
8006 const ver_ndx = @backingInt(def.ndx);
8007 if (verdef[ver_ndx] != .none) {
8008 return diags.failParse(path, "multiple definitions for symbol version index '{d}'", .{ver_ndx});
8009 }
8010 if (verdef_offset + def.aux + @sizeOf(std.elf.Verdaux) > verdef_sh.size) {
8011 return diags.failParse(path, "verdaux entry exceeds section bounds", .{});
8012 }
8013 try fr.seekTo(verdef_sh.offset + verdef_offset + def.aux);
8014 const aux = try r.peekStruct(std.elf.Verdaux, target_endian);
8015 if (aux.name >= shdr.dynstr.size) {
8016 return diags.failParse(path, "bad verdaux entry name string", .{});
8017 }
8018 const version_name = std.mem.sliceTo(dynstr[aux.name..], 0);
8019 verdef[ver_ndx] = @fromBackingInt(@intCast(elf.dso_globals.string_bytes.items.len));
8020 try elf.dso_globals.string_bytes.ensureUnusedCapacity(gpa, version_name.len + 1);
8021 elf.dso_globals.string_bytes.appendSliceAssumeCapacity(version_name);
8022 elf.dso_globals.string_bytes.appendAssumeCapacity(0);
8023 }
8024 verdef_offset += def.next;
8025 }
8026
8027 break :verdef verdef;
8028 };
8029 defer gpa.free(verdef);
8030
6944 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.8031 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
6945 try fr.seekTo(dynamic_sh.offset);8032 try fr.seekTo(shdr.dynamic.offset);
6946 const soname: []const u8 = for (0..dynnum) |_| {8033 const soname_slice: []const u8 = for (0..dynnum) |_| {
6947 const tag = try r.takeInt(ElfN.Addr, target_endian);8034 const tag = try r.takeInt(ElfN.Addr, target_endian);
6948 const val = try r.takeInt(ElfN.Addr, target_endian);8035 const val = try r.takeInt(ElfN.Addr, target_endian);
6949 if (tag == std.elf.DT_SONAME) {8036 if (tag == std.elf.DT_SONAME) {
...@@ -6957,16 +8044,16 @@ fn loadDso(...@@ -6957,16 +8044,16 @@ fn loadDso(
6957 .basename => std.fs.path.basename(path.sub_path),8044 .basename => std.fs.path.basename(path.sub_path),
6958 .full_path => try path.toString(comp.arena),8045 .full_path => try path.toString(comp.arena),
6959 };8046 };
6960 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});8047 const soname = try elf.string(.dynstr, soname_slice);
8048 try elf.needed.put(gpa, soname, {});
69618049
6962 // Scan the symbol table and populate `elf.dso_globals`.8050 // Scan the symbol table and populate `elf.dso_globals`.
6963 const first_global = @min(dynsym_sh.info, symnum);8051 const first_global = @min(shdr.dynsym.info, symnum);
6964 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);8052 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
6965 try elf.ensureUnusedPltCapacity(symnum - first_global);8053 try fr.seekTo(shdr.dynsym.offset + first_global * shdr.dynsym.entsize);
6966 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);8054 for (first_global..symnum) |in_dynsym_index| {
6967 for (first_global..symnum) |_| {
6968 const sym = try r.peekStruct(ElfN.Sym, target_endian);8055 const sym = try r.peekStruct(ElfN.Sym, target_endian);
6969 try r.discardAll(@intCast(dynsym_sh.entsize));8056 try r.discardAll(@intCast(shdr.dynsym.entsize));
69708057
6971 switch (sym.info.bind) {8058 switch (sym.info.bind) {
6972 else => continue,8059 else => continue,
...@@ -6986,96 +8073,76 @@ fn loadDso(...@@ -6986,96 +8073,76 @@ fn loadDso(
6986 return diags.failParse(path, "bad symbol name string", .{});8073 return diags.failParse(path, "bad symbol name string", .{});
6987 }8074 }
69888075
8076 const maybe_version: DsoGlobals.String.Optional, const is_default_version: bool = if (versym.len > 0) switch (versym[in_dynsym_index]) {
8077 .LOCAL, .GLOBAL => .{ .none, false },
8078 else => |v| version: {
8079 if (v.VERSION >= verdef.len or verdef[v.VERSION] == .none) {
8080 return diags.failParse(path, "bad symbol version index '{d}'", .{v.VERSION});
8081 }
8082 break :version .{ verdef[v.VERSION], !v.HIDDEN };
8083 },
8084 } else .{ .none, false };
8085
6989 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems8086 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems
6990 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.8087 // insane---refer to the doc comment on `DsoGlobals.Symbol.alignment`.
6991 const sym_align: Alignment = switch (sym.value) {8088 const sym_align: Alignment = switch (sym.value) {
6992 0 => section_aligns[sym.shndx],8089 0 => section_aligns[sym.shndx],
6993 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),8090 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
6994 };8091 };
69958092
6996 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));8093 const name = std.mem.sliceTo(dynstr[sym.name..], 0);
6997 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);8094
8095 const gop = elf.dso_globals.symbols.getOrPutAssumeCapacityAdapted(@as(DsoGlobals.Symbol.Key, .{
8096 .name = name,
8097 .version = if (maybe_version.unwrap()) |version| slice: {
8098 break :slice version.slice(&elf.dso_globals);
8099 } else null,
8100 }), @as(DsoGlobals.Symbol.Adapter, .{ .dso_globals = &elf.dso_globals }));
69988101
6999 if (gop.found_existing and gop.value_ptr.type != .NOTYPE) {8102 if (gop.found_existing and gop.key_ptr.type != .NOTYPE) {
7000 if (sym.size > gop.value_ptr.size or8103 if (sym.size > gop.key_ptr.size or
7001 sym_align.compare(.gt, gop.value_ptr.alignment))8104 sym_align.compare(.gt, gop.key_ptr.alignment))
7002 {8105 {
7003 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);8106 gop.key_ptr.size = @max(gop.key_ptr.size, sym.size);
7004 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);8107 gop.key_ptr.alignment = gop.key_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!
7008 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
7009 try copied_global.node.resizeLeaf(
7010 gpa,
7011 &elf.mf,
7012 gop.value_ptr.alignment.forward(gop.value_ptr.size),
7013 );
7014 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)) {
7017 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
7018 }
7019 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
7020 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
7021 }
7022 }
7023 }8108 }
7024 continue;8109 } else {
8110 gop.key_ptr.* = .{
8111 .name = name: {
8112 if (gop.found_existing) break :name gop.key_ptr.name;
8113 const name_str_off = elf.dso_globals.string_bytes.items.len;
8114 try elf.dso_globals.string_bytes.ensureUnusedCapacity(gpa, name.len + 1);
8115 elf.dso_globals.string_bytes.appendSliceAssumeCapacity(name);
8116 elf.dso_globals.string_bytes.appendAssumeCapacity(0);
8117 break :name @fromBackingInt(@intCast(name_str_off));
8118 },
8119 .version = maybe_version,
8120 .soname = soname,
8121 .type = sym.info.type,
8122 .size = sym.size,
8123 .alignment = sym_align,
8124 };
7025 }8125 }
70268126
7027 gop.value_ptr.* = .{8127 if (is_default_version) {
7028 .type = sym.info.type,8128 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
7029 .size = sym.size,8129 .dso_globals = &elf.dso_globals,
7030 .alignment = sym_align,8130 };
7031 };8131 const default_gop = try elf.dso_globals.default_sym_vers.getOrPutAdapted(gpa, name, adapter);
70328132 if (!default_gop.found_existing) {
7033 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate8133 default_gop.key_ptr.* = @intCast(gop.index);
7034 // its type now.8134 }
7035 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
7036 elf.globals.weak_undef.getPtr(name) orelse
7037 continue;
7038
7039 if (global_ptr.dynsym_index == 0) continue;
7040
7041 if (elf.want_copied_globals.swapRemove(name)) {
7042 // We just found a DSO definition of a symbol for which we wanted a copy
7043 // relocation, so add one if we can!
7044 _ = try elf.maybeAddCopyRelocation(name);
7045 }8135 }
70468136
7047 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));8137 // Update any global(s) which may care about this input DSO global.
7048 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`8138 if (maybe_version.unwrap()) |version| {
70498139 if (elf.globalByName(.{ .name = name, .version = version.slice(&elf.dso_globals) })) |gsi| {
7050 switch (elf.targetLoad(&sym_ptr.other).visibility) {8140 try elf.updateGlobalDynamic(gsi, false);
7051 .HIDDEN, .INTERNAL, .PROTECTED => continue,8141 }
7052 .DEFAULT => {},
7053 }8142 }
70548143 if (maybe_version == .none or is_default_version) {
7055 const cur_info = elf.targetLoad(&sym_ptr.info);8144 if (elf.globalByName(.{ .name = name, .version = null })) |gsi| {
7056 if (cur_info.type == .NOTYPE) {8145 try elf.updateGlobalDynamic(gsi, false);
7057 const new_type: std.elf.STT = switch (sym.info.type) {
7058 .GNU_IFUNC => .FUNC,
7059 else => |t| t,
7060 };
7061
7062 elf.targetStore(&sym_ptr.info, .{
7063 .bind = cur_info.bind,
7064 .type = new_type,
7065 });
7066
7067 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
7068 elf.targetStore(&dynsym_ptr.info, .{
7069 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
7070 .type = new_type,
7071 });
7072
7073 if (new_type == .FUNC) {
7074 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
7075 elf.addPltEntry(name, global_ptr.dynsym_index);
7076 // ...and therefore, we need to re-apply that symbol's relocations, as
7077 // some might be targeting its PLT entry.
7078 Symbol.Id.global(name).applyTargetRelocs(elf);
7079 }8146 }
7080 }8147 }
7081 }8148 }
...@@ -7089,9 +8156,11 @@ fn loadDso(...@@ -7089,9 +8156,11 @@ fn loadDso(
7089/// `error.AlreadyReported`, but if the magic number is missing or incorrect, returns8156/// `error.AlreadyReported`, but if the magic number is missing or incorrect, returns
7090/// `error.BadMagic` instead.8157/// `error.BadMagic` instead.
7091///8158///
8159/// If necessary, modifies our own ident to use `ELF_OSABI_GNU` instead of `ELF_OSABI_NONE`.
8160///
7092/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.8161/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.
7093fn checkInputIdent(8162fn checkInputIdent(
7094 elf: *const Elf,8163 elf: *Elf,
7095 path: std.Build.Cache.Path,8164 path: std.Build.Cache.Path,
7096 r: *Io.Reader,8165 r: *Io.Reader,
7097) error{ BadMagic, EndOfStream, AlreadyReported, ReadFailed }!void {8166) error{ BadMagic, EndOfStream, AlreadyReported, ReadFailed }!void {
...@@ -7106,8 +8175,9 @@ fn checkInputIdent(...@@ -7106,8 +8175,9 @@ fn checkInputIdent(
7106 }8175 }
71078176
7108 const ident = try r.peekStructPointer(std.elf.Ident);8177 const ident = try r.peekStructPointer(std.elf.Ident);
7109 const target: *const std.elf.Ident =8178 const target: *std.elf.Ident = @ptrCast(
7110 @ptrCast(elf.ni.elf.sliceConst(&elf.mf)[0..@sizeOf(std.elf.Ident)]);8179 elf.ni.elf.slice(&elf.mf)[0..@sizeOf(std.elf.Ident)],
8180 );
71118181
7112 if (ident.class != target.class) return diags.failParse(8182 if (ident.class != target.class) return diags.failParse(
7113 path,8183 path,
...@@ -7124,17 +8194,24 @@ fn checkInputIdent(...@@ -7124,17 +8194,24 @@ fn checkInputIdent(
7124 "bad ELF version ({d})",8194 "bad ELF version ({d})",
7125 .{ident.version},8195 .{ident.version},
7126 );8196 );
71278197 // OSABI is a bit more complex.
7128 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.8198 const expect_abiversion: u8 = switch (ident.osabi) {
7129 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.8199 .NONE => 0,
7130 const expect_abiversion: u8 = abiver: {8200 .GNU => abiversion: {
7131 if (ident.osabi == .NONE) break :abiver 0;8201 // If we're currently emitting `ELF_OSABI_NONE` then prefer `ELF_OSABI_GNU` to signify
7132 if (ident.osabi == target.osabi) break :abiver target.abiversion;8202 // the GNU-specific features, but if we're already emitting a target-specific osabi then
7133 return diags.failParse(8203 // just leave it alone.
8204 if (target.osabi == .NONE) target.osabi = .GNU;
8205 // Either way, allow the `ELF_OSABI_GNU` input through.
8206 break :abiversion 0;
8207 },
8208 else => if (ident.osabi == target.osabi) abiversion: {
8209 break :abiversion target.abiversion;
8210 } else return diags.failParse(
7134 path,8211 path,
7135 "bad ELF OS/ABI ({?s})",8212 "bad ELF OS/ABI ({?s})",
7136 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},8213 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
7137 );8214 ),
7138 };8215 };
7139 if (ident.abiversion != expect_abiversion) return diags.failParse(8216 if (ident.abiversion != expect_abiversion) return diags.failParse(
7140 path,8217 path,
...@@ -7166,25 +8243,32 @@ fn createInitFiniArraySection(...@@ -7166,25 +8243,32 @@ fn createInitFiniArraySection(
7166 .manual_size = true,8243 .manual_size = true,
7167 });8244 });
7168 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});8245 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
7169 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);8246 // These symbols definitely already exist with strong definitions, because we added them
7170 // These symbols definitely already have strong definitions, because we added them alongside the8247 // alongside the other linker-defined symbols, all the way back in `initHeaders`.
7171 // other linker-defined symbols, all the way back in `initHeaders`.8248 const start_gsi = elf.globalByName(.{
7172 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");8249 .name = "__" ++ name ++ "_start",
7173 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");8250 .version = null,
7174 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{8251 }).?;
8252 const end_gsi = elf.globalByName(.{
8253 .name = "__" ++ name ++ "_end",
8254 .version = null,
8255 }).?;
8256 try elf.setGlobalSymbolValue(start_gsi, .{
7175 .node = .wrap(shndx.get(elf).ni),8257 .node = .wrap(shndx.get(elf).ni),
7176 .value = shndx.vaddr(elf),8258 .value = shndx.vaddr(elf),
7177 .size = 0,8259 .size = 0,
7178 .type = .NOTYPE,8260 .type = .NOTYPE,
7179 .shndx = shndx.*,8261 .shndx = shndx.*,
7180 });8262 });
7181 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{8263 try elf.updateGlobalDynamic(start_gsi, true);
8264 try elf.setGlobalSymbolValue(end_gsi, .{
7182 .node = .wrap(shndx.get(elf).ni),8265 .node = .wrap(shndx.get(elf).ni),
7183 .value = shndx.vaddr(elf),8266 .value = shndx.vaddr(elf),
7184 .size = 0,8267 .size = 0,
7185 .type = .NOTYPE,8268 .type = .NOTYPE,
7186 .shndx = shndx.*,8269 .shndx = shndx.*,
7187 });8270 });
8271 try elf.updateGlobalDynamic(end_gsi, true);
7188}8272}
7189fn updateInitFiniArraySectionSize(8273fn updateInitFiniArraySectionSize(
7190 elf: *Elf,8274 elf: *Elf,
...@@ -7194,8 +8278,11 @@ fn updateInitFiniArraySectionSize(...@@ -7194,8 +8278,11 @@ fn updateInitFiniArraySectionSize(
7194 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {8278 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
7195 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),8279 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
7196 };8280 };
7197 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");8281 const end_sym_gsi = elf.globalByName(.{
7198 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);8282 .name = "__" ++ name ++ "_end",
8283 .version = null,
8284 }).?;
8285 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);
7199}8286}
72008287
7201pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {8288pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
...@@ -7215,13 +8302,12 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -7215,13 +8302,12 @@ fn prelinkInner(elf: *Elf) Error!void {
7215 if (comp.config.use_llvm) break :self_hosted_codegen;8302 if (comp.config.use_llvm) break :self_hosted_codegen;
72168303
7217 // We're using self-hosted codegen---add an input representing the Zig "object".8304 // We're using self-hosted codegen---add an input representing the Zig "object".
7218 try elf.ensureUnusedSymbolCapacity(1, .all_local);
7219 try elf.inputs.ensureUnusedCapacity(gpa, 1);8305 try elf.inputs.ensureUnusedCapacity(gpa, 1);
7220 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});8306 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
7221 defer gpa.free(zcu_name);8307 defer gpa.free(zcu_name);
7222 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{8308 const zcu_file_symbol = try elf.addLocalSymbol(.{
7223 .node = .none,8309 .node = .none,
7224 .name = try elf.string(.strtab, zcu_name),8310 .name = zcu_name,
7225 .value = 0,8311 .value = 0,
7226 .size = 0,8312 .size = 0,
7227 .type = .FILE,8313 .type = .FILE,
...@@ -7512,8 +8598,10 @@ fn prepareDynamic(elf: *Elf) Error!void {...@@ -7512,8 +8598,10 @@ fn prepareDynamic(elf: *Elf) Error!void {
7512 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +8598 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
7513 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +8599 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
7514 @as(usize, @intFromBool(use_plt)) * 4 +8600 @as(usize, @intFromBool(use_plt)) * 4 +
8601 @as(usize, @intFromBool(elf.verneed.count() > 0)) * 2 +
8602 @as(usize, @intFromBool(elf.verdef.count() > 0)) * 2 +
7515 @intFromBool(comp.config.output_mode == .Exe) +8603 @intFromBool(comp.config.output_mode == .Exe) +
7516 @intFromBool(elf.textrel_count > 0) + 9;8604 @intFromBool(elf.textrel_count > 0) + 10;
75178605
7518 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();8606 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
75198607
...@@ -7612,8 +8700,25 @@ fn flushDynamic(elf: *Elf) void {...@@ -7612,8 +8700,25 @@ fn flushDynamic(elf: *Elf) void {
7612 };8700 };
7613 dynamic_index += 4;8701 dynamic_index += 4;
7614 }8702 }
8703 if (elf.verneed.count() > 0) {
8704 dynamic_entries[dynamic_index..][0..2].* = .{
8705 .{ std.elf.DT_VERNEED, @intCast(elf.shndx.gnu_version_r.vaddr(elf)) },
8706 .{ std.elf.DT_VERNEEDNUM, verneed_num: {
8707 const shdr = @field(elf.shdrPtr(elf.shndx.gnu_version_r), @tagName(class));
8708 break :verneed_num elf.targetLoad(&shdr.info);
8709 } },
8710 };
8711 dynamic_index += 2;
8712 }
8713 if (elf.verdef.count() > 0) {
8714 dynamic_entries[dynamic_index..][0..2].* = .{
8715 .{ std.elf.DT_VERDEF, @intCast(elf.shndx.gnu_version_d.vaddr(elf)) },
8716 .{ std.elf.DT_VERDEFNUM, @intCast(elf.verdef.count() + 1) },
8717 };
8718 dynamic_index += 2;
8719 }
76158720
7616 dynamic_entries[dynamic_index..][0..9].* = .{8721 dynamic_entries[dynamic_index..][0..10].* = .{
7617 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },8722 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
7618 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },8723 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
7619 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },8724 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
...@@ -7621,10 +8726,11 @@ fn flushDynamic(elf: *Elf) void {...@@ -7621,10 +8726,11 @@ fn flushDynamic(elf: *Elf) void {
7621 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },8726 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
7622 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },8727 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
7623 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },8728 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
8729 .{ std.elf.DT_VERSYM, @intCast(elf.shndx.gnu_version.vaddr(elf)) },
7624 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },8730 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
7625 .{ std.elf.DT_NULL, 0 },8731 .{ std.elf.DT_NULL, 0 },
7626 };8732 };
7627 dynamic_index += 9;8733 dynamic_index += 10;
76288734
7629 assert(dynamic_index == dynamic_entries.len);8735 assert(dynamic_index == dynamic_entries.len);
7630 if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry|8736 if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry|
...@@ -7661,7 +8767,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -7661,7 +8767,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
7661 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,8767 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
7662 else => false,8768 else => false,
7663 };8769 };
7664 if (want_symbol) try elf.ensureUnusedSymbolCapacity(1, .all_local);
76658770
7666 const shstrtab_entry = try elf.string(.shstrtab, opts.name);8771 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
7667 const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {8772 const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
...@@ -7708,9 +8813,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -7708,9 +8813,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
7708 });8813 });
7709 const addr = elf.computeNodeVAddr(ni);8814 const addr = elf.computeNodeVAddr(ni);
7710 elf.shdrs.appendAssumeCapacity(.{8815 elf.shdrs.appendAssumeCapacity(.{
7711 .lsi = if (want_symbol) elf.addLocalSymbolAssumeCapacity(.{8816 .lsi = if (want_symbol) try elf.addLocalSymbol(.{
7712 .node = ni.toOptional(),8817 .node = ni.toOptional(),
7713 .name = .empty,8818 .name = "",
7714 .value = addr,8819 .value = addr,
7715 .size = 0,8820 .size = 0,
7716 .type = .SECTION,8821 .type = .SECTION,
...@@ -8187,10 +9292,27 @@ fn addSymbolRelocAssumeCapacity(...@@ -8187,10 +9292,27 @@ fn addSymbolRelocAssumeCapacity(
8187 => false,9292 => false,
8188 },9293 },
8189 };9294 };
9295 if (try_copy_reloc) switch (target.unwrap()) {
9296 .local => {},
9297 .global => |gsi| if (!gsi.ptr(elf).status.want_static_value) {
9298 gsi.ptr(elf).status.want_static_value = true;
9299 try elf.updateGlobalDynamic(gsi, false);
9300 },
9301 };
81909302
8191 classify: switch (elf.classifySymbolValue(target)) {9303 classify: switch (elf.classifySymbolValue(target)) {
8192 .static => break :r .none,9304 .static => {
9305 switch (target.unwrap()) {
9306 .local => {},
9307 .global => |gsi| gsi.ptr(elf).status.any_static_target_relocs = true,
9308 }
9309 break :r .none;
9310 },
8193 .static_relative => {9311 .static_relative => {
9312 switch (target.unwrap()) {
9313 .local => {},
9314 .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true,
9315 }
8194 switch (@"type".target) {9316 switch (@"type".target) {
8195 // Only relocations which resolve to absolute addresses require runtime9317 // Only relocations which resolve to absolute addresses require runtime
8196 // `R_*_RELATIVE` relocations.9318 // `R_*_RELATIVE` relocations.
...@@ -8217,13 +9339,8 @@ fn addSymbolRelocAssumeCapacity(...@@ -8217,13 +9339,8 @@ fn addSymbolRelocAssumeCapacity(
8217 .addend = 0,9339 .addend = 0,
8218 }).toOptional();9340 }).toOptional();
8219 },9341 },
8220 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {9342 .dynamic => {
8221 switch (elf.classifySymbolValue(target)) {9343 target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true;
8222 .static => continue :classify .static,
8223 .static_relative => continue :classify .static_relative,
8224 .dynamic => unreachable, // we just added a copy relocation
8225 }
8226 } else {
8227 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {9344 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
8228 // PLT relocations targeting dynamic symbols actually target that symbol's PLT9345 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
8229 // entry, so we should emit an `R_*_RELATIVE` relocation instead.9346 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
...@@ -8255,7 +9372,7 @@ fn addSymbolRelocAssumeCapacity(...@@ -8255,7 +9372,7 @@ fn addSymbolRelocAssumeCapacity(
8255 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{9372 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8256 .type = dynamic_reloc_type,9373 .type = dynamic_reloc_type,
8257 .offset = node_vaddr + offset,9374 .offset = node_vaddr + offset,
8258 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,9375 .raw_sym_index = target.unwrap().global.dynsymIndex(elf).?,
8259 .addend = addend,9376 .addend = addend,
8260 }).toOptional();9377 }).toOptional();
8261 },9378 },
...@@ -8498,21 +9615,25 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8498,21 +9615,25 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8498 const sym_value = sym_id.value(elf);9615 const sym_value = sym_id.value(elf);
8499 break :val .{ .signed = @bitCast(sym_value -% tls_size) };9616 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
8500 }9617 }
8501 break :val switch (sym_id.unwrap()) {9618 switch (sym_id.unwrap()) {
8502 // For global symbols, just target the right dynsym with no addend.9619 .global => |gsi| if (gsi.dynsymIndex(elf)) |dynsym_index| {
8503 .global => |name| .{ .reloc = .{9620 // When there is a dynsym entry, just target that with no addend.
8504 .type = .tpOff(elf),9621 break :val .{ .reloc = .{
8505 .dynsym_index = elf.globalByName(name).?.dynsym_index,9622 .type = .tpOff(elf),
8506 .addend = 0,9623 .dynsym_index = dynsym_index,
8507 } },9624 .addend = 0,
8508 // For local symbols, target the null symbol (index 0) so we get the offset to the9625 } };
8509 // base of our TLS block, and then use `addend` to offset to the right symbol.9626 },
8510 .local => .{ .reloc = .{9627 .local => {},
8511 .type = .tpOff(elf),9628 }
8512 .dynsym_index = 0,9629 // Otherwise, we know the offset of the symbol into our own TLS block, so target the
8513 .addend = @intCast(sym_id.value(elf)),9630 // null symbol (index 0) so we get the offset to the base of that block, and use the
8514 } },9631 // addend to offset to the correct symbol.
8515 };9632 break :val .{ .reloc = .{
9633 .type = .tpOff(elf),
9634 .dynsym_index = 0,
9635 .addend = @intCast(sym_id.value(elf)),
9636 } };
8516 },9637 },
8517 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {9638 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
8518 .static => .{ .unsigned = sym.value(elf) },9639 .static => .{ .unsigned = sym.value(elf) },
...@@ -8523,7 +9644,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8523,7 +9644,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8523 } },9644 } },
8524 .dynamic => .{ .reloc = .{9645 .dynamic => .{ .reloc = .{
8525 .type = .globDat(elf),9646 .type = .globDat(elf),
8526 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,9647 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
8527 .addend = 0,9648 .addend = 0,
8528 } },9649 } },
8529 },9650 },
...@@ -8532,7 +9653,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8532,7 +9653,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8532 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`9653 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
8533 .dynamic => .{ .reloc = .{9654 .dynamic => .{ .reloc = .{
8534 .type = .dtpOff(elf),9655 .type = .dtpOff(elf),
8535 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,9656 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
8536 .addend = 0,9657 .addend = 0,
8537 } },9658 } },
8538 },9659 },
...@@ -8545,7 +9666,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8545,7 +9666,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8545 .type = .dtpMod(elf),9666 .type = .dtpMod(elf),
8546 .dynsym_index = switch (elf.classifySymbolValue(sym)) {9667 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
8547 .static, .static_relative => 0,9668 .static, .static_relative => 0,
8548 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,9669 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
8549 },9670 },
8550 .addend = 0,9671 .addend = 0,
8551 } },9672 } },
...@@ -8609,7 +9730,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8609,7 +9730,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
86099730
8610/// If `node` cannot contain runtime relocations, returns `.no`.9731/// If `node` cannot contain runtime relocations, returns `.no`.
8611///9732///
8612/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires9733/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires
8613/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.9734/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
8614fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {9735fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
8615 const shndx = elf.getNodeShndx(node);9736 const shndx = elf.getNodeShndx(node);
...@@ -8621,83 +9742,6 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye...@@ -8621,83 +9742,6 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
8621 return .yes;9742 return .yes;
8622}9743}
86239744
8624/// If the given undefined global could have a copy relocation, creates that relocation if it does
8625/// not already exist, and returns `true`.
8626///
8627/// Returns `false` iff a copy relocation cannot currently be created for the global. If it may be
8628/// possible in future, the symbol is added to `elf.want_copied_globals` so that the copy relocation
8629/// will be created if and when we discover a suitable definition in an input DSO.
8630///
8631/// If this function creates a new copy relocation, it will also update relocations targeting the
8632/// global where needed---the caller does not need to do this.
8633///
8634/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global.
8635fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
8636 assert(elf.shndx.dynamic != .UNDEF);
8637
8638 // Only dynamic executables may contain `R_*_COPY` relocations.
8639 if (elf.base.comp.config.output_mode != .Exe) return false;
8640
8641 const gpa = elf.base.comp.gpa;
8642
8643 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse
8644 elf.globals.weak_undef.getPtr(global_name).?;
8645
8646 assert(global_ptr.dynsym_index != 0);
8647
8648 const dso_global = elf.dso_globals.get(global_name) orelse {
8649 // We do not have a definition to provide the correct size for the symbol. If a definition
8650 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
8651 try elf.want_copied_globals.put(gpa, global_name, {});
8652 return false;
8653 };
8654
8655 if (dso_global.type != .OBJECT) return false;
8656
8657 const gop = try elf.copied_globals.getOrPut(gpa, global_name);
8658 if (gop.found_existing) return true;
8659 errdefer assert(elf.copied_globals.pop().?.key == global_name);
8660
8661 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
8662
8663 try elf.nodes.ensureUnusedCapacity(gpa, 1);
8664 const node = elf.addNodeAssumeCapacity(
8665 try Section.Index.data.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
8666 .size = dso_global.alignment.forward(dso_global.size),
8667 .alignment = dso_global.alignment,
8668 }),
8669 .{ .copied_global = global_name },
8670 );
8671 errdefer comptime unreachable;
8672
8673 const vaddr = elf.computeNodeVAddr(node);
8674 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8675 .type = .copy(elf),
8676 .offset = vaddr,
8677 .raw_sym_index = global_ptr.dynsym_index,
8678 .addend = 0,
8679 });
8680 gop.value_ptr.* = .{
8681 .node = node,
8682 .rela_index = rela_index,
8683 };
8684
8685 switch (elf.symPtr(global_ptr.symtab_index)) {
8686 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
8687 }
8688 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
8689 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
8690 }
8691
8692 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
8693 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
8694 // and then update the symbol's address (and re-apply relocations targeting it of course).
8695 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
8696 Symbol.Id.global(global_name).flushMoved(elf, vaddr);
8697
8698 return true;
8699}
8700
8701pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {9745pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
8702 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {9746 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
8703 else => |e| return e,9747 else => |e| return e,
...@@ -9232,14 +10276,18 @@ fn flushInner(...@@ -9232,14 +10276,18 @@ fn flushInner(
923210276
9233 try elf.flushFiles();10277 try elf.flushFiles();
923410278
9235 if (comp.config.output_mode == .Exe) {10279 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {
9236 var any_undef = false;10280 for (elf.unknown_globals.keys()) |gsi| {
9237 for (elf.globals.strong_undef.keys()) |name| {10281 diags.addError("undefined global symbol '{s}'", .{gsi.rawName(elf).slice(elf)});
9238 if (elf.dso_globals.contains(name)) continue;10282 }
9239 any_undef = true;10283 return error.AlreadyReported;
9240 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});10284 }
10285
10286 if (elf.defined_alias_globals.count() > 0) {
10287 for (elf.defined_alias_globals.keys()) |gsi| {
10288 diags.addError("multiple definitions of '{s}'", .{gsi.name(elf)});
9241 }10289 }
9242 if (any_undef) return error.AlreadyReported;10290 return error.AlreadyReported;
9243 }10291 }
924410292
9245 try elf.prepareDynamic();10293 try elf.prepareDynamic();
...@@ -9279,7 +10327,7 @@ fn flushInner(...@@ -9279,7 +10327,7 @@ fn flushInner(
9279 elf.flushDynamic();10327 elf.flushDynamic();
928010328
9281 const entry_addr: u64 = entry: {10329 const entry_addr: u64 = entry: {
9282 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {10330 const sym_name: []const u8 = name: switch (elf.options.entry) {
9283 .default => switch (comp.config.output_mode) {10331 .default => switch (comp.config.output_mode) {
9284 .Exe => continue :name .enabled,10332 .Exe => continue :name .enabled,
9285 .Lib, .Obj => continue :name .disabled,10333 .Lib, .Obj => continue :name .disabled,
...@@ -9288,9 +10336,11 @@ fn flushInner(...@@ -9288,9 +10336,11 @@ fn flushInner(
9288 .enabled => "_start",10336 .enabled => "_start",
9289 .named => |named| named,10337 .named => |named| named,
9290 };10338 };
9291 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);10339 const gsi = elf.globalByName(.{
9292 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;10340 .name = sym_name,
9293 break :entry Symbol.Id.global(sym_name_strtab).value(elf);10341 .version = null,
10342 }) orelse break :entry 0;
10343 break :entry Symbol.Id.global(gsi).value(elf);
9294 };10344 };
9295 switch (elf.ehdrPtr()) {10345 switch (elf.ehdrPtr()) {
9296 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),10346 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
...@@ -9359,26 +10409,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -9359,26 +10409,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9359 break :task;10409 break :task;
9360 }10410 }
9361 if (elf.changed_symtab_index.pop()) |kv| {10411 if (elf.changed_symtab_index.pop()) |kv| {
9362 const idle_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);10412 const gsi = kv.key;
10413
10414 const idle_prog_node = elf.mf.update_prog_node.start(gsi.rawName(elf).slice(elf), 0);
9363 defer idle_prog_node.end();10415 defer idle_prog_node.end();
936410416
9365 const global_name = kv.key;10417 const sym_id: Symbol.Id = .global(gsi);
9366 const global = elf.globalByName(global_name).?;10418 const symtab_index = gsi.ptr(elf).symtab_index;
9367 const sym_id: Symbol.Id = .global(global_name);
9368 const sym = global.symtab_index.ptr(elf);
936910419
9370 switch (elf.ehdrType()) {10420 switch (elf.ehdrType()) {
9371 .REL => {10421 .REL => {
9372 // Index in `.symtab` has changed. Relocatables are easy, we just need to update10422 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
9373 // all of the output relocations.10423 // all of the output relocations.
9374 const symtab_index = @backingInt(global.symtab_index);10424 var ri = symtab_index.ptr(elf).first_target_reloc;
9375 var ri = sym.first_target_reloc;
9376 while (ri != .none) {10425 while (ri != .none) {
9377 const reloc = ri.get(elf);10426 const reloc = ri.get(elf);
9378 assert(reloc.target == sym_id);10427 assert(reloc.target == sym_id);
9379 // In relocatables, every symbol relocation has an output relocation.10428 // In relocatables, every symbol relocation has an output relocation.
9380 const rela_index = reloc.rela_index.unwrap().?;10429 const rela_index = reloc.rela_index.unwrap().?;
9381 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index);10430 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, @backingInt(symtab_index));
9382 ri = reloc.next;10431 ri = reloc.next;
9383 }10432 }
9384 },10433 },
...@@ -9391,29 +10440,32 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -9391,29 +10440,32 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9391 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they10440 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
9392 // don't care about the dynsym index). The only exception is a copy reloc10441 // don't care about the dynsym index). The only exception is a copy reloc
9393 // could exist (and be the *reason* the symbol value is statically known).10442 // could exist (and be the *reason* the symbol value is statically known).
9394 if (elf.copied_globals.get(global_name)) |copied| {10443 if (elf.copied_globals.get(gsi)) |copied| {
9395 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);10444 const dynsym_index = gsi.dynsymIndex(elf).?;
10445 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);
9396 }10446 }
9397 },10447 },
9398 .dynamic => {10448 .dynamic => {
9399 assert(!elf.copied_globals.contains(global_name)); // value would be statically known10449 assert(!elf.copied_globals.contains(gsi)); // value would be statically known
10450
10451 const dynsym_index = gsi.dynsymIndex(elf).?;
940010452
9401 // Update symbol relocs:10453 // Update symbol relocs:
9402 var ri = sym.first_target_reloc;10454 var ri = symtab_index.ptr(elf).first_target_reloc;
9403 while (ri != .none) {10455 while (ri != .none) {
9404 const reloc = ri.get(elf);10456 const reloc = ri.get(elf);
9405 assert(reloc.target == sym_id);10457 assert(reloc.target == sym_id);
9406 // There may or may not be a runtime relocation for this symbol reloc.10458 // There may or may not be a runtime relocation for this symbol reloc.
9407 if (reloc.rela_index.unwrap()) |rela_index| {10459 if (reloc.rela_index.unwrap()) |rela_index| {
9408 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index);10460 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, dynsym_index);
9409 }10461 }
9410 ri = reloc.next;10462 ri = reloc.next;
9411 }10463 }
941210464
9413 // Update the PLT entry's reloc if there is one:10465 // Update the PLT entry's reloc if there is one:
9414 if (elf.plt.getIndex(global_name)) |plt_index| {10466 if (elf.plt.getIndex(gsi)) |plt_index| {
9415 // PLT indices exactly match `.rela.plt` relocation indices.10467 // PLT indices exactly match `.rela.plt` relocation indices.
9416 elf.shndx.rela_plt.relaUpdateSym(elf, @fromBackingInt(@intCast(plt_index)), global.dynsym_index);10468 elf.shndx.rela_plt.relaUpdateSym(elf, @fromBackingInt(@intCast(plt_index)), dynsym_index);
9417 }10469 }
941810470
9419 // Update relocs for any relevant GOT entries:10471 // Update relocs for any relevant GOT entries:
...@@ -9577,6 +10629,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {...@@ -9577,6 +10629,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
9577 .code_view => unreachable,10629 .code_view => unreachable,
9578 }10630 }
9579 try elf.genPendingDebug(gpa);10631 try elf.genPendingDebug(gpa);
10632 assert(elf.pending_uavs.items.len == 0); // no UAVs added by lazy code/data or by debug info
9580}10633}
9581fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {10634fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
9582 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {10635 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {
...@@ -9872,13 +10925,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9872,13 +10925,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9872 }10925 }
987310926
9874 // Update global symbols targeting this section10927 // Update global symbols targeting this section
9875 if (elf.node_global_symbols.get(ni)) |first_name| {10928 if (elf.node_global_symbols.get(ni)) |first_gsi| {
9876 assert(first_name != .empty);10929 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
9877 var name = first_name;10930 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
9878 while (name != .empty) {10931 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
9879 const old_sym_addr = Symbol.Id.global(name).value(elf);10932 Symbol.Id.global(gsi).flushMoved(elf, old_sym_addr - old_addr + addr);
9880 Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr);
9881 name = elf.globalByName(name).?.next_in_node;
9882 }10933 }
9883 }10934 }
988410935
...@@ -9940,16 +10991,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9940,16 +10991,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9940 }10991 }
994110992
9942 // Update global symbols10993 // Update global symbols
9943 if (elf.node_global_symbols.get(ni)) |first_name| {10994 if (elf.node_global_symbols.get(ni)) |first_gsi| {
9944 assert(first_name != .empty);10995 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
9945 var name = first_name;10996 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
9946 while (name != .empty) {10997 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
9947 const old_sym_addr = Symbol.Id.global(name).value(elf);10998 Symbol.Id.global(gsi).flushMoved(
9948 Symbol.Id.global(name).flushMoved(
9949 elf,10999 elf,
9950 old_sym_addr - old_section_addr + new_section_addr,11000 old_sym_addr - old_section_addr + new_section_addr,
9951 );11001 );
9952 name = elf.globalByName(name).?.next_in_node;
9953 }11002 }
9954 }11003 }
995511004
...@@ -9961,7 +11010,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9961,7 +11010,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9961 .copied_global => |global_name| {11010 .copied_global => |global_name| {
9962 const copied_global = elf.copied_globals.getPtr(global_name) orelse {11011 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
9963 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet11012 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet
9964 // support deleting nodes. See logic in `setGlobalSymbolValue`.11013 // support deleting nodes. See logic in `updateGlobalDynamic`.
9965 return;11014 return;
9966 };11015 };
9967 assert(copied_global.node == ni);11016 assert(copied_global.node == ni);
...@@ -9974,12 +11023,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -9974,12 +11023,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
9974 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {11023 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {
9975 const new_addr = elf.computeNodeVAddr(ni);11024 const new_addr = elf.computeNodeVAddr(ni);
9976 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);11025 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
9977 if (elf.node_global_symbols.get(ni)) |first_name| {11026 if (elf.node_global_symbols.get(ni)) |first_gsi| {
9978 assert(first_name != .empty);11027 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
9979 var name = first_name;11028 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
9980 while (name != .empty) {11029 Symbol.Id.global(gsi).flushMoved(elf, new_addr);
9981 Symbol.Id.global(name).flushMoved(elf, new_addr);
9982 name = elf.globalByName(name).?.next_in_node;
9983 }11030 }
9984 }11031 }
9985 elf.flushMovedNodeRelocs(ni, new_addr, .{11032 elf.flushMovedNodeRelocs(ni, new_addr, .{
...@@ -10712,42 +11759,78 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -10712,42 +11759,78 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
10712 }11759 }
10713}11760}
1071411761
10715fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {11762/// If `gsi` does not have a PLT entry, adds one and returns `true`. If `gsi` already has a PLT
11763/// entry, does nothing and returns `false`.
11764///
11765/// Asserts that `gsi` owns a `.dynsym` entry.
11766fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
10716 const target_endian = elf.targetEndian();11767 const target_endian = elf.targetEndian();
1071711768
11769 const maybe_dead_plt_index: ?u32 = dead_plt_index: {
11770 const old_plt_index = elf.plt.getIndex(gsi) orelse break :dead_plt_index null;
11771 if (!elf.pltEntryIsDead(old_plt_index)) return false;
11772 break :dead_plt_index @intCast(old_plt_index);
11773 };
11774
11775 const plt = elf.targetPltInfo();
11776
11777 const gpa = elf.base.comp.gpa;
11778 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, 1);
11779 try elf.plt.ensureUnusedCapacity(gpa, 1);
11780 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(
11781 gpa,
11782 &elf.mf,
11783 plt.entry_size * (plt.header_entries + elf.plt.count() + 1),
11784 );
11785 if (plt.got_plt) |got_plt| try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(
11786 gpa,
11787 &elf.mf,
11788 elf.targetPtrSize() * (got_plt.header_entries + elf.plt.count() + 1),
11789 );
11790 if (plt.plt_sec) |plt_sec| try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(
11791 gpa,
11792 &elf.mf,
11793 plt_sec.entry_size * (elf.plt.count() + 1),
11794 );
11795
10718 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a11796 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
10719 // free-list for the PLT itself---see `pltEntryIsDead` for details.11797 // free-list for the PLT itself---see `pltEntryIsDead` for details.
10720 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{11798 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
10721 .type = .jumpSlot(elf),11799 .type = .jumpSlot(elf),
10722 .offset = 0, // populated later11800 .offset = 0, // populated later
10723 .raw_sym_index = dynsym_index,11801 .raw_sym_index = gsi.ownedDynsymIndex(elf).?,
10724 .addend = 0,11802 .addend = 0,
10725 }));11803 }));
1072611804
10727 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.11805 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.
10728 const got_plt_section: Section.Index, const got_plt_offset: u64 = got_plt: {11806 const got_plt_section: Section.Index, const got_plt_offset: u64 = if (plt.got_plt) |got_plt| .{
10729 const plt = elf.targetPltInfo();11807 elf.shndx.got_plt,
10730 break :got_plt if (plt.got_plt) |got_plt| .{11808 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
10731 elf.shndx.got_plt,11809 } else .{
10732 elf.targetPtrSize() * (got_plt.header_entries + plt_index),11810 elf.shndx.plt,
10733 } else .{11811 plt.entry_size * (plt.header_entries + plt_index),
10734 elf.shndx.plt,
10735 plt.entry_size * (plt.header_entries + plt_index),
10736 };
10737 };11812 };
1073811813
10739 // Now that we know the index, we can set the relocation's offset.11814 // Now that we know the index, we can set the relocation's offset.
10740 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);11815 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
1074111816
10742 if (plt_index < elf.plt.count()) {11817 if (plt_index < elf.plt.count()) {
10743 // We reused a free entry, so we're already done!11818 // We reused a free entry, so we're already done! However, if this global has a dead PLT
10744 elf.plt.setKey(plt_index, global_name);11819 // entry, then `gsi` is already a key in `elf.plt`, so we'll need to swap it out for some
10745 return;11820 // other dead key.
11821 if (maybe_dead_plt_index) |dead_plt_index| {
11822 const dead_gsi = elf.plt.keys()[plt_index];
11823 elf.plt.setKey(dead_plt_index, dead_gsi);
11824 }
11825 elf.plt.setKey(plt_index, gsi);
11826 return true;
10746 }11827 }
1074711828
11829 assert(maybe_dead_plt_index == null); // if there were a dead entry we would have reused it
11830
10748 // We added a new entry, so we now need to extend the PLT sections.11831 // We added a new entry, so we now need to extend the PLT sections.
10749 assert(plt_index == elf.plt.count());11832 assert(plt_index == elf.plt.count());
10750 elf.plt.putAssumeCapacityNoClobber(global_name, {});11833 elf.plt.putAssumeCapacityNoClobber(gsi, {});
1075111834
10752 switch (elf.ehdrMachine()) {11835 switch (elf.ehdrMachine()) {
10753 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),11836 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
...@@ -10906,6 +11989,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -10906,6 +11989,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10906 }11989 }
10907 },11990 },
10908 }11991 }
11992
11993 return true;
10909}11994}
10910fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {11995fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
10911 const target_endian = elf.targetEndian();11996 const target_endian = elf.targetEndian();
...@@ -10919,8 +12004,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -10919,8 +12004,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
10919 // its relocations are probably going through the PLT, so we don't bother with12004 // its relocations are probably going through the PLT, so we don't bother with
10920 // specific tracking for PLT relocations---instead just re-apply all relocations12005 // specific tracking for PLT relocations---instead just re-apply all relocations
10921 // targeting symbols with PLT entries.12006 // targeting symbols with PLT entries.
10922 for (elf.plt.keys()) |name| {12007 for (elf.plt.keys()) |gsi| {
10923 Symbol.Id.global(name).applyTargetRelocs(elf);12008 Symbol.Id.global(gsi).applyTargetRelocs(elf);
10924 }12009 }
10925 // We also need to update all of the references from `.plt.sec` to `.got.plt`.12010 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
10926 // However, if there's also a flush pending for `.got.plt`, don't bother doing12011 // However, if there's also a flush pending for `.got.plt`, don't bother doing
...@@ -10976,8 +12061,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -10976,8 +12061,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
10976 // its relocations are probably going through the PLT, so we don't bother with12061 // its relocations are probably going through the PLT, so we don't bother with
10977 // specific tracking for PLT relocations---instead just re-apply all relocations12062 // specific tracking for PLT relocations---instead just re-apply all relocations
10978 // targeting symbols with PLT entries.12063 // targeting symbols with PLT entries.
10979 for (elf.plt.keys()) |name| {12064 for (elf.plt.keys()) |gsi| {
10980 Symbol.Id.global(name).applyTargetRelocs(elf);12065 Symbol.Id.global(gsi).applyTargetRelocs(elf);
10981 }12066 }
10982 // We also need to update all of the references from `.plt` to `.got.plt`.12067 // We also need to update all of the references from `.plt` to `.got.plt`.
10983 // However, if there's also a flush pending for `.got.plt`, don't bother doing12068 // However, if there's also a flush pending for `.got.plt`, don't bother doing
...@@ -11043,8 +12128,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -11043,8 +12128,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
11043 // its relocations are probably going through the PLT, so we don't bother with12128 // its relocations are probably going through the PLT, so we don't bother with
11044 // specific tracking for PLT relocations---instead just re-apply all relocations12129 // specific tracking for PLT relocations---instead just re-apply all relocations
11045 // targeting symbols with PLT entries.12130 // targeting symbols with PLT entries.
11046 for (elf.plt.keys()) |name| {12131 for (elf.plt.keys()) |gsi| {
11047 Symbol.Id.global(name).applyTargetRelocs(elf);12132 Symbol.Id.global(gsi).applyTargetRelocs(elf);
11048 }12133 }
11049 // Update the offsets of the relocation entries in `.rela.plt`.12134 // Update the offsets of the relocation entries in `.rela.plt`.
11050 const rela_plt_shndx = elf.shndx.rela_plt;12135 const rela_plt_shndx = elf.shndx.rela_plt;
...@@ -11069,6 +12154,7 @@ pub fn updateExports(...@@ -11069,6 +12154,7 @@ pub fn updateExports(
11069 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),12154 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
11070 };12155 };
11071 }12156 }
12157 try elf.genPending(pt);
11072}12158}
11073fn updateExportInner(12159fn updateExportInner(
11074 elf: *Elf,12160 elf: *Elf,
...@@ -11087,7 +12173,6 @@ fn updateExportInner(...@@ -11087,7 +12173,6 @@ fn updateExportInner(
11087 Value.fromInterned(uav).fmtValue(pt),12173 Value.fromInterned(uav).fmtValue(pt),
11088 }),12174 }),
11089 }12175 }
11090 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
11091 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {12176 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
11092 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),12177 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
11093 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),12178 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
...@@ -11106,9 +12191,9 @@ fn updateExportInner(...@@ -11106,9 +12191,9 @@ fn updateExportInner(
11106 };12191 };
1110712192
11108 const name = @"export".opts.name.toSlice(ip);12193 const name = @"export".opts.name.toSlice(ip);
11109 _ = elf.addGlobalSymbolAssumeCapacity(.{12194 _ = elf.addGlobalSymbol(.{
11110 .node = exported_lsi.index().ptr(elf).node,12195 .node = exported_lsi.index().ptr(elf).node,
11111 .name = try .string(elf, name),12196 .name = name,
11112 .value = cur_value,12197 .value = cur_value,
11113 .size = cur_size,12198 .size = cur_size,
11114 .type = @"type",12199 .type = @"type",
...@@ -11130,8 +12215,12 @@ fn updateExportInner(...@@ -11130,8 +12215,12 @@ fn updateExportInner(
11130 // get these errors on every non-initial incremental update. Hack around that by12215 // get these errors on every non-initial incremental update. Hack around that by
11131 // only emitting this error if the symbol we're conflicting with comes from an input12216 // only emitting this error if the symbol we're conflicting with comes from an input
11132 // section (as opposed to the ZCU).12217 // section (as opposed to the ZCU).
11133 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;12218 const parsed = parseVersionedSymbolName(name);
11134 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {12219 const conflicting_global = elf.globalByName(.{
12220 .name = parsed.name,
12221 .version = parsed.version,
12222 }).?;
12223 if (conflicting_global.ptr(elf).symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
11135 if (elf.getNode(conflicting_node) == .input_section) {12224 if (elf.getNode(conflicting_node) == .input_section) {
11136 return elf.base.comp.link_diags.fail(12225 return elf.base.comp.link_diags.fail(
11137 "multiple definitions of '{s}'",12226 "multiple definitions of '{s}'",
...@@ -11140,6 +12229,12 @@ fn updateExportInner(...@@ -11140,6 +12229,12 @@ fn updateExportInner(
11140 }12229 }
11141 }12230 }
11142 },12231 },
12232 error.MultipleDefaultVersions => return elf.base.comp.link_diags.fail(
12233 "multiple default versions of '{s}'",
12234 .{parseVersionedSymbolName(name).name},
12235 ),
12236 error.UndefinedDefaultVersion => unreachable, // this is an export, so the symbol is defined
12237 else => |e| return e,
11143 };12238 };
11144}12239}
1114512240
...@@ -11204,7 +12299,7 @@ pub fn printNode(...@@ -11204,7 +12299,7 @@ pub fn printNode(
11204 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),12299 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
11205 });12300 });
11206 },12301 },
11207 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),12302 .copied_global => |gsi| try w.print("(copy:{s})", .{gsi.rawName(elf).slice(elf)}),
11208 .nav => |nmi| {12303 .nav => |nmi| {
11209 const zcu = elf.base.comp.zcu.?;12304 const zcu = elf.base.comp.zcu.?;
11210 const ip = &zcu.intern_pool;12305 const ip = &zcu.intern_pool;
...@@ -11392,7 +12487,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato...@@ -11392,7 +12487,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato
11392fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {12487fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
11393 const index = switch (sym.unwrap()) {12488 const index = switch (sym.unwrap()) {
11394 .local => return null,12489 .local => return null,
11395 .global => |name| elf.plt.getIndex(name) orelse return null,12490 .global => |gsi| elf.plt.getIndex(gsi.resolveAlias(elf)) orelse return null,
11396 };12491 };
11397 if (elf.pltEntryIsDead(index)) return null;12492 if (elf.pltEntryIsDead(index)) return null;
11398 const plt = elf.targetPltInfo();12493 const plt = elf.targetPltInfo();
...@@ -11402,3 +12497,256 @@ fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {...@@ -11402,3 +12497,256 @@ fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
11402 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;12497 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
11403 }12498 }
11404}12499}
12500
12501fn parseVersionedSymbolName(raw_name: []const u8) struct {
12502 name: []const u8,
12503 version: ?[]const u8,
12504 is_default_version: bool,
12505} {
12506 const split_index = std.mem.findScalar(u8, raw_name, '@') orelse {
12507 return .{ .name = raw_name, .version = null, .is_default_version = false };
12508 };
12509 if (std.mem.startsWith(u8, raw_name[split_index..], "@@")) {
12510 return .{
12511 .name = raw_name[0..split_index],
12512 .version = raw_name[split_index + 2 ..],
12513 .is_default_version = true,
12514 };
12515 } else {
12516 return .{
12517 .name = raw_name[0..split_index],
12518 .version = raw_name[split_index + 1 ..],
12519 .is_default_version = false,
12520 };
12521 }
12522}
12523
12524const VerdefAdapter = struct {
12525 elf: *Elf,
12526 pub fn eql(ctx: VerdefAdapter, lhs_name: String(.dynstr), _: void, rhs_index: usize) bool {
12527 const elf = ctx.elf;
12528 const rhs_entry = &elf.verdefSlice()[rhs_index + 1];
12529 const rhs_name: String(.dynstr) = @fromBackingInt(
12530 elf.targetLoad(&rhs_entry.aux.name),
12531 );
12532 return lhs_name == rhs_name;
12533 }
12534 pub fn hash(ctx: VerdefAdapter, name: String(.dynstr)) u32 {
12535 _ = ctx;
12536 return @truncate(std.hash.int(@backingInt(name)));
12537 }
12538};
12539
12540fn verdefId(elf: *Elf, version: String(.dynstr)) Error!u15 {
12541 const gpa = elf.base.comp.gpa;
12542 const adapter: VerdefAdapter = .{ .elf = elf };
12543 try elf.shndx.gnu_version_d.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, (elf.verdef.count() + 2) * @sizeOf(VerdefEntry));
12544 const gop = try elf.verdef.getOrPutAdapted(gpa, version, adapter);
12545
12546 if (gop.found_existing) {
12547 const verdef_ptr = &elf.verdefSlice()[gop.index + 1].def;
12548 return @intCast(@backingInt(elf.targetLoad(&verdef_ptr.ndx)));
12549 }
12550
12551 const version_id = std.math.cast(u15, elf.next_version_id) orelse {
12552 return elf.base.comp.link_diags.fail(
12553 "symbol version identifier '{d}' out of range",
12554 .{elf.next_version_id},
12555 );
12556 };
12557 elf.next_version_id += 1;
12558
12559 errdefer comptime unreachable;
12560
12561 switch (elf.shdrPtr(elf.shndx.gnu_version_d)) {
12562 inline else => |shdr| {
12563 const old_size = elf.targetLoad(&shdr.size);
12564 const old_info = elf.targetLoad(&shdr.info);
12565 assert(old_info == gop.index + 1);
12566 assert(old_size == old_info * @sizeOf(VerdefEntry));
12567 elf.targetStore(&shdr.info, old_info + 1);
12568 elf.targetStore(&shdr.size, old_size + @sizeOf(VerdefEntry));
12569 },
12570 }
12571 const entry_ptr = &elf.verdefSlice()[gop.index + 1];
12572 entry_ptr.* = .{
12573 .def = .{
12574 .version = 1,
12575 .flags = 0,
12576 .ndx = @fromBackingInt(version_id),
12577 .cnt = 1,
12578 .hash = std.elf.hash.calculate(version.slice(elf)),
12579 .aux = @offsetOf(VerdefEntry, "aux"),
12580 .next = @sizeOf(VerdefEntry),
12581 },
12582 .aux = .{
12583 .name = @backingInt(version),
12584 .next = 0,
12585 },
12586 };
12587 if (elf.targetEndian() != std.lang.Endian.native) {
12588 std.mem.byteSwapAllFields(VerdefEntry, entry_ptr);
12589 }
12590
12591 return version_id;
12592}
12593
12594const VerneedAdapter = struct {
12595 elf: *Elf,
12596 const Key = struct {
12597 file: String(.dynstr),
12598 /// If this is `null`, key refers to a `std.elf.Verneed` entry; otherwise it refers to a
12599 /// `std.elf.Vernaux` entry.
12600 version: ?String(.dynstr),
12601 };
12602 const StoredKey = struct {
12603 kind: enum(u8) { verneed, vernaux },
12604 /// If `kind == .verneed`, this is the index in `.gnu.version_r` of the last
12605 /// `std.elf.Vernaux` in this `std.elf.Verneed`.
12606 ///
12607 /// If `kind == .vernaux`, this is the index in `.gnu.version_r` of the `std.elf.Verneed`
12608 /// associated with this `std.elf.Vernaux`.
12609 last_or_verneed_index: u32,
12610 };
12611 pub fn eql(ctx: VerneedAdapter, lhs: Key, rhs_stored: StoredKey, rhs_index: usize) bool {
12612 const elf = ctx.elf;
12613
12614 const lhs_version = lhs.version orelse {
12615 if (rhs_stored.kind != .verneed) return false;
12616 const rhs_file: String(.dynstr) = @fromBackingInt(elf.targetLoad(
12617 &elf.verneedSlice()[rhs_index].verneed.file,
12618 ));
12619 return lhs.file == rhs_file;
12620 };
12621
12622 if (rhs_stored.kind != .vernaux) return false;
12623 const rhs_file: String(.dynstr) = @fromBackingInt(elf.targetLoad(
12624 &elf.verneedSlice()[rhs_stored.last_or_verneed_index].verneed.file,
12625 ));
12626 const rhs_version: String(.dynstr) = @fromBackingInt(elf.targetLoad(
12627 &elf.verneedSlice()[rhs_index].vernaux.name,
12628 ));
12629
12630 return lhs.file == rhs_file and lhs_version == rhs_version;
12631 }
12632 pub fn hash(ctx: VerneedAdapter, key: Key) u32 {
12633 _ = ctx;
12634 const packed_key: packed struct(u65) {
12635 file: String(.dynstr),
12636 have_version: bool,
12637 version: String(.dynstr),
12638 } = .{
12639 .file = key.file,
12640 .have_version = key.version != null,
12641 .version = key.version orelse .empty,
12642 };
12643 return @truncate(std.hash.int(@backingInt(packed_key)));
12644 }
12645};
12646
12647fn verneedId(elf: *Elf, file: String(.dynstr), version: String(.dynstr)) Error!u15 {
12648 const gpa = elf.base.comp.gpa;
12649 const adapter: VerneedAdapter = .{ .elf = elf };
12650 try elf.shndx.gnu_version_r.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, (elf.verneed.count() + 2) * @sizeOf(VerneedEntry));
12651
12652 if (elf.verneed.getIndexAdapted(@as(VerneedAdapter.Key, .{
12653 .file = file,
12654 .version = version,
12655 }), adapter)) |index| {
12656 const vernaux_ptr = &elf.verneedSlice()[index].vernaux;
12657 return @intCast(elf.targetLoad(&vernaux_ptr.other));
12658 }
12659
12660 const file_gop = try elf.verneed.getOrPutAdapted(gpa, @as(VerneedAdapter.Key, .{
12661 .file = file,
12662 .version = null,
12663 }), adapter);
12664
12665 const predicted_vernaux_index: u32 = @intCast(elf.verneed.count());
12666
12667 if (!file_gop.found_existing) {
12668 switch (elf.shdrPtr(elf.shndx.gnu_version_r)) {
12669 inline else => |shdr| {
12670 const old_size = elf.targetLoad(&shdr.size);
12671 const old_info = elf.targetLoad(&shdr.info);
12672 elf.targetStore(&shdr.info, old_info + 1);
12673 elf.targetStore(&shdr.size, old_size + @sizeOf(VerneedEntry));
12674 },
12675 }
12676 const new_verneed = &elf.verneedSlice()[file_gop.index].verneed;
12677 new_verneed.* = .{
12678 .version = 1,
12679 .cnt = 1,
12680 .file = @backingInt(file),
12681 .aux = @sizeOf(VerneedEntry),
12682 .next = 0,
12683 };
12684 if (elf.targetEndian() != std.lang.Endian.native) {
12685 std.mem.byteSwapAllFields(std.elf.Verneed, new_verneed);
12686 }
12687 file_gop.key_ptr.* = .{
12688 .kind = .verneed,
12689 .last_or_verneed_index = predicted_vernaux_index,
12690 };
12691
12692 if (file_gop.index != 0) {
12693 const prev_verneed = &elf.verneedSlice()[elf.last_verneed_file_index].verneed;
12694 const off = (file_gop.index - elf.last_verneed_file_index) * @sizeOf(VerneedEntry);
12695 assert(off > 0);
12696 elf.targetStore(&prev_verneed.next, @intCast(off));
12697 }
12698 elf.last_verneed_file_index = file_gop.index;
12699 } else {
12700 const verneed_ptr = &elf.verneedSlice()[file_gop.index].verneed;
12701 const old_cnt = elf.targetLoad(&verneed_ptr.cnt);
12702 assert(old_cnt > 0);
12703 elf.targetStore(&verneed_ptr.cnt, old_cnt + 1);
12704
12705 const last_vernaux_index = file_gop.key_ptr.last_or_verneed_index;
12706 const last_vernaux = &elf.verneedSlice()[last_vernaux_index].vernaux;
12707 assert(elf.targetLoad(&last_vernaux.next) == 0);
12708 const off = (predicted_vernaux_index - last_vernaux_index) * @sizeOf(VerneedEntry);
12709 assert(off > 0);
12710 elf.targetStore(&last_vernaux.next, @intCast(off));
12711 file_gop.key_ptr.last_or_verneed_index = predicted_vernaux_index;
12712 }
12713
12714 const version_gop = try elf.verneed.getOrPutAdapted(gpa, @as(VerneedAdapter.Key, .{
12715 .file = file,
12716 .version = version,
12717 }), adapter);
12718 assert(!version_gop.found_existing); // already checked `contains` earlier
12719 assert(version_gop.index == predicted_vernaux_index);
12720 switch (elf.shdrPtr(elf.shndx.gnu_version_r)) {
12721 inline else => |shdr| {
12722 const old_size = elf.targetLoad(&shdr.size);
12723 elf.targetStore(&shdr.size, old_size + @sizeOf(VerneedEntry));
12724 },
12725 }
12726 version_gop.key_ptr.* = .{
12727 .kind = .vernaux,
12728 .last_or_verneed_index = @intCast(file_gop.index),
12729 };
12730
12731 const version_id = std.math.cast(u15, elf.next_version_id) orelse {
12732 return elf.base.comp.link_diags.fail(
12733 "symbol version identifier '{d}' out of range",
12734 .{elf.next_version_id},
12735 );
12736 };
12737 elf.next_version_id += 1;
12738
12739 const new_vernaux = &elf.verneedSlice()[version_gop.index].vernaux;
12740 new_vernaux.* = .{
12741 .hash = std.elf.hash.calculate(version.slice(elf)),
12742 .flags = 0,
12743 .other = version_id,
12744 .name = @backingInt(version),
12745 .next = 0,
12746 };
12747 if (elf.targetEndian() != std.lang.Endian.native) {
12748 std.mem.byteSwapAllFields(std.elf.Vernaux, new_vernaux);
12749 }
12750
12751 return version_id;
12752}
src/target.zig+11
...@@ -285,6 +285,17 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {...@@ -285,6 +285,17 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
285 };285 };
286}286}
287287
288pub fn preferNewLinkerOverLld(target: *const std.Target) bool {
289 return switch (target.ofmt) {
290 .elf => switch (target.cpu.arch) {
291 // Elf2 is more complete than LLD on these targets.
292 .sparc64 => true,
293 else => false,
294 },
295 else => false,
296 };
297}
298
288/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.299/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.
289pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {300pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {
290 return switch (ofmt) {301 return switch (ofmt) {