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 {
15651565 name: Word,
15661566 next: Word,
15671567};
1568/// Deprecated, use `std.elf.Verneed`
15681569pub const Elf32_Verneed = extern struct {
15691570 vn_version: Half,
15701571 vn_cnt: Half,
......@@ -1572,6 +1573,7 @@ pub const Elf32_Verneed = extern struct {
15721573 vn_aux: Word,
15731574 vn_next: Word,
15741575};
1576/// Deprecated, use `std.elf.Verneed`
15751577pub const Elf64_Verneed = extern struct {
15761578 vn_version: Half,
15771579 vn_cnt: Half,
......@@ -1579,6 +1581,13 @@ pub const Elf64_Verneed = extern struct {
15791581 vn_aux: Word,
15801582 vn_next: Word,
15811583};
1584pub const Verneed = extern struct {
1585 version: Half,
1586 cnt: Half,
1587 file: Word,
1588 aux: Word,
1589 next: Word,
1590};
15821591pub const Vernaux = extern struct {
15831592 hash: Word,
15841593 flags: Half,
src/Compilation/Config.zig+8
......@@ -413,6 +413,12 @@ pub fn resolve(options: Options) ResolveError!Config {
413413 // assume no LLD.
414414 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
416422 // If we have no zig code to compile, no need for the self-hosted linker.
417423 if (!options.have_zcu) break :b true;
418424
......@@ -448,6 +454,8 @@ pub fn resolve(options: Options) ResolveError!Config {
448454
449455 if (options.use_new_linker) |x| break :b x;
450456
457 if (target_util.preferNewLinkerOverLld(target)) break :b true;
458
451459 break :b options.incremental;
452460 };
453461
src/link/Dwarf2.zig+3-6
......@@ -1855,12 +1855,9 @@ pub fn genDebugInfoHeader(
18551855 try dwarf.abbrevCode(dih_nw, .module);
18561856 try dwarf.strx(dih_nw, mod.fully_qualified_name);
18571857 try dih_w.writeUleb128(0);
1858 try dwarf.genModuleDependency(
1859 dih_nw,
1860 "builtin",
1861 zcu.builtin_modules.get(mod.getBuiltinOptions(comp.config).hash()).?,
1862 module_offset,
1863 );
1858 if (zcu.builtin_modules.get(mod.getBuiltinOptions(comp.config).hash())) |builtin_mod| {
1859 try dwarf.genModuleDependency(dih_nw, "builtin", builtin_mod, module_offset);
1860 }
18641861 try dwarf.genModuleDependency(dih_nw, "root", zcu.root_mod, module_offset);
18651862 try dwarf.genModuleDependency(dih_nw, "std", zcu.std_mod, module_offset);
18661863 for (mod.deps.keys(), mod.deps.values()) |name, dep|
src/link/Elf2.zig+2364-1016
......@@ -52,6 +52,9 @@ shndx: struct {
5252 tdata: Section.Index,
5353 rela_dyn: Section.Index,
5454 rela_plt: Section.Index,
55 gnu_version: Section.Index,
56 gnu_version_d: Section.Index,
57 gnu_version_r: Section.Index,
5558 debug_abbrev: Section.Index,
5659 debug_addr: Section.Index,
5760 eh_frame_hdr: Section.Index,
......@@ -75,55 +78,63 @@ dynamic: struct {
7578 soname: String(.dynstr),
7679},
7780symtab: std.ArrayList(Symbol),
78globals: struct {
79 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
80 weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
81 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
82 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
83},
84/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).
85copied_globals: std.array_hash_map.Auto(String(.strtab), struct {
81globals: std.ArrayList(Symbol.Global),
82/// Accessed with `Symbol.Global.VersionedName.Adapter`. Items map 1--1 to `globals`.
83globals_by_name: std.array_hash_map.Custom(void, void, void, true),
84/// Accessed with `Symbol.Global.DefaultVersionAdapter`.
85default_version_globals: std.array_hash_map.Custom(Symbol.Global.Index, void, void, true),
86/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We
87/// maintain this set to allow efficient reporting of "undefined global symbol" errors.
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 {
8694 node: MappedFile.Node.Index,
8795 /// The index of this global's runtime relocation in `.rela.dyn`.
8896 rela_index: Section.RelaIndex,
8997}),
90/// Key is the name of an undef global for which we would *like* to create a copy relocation
91/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.
98/// When rearranging entries in the dynamic symbol table, we need to map a dynamic symbol index to
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.
92104///
93/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
94/// will remove it from this map and call `maybeAddCopyRelocation`.
95want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),
96/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
97/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
105/// Key is index into `.dynsym`.
106versioned_dynsym_owners: std.array_hash_map.Auto(u32, Symbol.Global.Index),
107/// Accessed with `VerdefAdapter`. Entries in `.gnu.version_d` are contiguous verdef/verdaux pairs
108/// (see `VerdefEntry`), and the "base" version is not in this map, so a given `index` in this map
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).
98123///
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`.
100131///
101132/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
102133/// 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)),
104/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:
105///
106/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
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}),
134node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Global.Index),
135/// See the definition of `DsoGlobals`.
136dso_globals: DsoGlobals,
137
127138shstrtab: StringTable,
128139strtab: StringTable,
129140dynstr: StringTable,
......@@ -132,7 +143,7 @@ dynstr: StringTable,
132143///
133144/// Value is the output relocation in `.rela.dyn` for the GOT entry.
134145got: 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.
136147///
137148/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
138149/// 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),
140151///
141152/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
142153/// 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),
144155/// The `.plt` section contains zero or more symbol relocations starting at this index.
145156plt_first_symbol_reloc: SymbolReloc.Index,
146157/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.
......@@ -203,12 +214,13 @@ got_relocs: std.ArrayList(GotReloc),
203214tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
204215/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
205216section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
206/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
217/// Key is a global symbol which has been moved to a new index in the symbol table. Any relocation
207218/// entries which target that symbol must be updated to reference the correct symbol index.
208219///
209/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it
210/// refers to the index in `.dynsym`.
211changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
220/// * In an `ET_REL`, this means the index in `.symtab` has changed.
221/// * In a DSO (or static PIE), this means the index in `.dynsym` has changed.
222/// * Otherwise this is always empty.
223changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
212224/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
213225/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
214226/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
......@@ -271,14 +283,14 @@ const Node = union(enum) {
271283 section_manual_size: Section.Index,
272284 /// May contain relocations.
273285 input_section: InputSection.Index,
274 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
275 /// which we have emitted a copy relocation.
286 /// Value is a global which has an entry in `elf.copied_globals`, so, a global for which we have
287 /// emitted a copy relocation.
276288 ///
277289 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
278290 ///
279291 /// 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`.
281 copied_global: String(.strtab),
292 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `updateGlobalDynamic`.
293 copied_global: Symbol.Global.Index,
282294 /// May contain relocations.
283295 nav: NavMapIndex,
284296 /// May contain relocations.
......@@ -1726,9 +1738,7 @@ const SymbolReloc = struct {
17261738 break :type reloc.target.value(elf) +% addend -% tls_size;
17271739 },
17281740 },
1729 .size => switch (elf.symPtr(reloc.target.index(elf))) {
1730 inline else => |sym| elf.targetLoad(&sym.size),
1731 },
1741 .size => reloc.target.size(elf),
17321742 .special => return reloc.type.action.special.applyInner(
17331743 elf,
17341744 reloc.target,
......@@ -2179,53 +2189,152 @@ const GotReloc = struct {
21792189 }
21802190};
21812191
2182fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {
2183 const gpa = elf.base.comp.gpa;
2192/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
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 adding
2188 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals
2189 // around to keep `.dynsym` compact. Either way, the maximum is N.
2190 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);
2220 /// Contains all default-version global symbols defined in any needed DSO.
2221 ///
2222 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
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 {
2193 // Ensure the symtab section's node is big enough
2194 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {
2195 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
2227 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2228 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2229 const String = enum(u32) {
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 }
21962247 };
2197 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_node_size);
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);
2248 };
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) {
2211 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2212 inline else => |shdr, class| .{
2213 elf.targetLoad(&shdr.size),
2214 @sizeOf(class.ElfN().Sym),
2215 },
2216 };
2217 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));
2271 const Adapter = struct {
2272 dso_globals: *const DsoGlobals,
2273 pub fn eql(ctx: Adapter, lhs_key: Key, rhs_symbol: DsoGlobals.Symbol, _: usize) bool {
2274 const dso_globals = ctx.dso_globals;
2275 const rhs_name = rhs_symbol.name.slice(dso_globals);
2276 if (!std.mem.eql(u8, lhs_key.name, rhs_name)) return false;
2277 if (lhs_key.version) |lhs_version| {
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;
2220 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, dynsym_need_size);
2300 const DefaultVersionAdapter = struct {
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);
2225 }
2226 },
2326 fn findDefaultVersion(dso_globals: *const DsoGlobals, name: []const u8) ?*const DsoGlobals.Symbol {
2327 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
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 }
22272336 }
2228}
2337};
22292338
22302339fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
22312340 const gpa = elf.base.comp.gpa;
......@@ -2283,10 +2392,10 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
22832392 chains[0] = 0;
22842393 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
22852394 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)) {
22872396 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
22882397 };
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;
22902399 // Make this symbol the head of that bucket, and chain to the old head.
22912400 chain.* = buckets[b];
22922401 elf.targetStore(&buckets[b], dynsym_index);
......@@ -2326,10 +2435,10 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
23262435 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
23272436 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)) {
23302439 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
23312440 };
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;
23332442 // Make this symbol the head of that bucket, and chain to the old head.
23342443 chains[dynsym_index] = buckets[b];
23352444 elf.targetStore(&buckets[b], dynsym_index);
......@@ -2367,10 +2476,10 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
23672476 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
23682477 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)) {
23712480 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
23722481 };
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
23752484 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
23762485 elf.targetStore(&chains[dynsym_index], 0);
......@@ -2393,34 +2502,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
23932502 }
23942503}
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}
24242505/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
24252506/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
24262507fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
......@@ -2443,13 +2524,23 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
24432524
24442525const AddLocalSymbolOptions = struct {
24452526 node: MappedFile.Node.Index.Optional,
2446 name: String(.strtab),
2527 name: []const u8,
24472528 value: u64,
24482529 size: u64,
24492530 type: std.elf.STT,
24502531 shndx: Section.Index,
24512532};
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
24532544 switch (elf.shdrPtr(.symtab)) {
24542545 inline else => |shdr, class| {
24552546 const ent_size = @sizeOf(class.ElfN().Sym);
......@@ -2478,12 +2569,12 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
24782569 // ...then the `elf.symtab` metadata...
24792570 new_index.ptr(elf).* = target_index.ptr(elf).*;
24802571 // ...then update the `elf.globals` tracking.
2481 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));
2482 elf.globalByName(global_name).?.symtab_index = new_index;
2572 const gsi = elf.globalBySym(new_index);
2573 gsi.ptr(elf).symtab_index = new_index;
24832574
24842575 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
24852576 // 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, {});
24872578 }
24882579 }
24892580
......@@ -2493,7 +2584,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
24932584 };
24942585
24952586 target_sym.* = .{
2496 .name = @backingInt(opts.name),
2587 .name = @backingInt(name),
24972588 .value = @intCast(opts.value),
24982589 .size = @intCast(opts.size),
24992590 .info = .{ .type = opts.type, .bind = .LOCAL },
......@@ -2509,140 +2600,112 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
25092600 }
25102601}
25112602
2512const AddGlobalSymbolOptions = struct {
2513 const Name = struct {
2514 strtab: String(.strtab),
2515 dynstr: String(.dynstr),
2516 fn string(elf: *Elf, slice: []const u8) Error!Name {
2517 return .{
2518 .strtab = try elf.string(.strtab, slice),
2519 .dynstr = switch (elf.shndx.dynsym) {
2520 .UNDEF => .empty,
2521 else => try elf.string(.dynstr, slice),
2522 },
2523 };
2603fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2604 MultipleDefinitions,
2605 MultipleDefaultVersions,
2606 UndefinedDefaultVersion,
2607} || Error)!Symbol.Global.Index {
2608 _ = opts.lib_name; // TODO
2609
2610 const gpa = elf.base.comp.gpa;
2611
2612 try elf.symtab.ensureUnusedCapacity(gpa, 1);
2613 try elf.globals.ensureUnusedCapacity(gpa, 1);
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;
25242626 }
2525 };
2627 }
25262628
2527 node: MappedFile.Node.Index.Optional,
2528 name: Name,
2529 lib_name: ?[]const u8 = null,
2530 value: u64,
2531 size: u64,
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
2629 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(@as(Symbol.Global.VersionedName, .{
2630 .name = name.name,
2631 .version = name.version,
2632 }), @as(Symbol.Global.VersionedName.Adapter, .{ .elf = elf }));
2633 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));
25392634
2540 if (elf.shndx.dynsym == .UNDEF) {
2541 assert(opts.name.dynstr == .empty);
2542 } else {
2543 assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));
2544 }
2545
2546 // We break from this `switch` only if this symbol name did not previously exist at all and so
2547 // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need
2548 // a new symtab entry.
2549 const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {
2550 .strong => new_global: {
2551 const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);
2552 if (gop.found_existing) return error.MultipleDefinitions;
2553 const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse
2554 elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2555 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2556 // The symbol did not already exist, so we'll use the "new global" path.
2557 break :new_global gop.value_ptr;
2558 };
2559 gop.value_ptr.* = old_kv.value;
2560 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2561 .node = opts.node,
2562 .value = opts.value,
2563 .size = opts.size,
2564 .type = opts.type,
2565 .shndx = opts.shndx,
2566 });
2567 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2568 return .global(opts.name.strtab);
2569 },
2570 .weak => new_global: {
2571 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2572 // The existing definition holds, we just merge our visibility in.
2573 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2574 return .global(opts.name.strtab);
2575 }
2576 const gop = elf.globals.weak_def.getOrPutAssumeCapacity(opts.name.strtab);
2577 if (gop.found_existing) {
2578 // The existing definition holds, we just merge our visibility in.
2579 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2580 return .global(opts.name.strtab);
2581 }
2582 const old_kv = elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2583 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2584 // The symbol did not already exist, so we'll use the "new global" path.
2585 break :new_global gop.value_ptr;
2586 };
2587 gop.value_ptr.* = old_kv.value;
2588 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2635 if (gop.found_existing) {
2636 var force_apply_relocs: bool = false;
2637
2638 if (name.version != null and name.is_default_version) {
2639 const default_gop = try elf.default_version_globals.getOrPutAdapted(
2640 elf.base.comp.gpa,
2641 name.name,
2642 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2643 );
2644 if (!default_gop.found_existing) {
2645 // This symbol is becoming a default version.
2646 default_gop.key_ptr.* = gsi;
2647 const new_strtab_name = try elf.string(.strtab, opts.name);
2648 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2649 inline else => |sym| elf.targetStore(&sym.name, @backingInt(new_strtab_name)),
2650 }
2651 force_apply_relocs = true;
2652 } else if (default_gop.key_ptr.* != gsi) {
2653 return error.MultipleDefaultVersions;
2654 }
2655 }
2656
2657 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {
2658 force_apply_relocs = true;
2659 }
2660
2661 // There's already a symbol with this name, we just need to potentially set its value, and
2662 // to update its bind and visibility.
2663 if (opts.shndx == .UNDEF) {
2664 // We are not defining the symbol, so we won't set its value and we should only
2665 // update its bind if it is undefined and we won't weaken it.
2666 if (!gsi.defined(elf) and
2667 gsi.ptr(elf).status.bind == .weak and
2668 opts.bind == .strong)
2669 {
2670 elf.setGlobalSymbolBind(gsi, .strong);
2671 }
2672 } else if (!gsi.defined(elf)) {
2673 elf.setGlobalSymbolBind(gsi, opts.bind);
2674 try elf.setGlobalSymbolValue(gsi, .{
25892675 .node = opts.node,
25902676 .value = opts.value,
25912677 .size = opts.size,
25922678 .type = opts.type,
25932679 .shndx = opts.shndx,
25942680 });
2595 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2596 return .global(opts.name.strtab);
2597 },
2598 } else switch (opts.bind) {
2599 .strong => new_global: {
2600 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2601 // The existing definition holds, we just merge our visibility in.
2602 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2603 return .global(opts.name.strtab);
2604 }
2605 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2606 // The existing definition holds, we just merge our visibility in.
2607 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2608 return .global(opts.name.strtab);
2609 }
2610 const gop = elf.globals.strong_undef.getOrPutAssumeCapacity(opts.name.strtab);
2611 if (gop.found_existing) {
2612 // The existing symbol is okay, we just merge our visibility in.
2613 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2614 return .global(opts.name.strtab);
2615 }
2616 const old_kv = elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2617 // The symbol did not already exist, so we'll use the "new global" path.
2618 break :new_global gop.value_ptr;
2619 };
2620 gop.value_ptr.* = old_kv.value;
2621 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2622 return .global(opts.name.strtab);
2623 },
2624 .weak => new_global: {
2625 if (elf.globals.strong_def.getPtr(opts.name.strtab) orelse
2626 elf.globals.strong_undef.getPtr(opts.name.strtab)) |global|
2627 {
2628 // The existing symbol is okay, we just merge our visibility in.
2629 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2630 return .global(opts.name.strtab);
2631 }
2632 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2633 // The existing symbol is okay, we just merge our visibility in.
2634 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2635 return .global(opts.name.strtab);
2636 }
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 };
2681 force_apply_relocs = true;
2682 } else switch (opts.bind) {
2683 .strong => switch (gsi.ptr(elf).status.bind) {
2684 .strong => return error.MultipleDefinitions,
2685 .weak => {
2686 elf.setGlobalSymbolBind(gsi, .strong);
2687 try elf.setGlobalSymbolValue(gsi, .{
2688 .node = opts.node,
2689 .value = opts.value,
2690 .size = opts.size,
2691 .type = opts.type,
2692 .shndx = opts.shndx,
2693 });
2694 force_apply_relocs = true;
2695 },
2696 },
2697 .weak => {},
2698 }
2699
2700 try elf.updateGlobalDynamic(gsi, force_apply_relocs);
2701
2702 return gsi;
2703 }
2704
2705 assert(elf.globals.items.len == @backingInt(gsi));
2706 assert(elf.globals.addOneAssumeCapacity() == gsi.ptr(elf)); // populated later
2707
2708 const strtab_name = try elf.string(.strtab, opts.name);
26462709
26472710 const force_local_bind: bool = switch (opts.visibility) {
26482711 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
......@@ -2656,13 +2719,6 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
26562719 .weak => .WEAK,
26572720 };
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
26662722 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
26672723 elf.symtab.appendAssumeCapacity(.{
26682724 .node = opts.node,
......@@ -2678,10 +2734,10 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
26782734 // ...then populate the newly-valid symbol pointer
26792735 const sym = @field(elf.symPtr(sym_index), @tagName(class));
26802736 sym.* = .{
2681 .name = @backingInt(opts.name.strtab),
2737 .name = @backingInt(strtab_name),
26822738 .value = @intCast(opts.value),
26832739 .size = @intCast(opts.size),
2684 .info = .{ .type = @"type", .bind = bind },
2740 .info = .{ .type = opts.type, .bind = bind },
26852741 .other = .{ .visibility = opts.visibility },
26862742 .shndx = opts.shndx.toSection().?,
26872743 };
......@@ -2691,264 +2747,640 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
26912747 },
26922748 }
26932749
2694 const old_head: String(.strtab) = old_head: {
2695 const node = opts.node.unwrap() orelse break :old_head .empty;
2696 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2697 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2698 gop.value_ptr.* = opts.name.strtab;
2750 const old_head: Symbol.Global.Index.Optional = old_head: {
2751 const node = opts.node.unwrap() orelse break :old_head .none;
2752 const node_globals_gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2753 const old_head: Symbol.Global.Index.Optional = if (node_globals_gop.found_existing)
2754 .wrap(node_globals_gop.value_ptr.*)
2755 else
2756 .none;
2757 node_globals_gop.value_ptr.* = gsi;
26992758 break :old_head old_head;
27002759 };
27012760
2702 new_global_ptr.* = .{
2703 .symtab_index = sym_index,
2704 .dynsym_index = dynsym_index: {
2705 if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;
2706 if (force_local_bind) break :dynsym_index 0;
2707 switch (elf.shdrPtr(elf.shndx.dynsym)) {
2708 inline else => |shdr, class| {
2709 const Sym = class.ElfN().Sym;
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 }
2761 gsi.ptr(elf).* = .{
2762 .status = .{
2763 .bind = opts.bind,
2764 .want_static_value = false,
2765 .owns_dynsym = false,
2766 .any_static_target_relocs = false,
2767 .any_static_relative_target_relocs = false,
2768 .any_dynamic_target_relocs = false,
27312769 },
2732 .prev_in_node = .empty,
2770 .dynsym_index = 0,
2771 .symtab_index = sym_index,
2772 .prev_in_node = .none,
27332773 .next_in_node = old_head,
27342774 };
27352775
2736 if (old_head != .empty) {
2737 const old_head_ptr = elf.globalByName(old_head).?;
2776 if (old_head.unwrap()) |old_head_gsi| {
2777 const old_head_ptr = old_head_gsi.ptr(elf);
27382778 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
2739 assert(old_head_ptr.prev_in_node == .empty);
2740 old_head_ptr.prev_in_node = opts.name.strtab;
2779 assert(old_head_ptr.prev_in_node == .none);
2780 old_head_ptr.prev_in_node = .wrap(gsi);
27412781 }
27422782
27432783 if (force_local_bind) {
2744 elf.moveDemotedGlobal(new_global_ptr);
2784 elf.moveDemotedGlobal(gsi);
27452785 }
27462786
2747 switch (@"type") {
2748 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2749 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
2750 {
2751 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
2752 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
2753 },
2754 else => {},
2787 if (name.version != null and name.is_default_version) {
2788 const default_gop = try elf.default_version_globals.getOrPutAdapted(
2789 elf.base.comp.gpa,
2790 name.name,
2791 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2792 );
2793 if (!default_gop.found_existing) {
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 }
27552799 }
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;
27582808}
2759fn setGlobalSymbolValue(
2760 elf: *Elf,
2761 global_name: String(.strtab),
2762 global_ptr: *Symbol.Global,
2763 new: struct {
2764 node: MappedFile.Node.Index.Optional,
2765 value: u64,
2766 size: u64,
2767 type: std.elf.STT,
2768 shndx: Section.Index,
2769 },
2770) void {
2771 assert(new.shndx != .UNDEF);
2772 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2773 if (global_ptr.next_in_node != .empty) {
2774 const next = elf.globalByName(global_ptr.next_in_node).?;
2775 assert(next.prev_in_node == global_name);
2776 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);
2777 next.prev_in_node = global_ptr.prev_in_node;
2778 }
2779 if (global_ptr.prev_in_node != .empty) {
2780 const prev = elf.globalByName(global_ptr.prev_in_node).?;
2781 assert(prev.next_in_node == global_name);
2782 assert(prev.symtab_index.ptr(elf).node.unwrap().? == old_node);
2783 prev.next_in_node = global_ptr.next_in_node;
2809
2810/// Updates the dynsym index associated with the given global symbol; populates that dynsym entry if
2811/// necessary; removes unnecessary dynamic relocations; creates/removes PLT entries as needed; and
2812/// creates/removes copy relocations as needed. Put simply, updates all state related to dynamic
2813/// linking for the given symbol.
2814///
2815/// As well as on creation, this function should be called whenever any of the following properties
2816/// of a global symbol changes:
2817///
2818/// * visibility
2819/// * type
2820/// * whether a copy relocation is required
2821/// * whether a PLT entry is required
2822/// * whether it is defined
2823/// * whether there is a matching symbol in `dso_globals`
2824/// * for versioned symbols, whether it is the default symbol version
2825/// * for unversioned symbols, this symbol's default version in `dso_globals`
2826///
2827/// This function may re-apply relocations targeting the global. If the caller has itself performed
2828/// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as
2829/// `true` to guarantee this function to do so.
2830fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void {
2831 const gpa = elf.base.comp.gpa;
2832
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, {});
27842850 } else {
2785 // We're the start of the linked list, so we need to change the head.
2786 if (global_ptr.next_in_node == .empty) {
2787 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);
2788 } else {
2789 elf.node_global_symbols.getPtr(old_node).?.* = global_ptr.next_in_node;
2790 }
2851 _ = elf.defined_alias_globals.swapRemove(alias_gsi);
2852 }
2853
2854 _ = elf.unknown_globals.swapRemove(alias_gsi);
2855 if (try elf.deleteOwnedDynsym(alias_gsi)) {
2856 apply_relocs = true;
27912857 }
2792 } else {
2793 assert(global_ptr.next_in_node == .empty);
2794 assert(global_ptr.prev_in_node == .empty);
27952858 }
27962859
2797 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {
2798 // This is a quite rare case: there was a definition for this symbol in a shared library
2799 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
2800 // definition which replaces it. We know that our definition cannot be preempted because we
2801 // are the executable (only executables can have copy relocations!), so we definitely do not
2802 // need the copy relocation.
2860 const maybe_dso_global: ?*const DsoGlobals.Symbol = dso_global: {
2861 if (gsi.defined(elf)) {
2862 break :dso_global null;
2863 }
2864 if (elf.dso_globals.find(gsi.name(elf), gsi.version(elf))) |dso_global| {
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), and
2805 // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations
2806 // targeting this symbol, but we were going to do that at the end of this function anyway.
2807 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2808 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
2809 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
2810 } else {
2811 _ = elf.want_copied_globals.swapRemove(global_name);
2812 }
2875 const visibility: std.elf.STV, const sym_type: std.elf.STT = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2876 inline else => |sym| info: {
2877 // If the symbol is undefined with type `STT_NOTYPE`, but there is a type available in
2878 // an input DSO, then use that symbol type!
2879 if (maybe_dso_global) |dso_global| {
2880 if (elf.targetLoad(&sym.shndx) == std.elf.SHN_UNDEF and
2881 elf.targetLoad(&sym.info).type == .NOTYPE and
2882 dso_global.type != .NOTYPE)
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: {
2817 const new_node = new.node.unwrap() orelse break :old_head .empty;
2818 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2819 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2820 gop.value_ptr.* = global_name;
2821 break :old_head old_head;
2898 const omit_dynsym: bool = omit: {
2899 if (elf.shndx.dynsym == .UNDEF) break :omit true;
2900 break :omit switch (visibility) {
2901 .HIDDEN, .INTERNAL => true,
2902 .DEFAULT, .PROTECTED => false,
2903 };
28222904 };
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;
2825 global_ptr.next_in_node = old_head;
2920 if (!gsi.defined(elf) and
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) {
2828 const old_head_ptr = elf.globalByName(old_head).?;
2829 assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);
2830 assert(old_head_ptr.prev_in_node == .empty);
2831 old_head_ptr.prev_in_node = global_name;
2832 }
2929 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
2930 gsi.deleteDynamicTargetRelocs(elf);
2931 if (maybe_alias_gsi) |alias_gsi| {
2932 alias_gsi.deleteDynamicTargetRelocs(elf);
2933 }
28332934
2834 // Now for the easy bit where we actually update the symtab entry.
2835 switch (elf.symPtr(global_ptr.symtab_index)) {
2836 inline else => |sym| {
2837 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2838 elf.targetStore(&sym.size, @intCast(new.size));
2839 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2840 const old_bind = elf.targetLoad(&sym.info).bind;
2841 elf.targetStore(&sym.info, .{
2842 .type = new.type,
2843 .bind = old_bind,
2844 });
2845 },
2935 if (apply_relocs) {
2936 Symbol.Id.global(gsi).applyTargetRelocs(elf);
2937 if (maybe_alias_gsi) |alias_gsi| {
2938 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
2939 }
2940 }
2941 return;
28462942 }
28472943
2848 // ...and also the dynsym entry if there is one.
2849 if (global_ptr.dynsym_index != 0) switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
2850 inline else => |sym| {
2851 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2852 elf.targetStore(&sym.size, @intCast(new.size));
2853 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2854 const old_bind = elf.targetLoad(&sym.info).bind;
2855 elf.targetStore(&sym.info, .{
2856 .type = new.type,
2857 .bind = old_bind,
2858 });
2859 },
2944 // We need an owned dynsym entry. We can either reuse an existing one or create a new one.
2945 const new_dynsym_index: u32 = owned_dynsym_index: {
2946 if (gsi.ptr(elf).status.owns_dynsym) {
2947 break :owned_dynsym_index gsi.ptr(elf).dynsym_index;
2948 }
2949
2950 // We're going to add a new entry to dynsym; ensure there's space in the relevant sections.
2951 const dynsym_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2952 inline else => |shdr, class| .{
2953 elf.targetLoad(&shdr.size),
2954 @sizeOf(class.ElfN().Sym),
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;
28602998 };
28612999
2862 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
2863 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
2864 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
2865 if (elf.plt.getIndex(global_name)) |plt_index| {
2866 if (!elf.pltEntryIsDead(plt_index) and
2867 elf.classifySymbolValue(.global(global_name)) != .dynamic)
2868 {
2869 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
2870 assert(elf.pltEntryIsDead(plt_index));
3000 gsi.ptr(elf).status.owns_dynsym = true;
3001
3002 if (gsi.ptr(elf).dynsym_index != new_dynsym_index) {
3003 gsi.ptr(elf).dynsym_index = new_dynsym_index;
3004 try elf.changed_symtab_index.put(gpa, gsi, {});
3005 }
3006 if (maybe_alias_gsi) |alias_gsi| {
3007 if (alias_gsi.ptr(elf).dynsym_index != new_dynsym_index) {
3008 alias_gsi.ptr(elf).dynsym_index = new_dynsym_index;
3009 try elf.changed_symtab_index.put(gpa, alias_gsi, {});
28713010 }
28723011 }
28733012
2874 // If this symbol was previously undefined, relocations targeting it may have been lowered to
2875 // runtime relocations which we have now discovered we do not need, so delete those. This does
2876 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.
2877 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(global_name)) != .dynamic) {
2878 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
2879 }
3013 {
3014 const dynsym_versym: std.elf.Versym = versym: {
3015 // If there's a definition, just use the version in the symbol name.
3016 if (gsi.defined(elf)) {
3017 if (gsi.version(elf)) |orig_version_slice| {
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 we
2882 // possibly removed the PLT entry above, some relocations which were previously targeting the
2883 // PLT will now instead target the symbol itself.
2884 Symbol.Id.global(global_name).flushMoved(elf, new.value);
3030 // ...but if it's undefined, we need to consult the matching input DSO global.
3031 if (maybe_dso_global) |dso_global| {
3032 if (dso_global.version.unwrap()) |version| {
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 }
28853206}
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
28863299/// 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, which
2888/// can also affect the bind of the symbol we output.
2889fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visibility: std.elf.STV, bind: enum { strong, weak }) void {
2890 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
3300/// undefined---their visibility values are combined to determine the resulting visibility.
3301///
3302/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes
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)) {
28913313 inline else => |sym| elf.targetLoad(&sym.other).visibility,
28923314 };
2893 // The combined visibility is essentially the "strictest" of the two, with most strict being
2894 // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2895 const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {
2896 .INTERNAL => .{ .INTERNAL, false },
3315
3316 // The new visibility is basically whichever of the old and new is "stricter", with the most
3317 // strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
3318 const new_visibility: std.elf.STV = switch (old_visibility) {
3319 .INTERNAL => .INTERNAL,
28973320 .HIDDEN => switch (other_visibility) {
2898 .INTERNAL => .{ .INTERNAL, false },
2899 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },
3321 .INTERNAL => .INTERNAL,
3322 .HIDDEN, .PROTECTED, .DEFAULT => .HIDDEN,
29003323 },
29013324 .PROTECTED => switch (other_visibility) {
2902 .INTERNAL => .{ .INTERNAL, true },
2903 .HIDDEN => .{ .HIDDEN, true },
2904 .PROTECTED, .DEFAULT => .{ .PROTECTED, false },
3325 .INTERNAL => .INTERNAL,
3326 .HIDDEN => .HIDDEN,
3327 .PROTECTED, .DEFAULT => .PROTECTED,
29053328 },
29063329 .DEFAULT => switch (other_visibility) {
2907 .INTERNAL => .{ .INTERNAL, true },
2908 .HIDDEN => .{ .HIDDEN, true },
2909 .PROTECTED => .{ .PROTECTED, false },
2910 .DEFAULT => .{ .DEFAULT, false },
3330 .INTERNAL => .INTERNAL,
3331 .HIDDEN => .HIDDEN,
3332 .PROTECTED => .PROTECTED,
3333 .DEFAULT => .DEFAULT,
29113334 },
29123335 };
2913 // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared
2914 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
2915 // putting the global in this state for the first time---let's call it "demoting" the global to
2916 // STB_LOCAL---we need to update its bind in the symtab.
2917 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;
2918 switch (elf.symPtr(global_ptr.symtab_index)) {
2919 inline else => |sym, class| {
2920 const old_info = elf.targetLoad(&sym.info);
2921 const new_info: class.ElfN().Sym.Info = .{
2922 .type = old_info.type,
2923 .bind = if (demote_to_local) b: {
2924 assert(old_info.bind != .LOCAL);
2925 break :b .LOCAL;
2926 } else if (old_info.bind == .LOCAL) .LOCAL else switch (bind) {
2927 .strong => .GLOBAL,
2928 .weak => .WEAK,
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 },
3336 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3337 inline else => |sym| elf.targetStore(
3338 &sym.other,
3339 .{ .visibility = new_visibility },
3340 ),
29403341 }
2941 if (demote_to_local) {
2942 // When demoting a global to STB_LOCAL, we need to move its symtab index so that it is with
2943 // the STB_LOCAL symbols instead of the global symbols.
2944 elf.moveDemotedGlobal(global_ptr);
3342
3343 if (elf.ehdrType() == .REL) {
3344 return false;
29453345 }
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;
29463377}
29473378/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
29483379/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
29493380/// 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 {
29513382 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
3383 const global_ptr = gsi.ptr(elf);
29523384 switch (elf.shdrPtr(.symtab)) {
29533385 inline else => |shdr, class| {
29543386 // `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 {
29693401 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
29703402 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));
2973 assert(elf.globalByName(this_name).? == global_ptr);
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);
3404 const other_gsi = elf.globalBySym(dest_index);
3405 assert(other_gsi.ptr(elf).symtab_index == dest_index);
29783406
29793407 // First swap the symtab entries...
29803408 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 {
29823410 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
29833411 // ...then update the `elf.globals` tracking.
29843412 global_ptr.symtab_index = dest_index;
2985 other_global_ptr.symtab_index = src_index;
3413 other_gsi.ptr(elf).symtab_index = src_index;
29863414 }
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,
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));
3430 const gpa = elf.base.comp.gpa;
29923431
2993 const ent_size = @sizeOf(class.ElfN().Sym);
2994 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
3432 const dynsym_index = gsi.ptr(elf).dynsym_index;
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.
2997 const old_size = elf.targetLoad(&dynsym_shdr.size);
2998 const new_size = old_size - ent_size;
2999 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
3436 var apply_relocs: bool = false;
3437 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3438 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
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;
3004 global_ptr.dynsym_index = 0;
3462 elf.popDynsymHashEntry(pop_dynsym_index);
30053463
3006 if (free_dynsym_index != remove_dynsym_index) {
3007 // The demoted global wasn't the last entry, so move whatever entry we just
3008 // truncated out of dynsym into its place.
3464 _ = elf.versioned_dynsym_owners.swapRemove(dynsym_index);
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));
3013 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
3470 const moved_gsi = elf.globalByDynsym(pop_dynsym_index);
30143471
3015 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
3016 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
3017 const moved_global_ptr = elf.globalByName(moved_name).?;
3472 elf.clearDynsymHashEntry(dynsym_index);
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);
3022 moved_global_ptr.dynsym_index = free_dynsym_index;
3477 const versym = elf.versymSlice();
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 any
3027 // relocation entries targeting it.
3028 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
3482 moved_gsi.ptr(elf).dynsym_index = @intCast(dynsym_index);
3483
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);
30293488 }
30303489
3031 // Now that we've given that symbol a new home, actually decrease the section size.
3032 elf.targetStore(&dynsym_shdr.size, new_size);
3490 // Since that symbol's dynsym index has changed, we'll have to update any
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 }
30333508 }
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);
30343516 },
30353517 }
3518
3519 return apply_relocs;
30363520}
30373521
30383522const Symbol = struct {
......@@ -3047,20 +3531,336 @@ const Symbol = struct {
30473531 first_target_reloc: SymbolReloc.Index,
30483532
30493533 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
30503557 /// The current index of the symtab entry for this global symbol.
30513558 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
30563560 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
3057 ///
3058 /// If `node` is `.none`, this is `.empty`.
3059 next_in_node: String(.strtab),
3561 next_in_node: Symbol.Global.Index.Optional,
30603562 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
3061 ///
3062 /// If `node` is `.none`, this is `.empty`.
3063 prev_in_node: String(.strtab),
3563 prev_in_node: Symbol.Global.Index.Optional,
3564
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 };
30643864 };
30653865
30663866 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
......@@ -3107,12 +3907,12 @@ const Symbol = struct {
31073907 fn local(lsi: Symbol.LocalIndex) Symbol.Id {
31083908 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };
31093909 }
3110 fn global(name: String(.strtab)) Symbol.Id {
3111 return .{ .kind = .global, .raw = @intCast(@backingInt(name)) };
3910 fn global(gsi: Symbol.Global.Index) Symbol.Id {
3911 return .{ .kind = .global, .raw = @intCast(@backingInt(gsi)) };
31123912 }
31133913 fn unwrap(s: Symbol.Id) union(enum) {
31143914 local: Symbol.LocalIndex,
3115 global: String(.strtab),
3915 global: Symbol.Global.Index,
31163916 } {
31173917 return switch (s.kind) {
31183918 .local => .{ .local = @fromBackingInt(s.raw) },
......@@ -3127,10 +3927,20 @@ const Symbol = struct {
31273927 return @bitCast(s);
31283928 }
31293929
3130 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
3930 fn index(s: Symbol.Id, elf: *Elf) Symbol.Index {
31313931 return switch (s.unwrap()) {
31323932 .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)),
31343944 };
31353945 }
31363946
......@@ -3138,37 +3948,53 @@ const Symbol = struct {
31383948 /// global for which we have emitted a copy relocation, returns the virtual address of that
31393949 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
31403950 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))) {
31423958 inline else => |sym| elf.targetLoad(&sym.value),
31433959 };
31443960 }
31453961
31463962 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
3147 // Update the symbol value in `.symtab`
31483963 const sym_index = sym_id.index(elf);
31493964 switch (elf.symPtr(sym_index)) {
31503965 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
31513966 }
31523967
3153 // Update the symbol value in `.dynsym` if applicable
31543968 switch (sym_id.unwrap()) {
31553969 .local => {},
3156 .global => |name| {
3157 const g = elf.globalByName(name).?;
3158 if (g.dynsym_index != 0) {
3159 switch (elf.dynsymPtr(g.dynsym_index)) {
3160 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3161 }
3970 .global => |gsi| if (gsi.ownedDynsymIndex(elf)) |dynsym_index| {
3971 switch (elf.dynsymPtr(dynsym_index)) {
3972 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
31623973 }
31633974 },
31643975 }
31653976
3166 // Re-apply relocations targeting this symbol
3977 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 {
31673988 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 }
31693996 }
31703997
3171 // Update GOT entries targeting this symbol
31723998 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
31733999 elf.updateGotEntry(got_index);
31744000 }
......@@ -3181,81 +4007,16 @@ const Symbol = struct {
31814007 }
31824008 }
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
32504010 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
32514011 /// some point due to a call to `flushMoved`.
32524012 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| {
32544015 return node.hasMoved(&elf.mf);
32554016 }
3256 switch (s.unwrap()) {
4017 switch (resolved.unwrap()) {
32574018 .local => {},
3258 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {
4019 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {
32594020 return copied_global.node.hasMoved(&elf.mf);
32604021 },
32614022 }
......@@ -3264,12 +4025,33 @@ const Symbol = struct {
32644025 };
32654026};
32664027
3267fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
3268 if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;
3269 if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;
3270 if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;
3271 if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;
3272 return null;
4028fn globalByName(elf: *Elf, name: Symbol.Global.VersionedName) ?Symbol.Global.Index {
4029 const adapter: Symbol.Global.VersionedName.Adapter = .{ .elf = elf };
4030 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;
4031 return @fromBackingInt(@intCast(index_raw));
4032}
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;
32734055}
32744056
32754057fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
......@@ -3295,23 +4077,25 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
32954077 return .static;
32964078 }
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))) {
32994083 inline else => |sym_ptr| .{
33004084 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
33014085 elf.targetLoad(&sym_ptr.other).visibility,
33024086 },
33034087 };
33044088
3305 switch (sym.unwrap()) {
4089 switch (resolved_sym.unwrap()) {
33064090 .local => {
33074091 assert(shndx != .UNDEF);
33084092 assert(visibility == .DEFAULT);
33094093 },
3310 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
4094 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
33114095 // An unprotected symbol in a DSO which is not an executable is subject to runtime
33124096 // preemption, so a dynamic relocation is required for it even if we have a definition.
33134097 return .dynamic;
3314 } else if (elf.copied_globals.contains(name)) {
4098 } else if (elf.copied_globals.contains(gsi)) {
33154099 // This becomes a locally-defined symbol in `.data`.
33164100 return if (runtime_load_addr) .static_relative else .static;
33174101 },
......@@ -3321,7 +4105,9 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
33214105 .UNDEF => switch (visibility) {
33224106 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
33234107 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`).
33254111 return .static;
33264112 } else .dynamic, // external symbol
33274113
......@@ -3396,7 +4182,6 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.Sy
33964182fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {
33974183 const gpa = elf.base.comp.gpa;
33984184
3399 try elf.ensureUnusedSymbolCapacity(1, .all_local);
34004185 try elf.nodes.ensureUnusedCapacity(gpa, 1);
34014186 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
34174202 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch
34184203 unreachable;
34194204 gop.value_ptr.* = .{
3420 .lsi = elf.addLocalSymbolAssumeCapacity(.{
4205 .lsi = try elf.addLocalSymbol(.{
34214206 .node = .wrap(node),
3422 .name = try elf.string(.strtab, name),
4207 .name = name,
34234208 .value = 0,
34244209 .size = 0,
34254210 .type = sym_type,
......@@ -3448,10 +4233,9 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb
34484233 }).toTypeErased();
34494234}
34504235fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3451 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
3452 const symbol = elf.addGlobalSymbolAssumeCapacity(.{
4236 const gsi = elf.addGlobalSymbol(.{
34534237 .node = .none,
3454 .name = try .string(elf, opts.name),
4238 .name = opts.name,
34554239 .lib_name = opts.lib_name,
34564240 .value = 0,
34574241 .size = 0,
......@@ -3470,8 +4254,14 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
34704254 .shndx = .UNDEF,
34714255 }) catch |err| switch (err) {
34724256 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,
34734263 };
3474 return symbol;
4264 return .global(gsi);
34754265}
34764266pub fn addReloc(
34774267 elf: *Elf,
......@@ -3618,8 +4408,21 @@ fn String(section: StringSection) type {
36184408 };
36194409}
36204410fn 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 {
36214420 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));
36234426}
36244427/// Like `string`, but asserts that the string is already in `section`.
36254428fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
......@@ -3663,35 +4466,41 @@ const StringTable = struct {
36634466 return st.map.getKeyAdapted(key, adapter).?;
36644467 }
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 {
36674481 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
36684482 // case the empty string.
36694483 if (key.len == 0) return 0;
36704484
3671 const gpa = elf.base.comp.gpa;
36724485 const ni = shndx.get(elf).ni;
36734486 const slice_const = ni.sliceConst(&elf.mf);
3674 const gop = try st.map.getOrPutContextAdapted(
3675 gpa,
4487 const gop = st.map.getOrPutAssumeCapacityAdapted(
36764488 key,
36774489 StringTable.Adapter{ .slice = slice_const },
3678 .{ .slice = slice_const },
36794490 );
36804491 if (gop.found_existing) return gop.key_ptr.*;
3681 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
3682 inline else => |shdr| {
3683 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
3684 const new_size: u32 = @intCast(old_size + key.len + 1);
3685 elf.targetStore(&shdr.size, new_size);
3686 break :size .{ old_size, new_size };
4492 const offset: u32 = switch (elf.shdrPtr(shndx)) {
4493 inline else => |shdr| offset: {
4494 const old_size = elf.targetLoad(&shdr.size);
4495 elf.targetStore(&shdr.size, @intCast(old_size + key.len + 1));
4496 break :offset @intCast(old_size);
36874497 },
36884498 };
3689 try ni.ensureMinimumSize(gpa, &elf.mf, new_size);
3690 const slice = ni.slice(&elf.mf)[old_size..];
4499 const slice = ni.slice(&elf.mf)[offset..];
36914500 @memcpy(slice[0..key.len], key);
36924501 slice[key.len] = 0;
3693 gop.key_ptr.* = old_size;
3694 return old_size;
4502 gop.key_ptr.* = offset;
4503 return offset;
36954504 }
36964505};
36974506
......@@ -3730,7 +4539,7 @@ fn create(
37304539 .big => .@"2MSB",
37314540 };
37324541 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`
37344543 .freestanding, .other => .STANDALONE,
37354544 .netbsd => .NETBSD,
37364545 .illumos => .SOLARIS,
......@@ -3814,6 +4623,9 @@ fn create(
38144623 .tdata = .UNDEF,
38154624 .rela_dyn = .UNDEF,
38164625 .rela_plt = .UNDEF,
4626 .gnu_version = .UNDEF,
4627 .gnu_version_d = .UNDEF,
4628 .gnu_version_r = .UNDEF,
38174629 .debug_abbrev = .UNDEF,
38184630 .debug_addr = .UNDEF,
38194631 .eh_frame_hdr = .UNDEF,
......@@ -3836,16 +4648,25 @@ fn create(
38364648 .soname = .empty,
38374649 },
38384650 .symtab = .empty,
3839 .globals = .{
3840 .strong_def = .empty,
3841 .weak_def = .empty,
3842 .strong_undef = .empty,
3843 .weak_undef = .empty,
3844 },
4651 .globals = .empty,
4652 .globals_by_name = .empty,
4653 .default_version_globals = .empty,
4654 .unknown_globals = .empty,
4655 .defined_alias_globals = .empty,
38454656 .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,
38474664 .node_global_symbols = .empty,
3848 .dso_globals = .empty,
4665 .dso_globals = .{
4666 .string_bytes = .empty,
4667 .symbols = .empty,
4668 .default_sym_vers = .empty,
4669 },
38494670 .shstrtab = .{ .map = .empty },
38504671 .strtab = .{ .map = .empty },
38514672 .dynstr = .{ .map = .empty },
......@@ -3915,14 +4736,19 @@ pub fn deinit(elf: *Elf) void {
39154736 elf.shdrs.deinit(gpa);
39164737 elf.phdrs.deinit(gpa);
39174738 elf.symtab.deinit(gpa);
3918 elf.globals.strong_def.deinit(gpa);
3919 elf.globals.weak_def.deinit(gpa);
3920 elf.globals.strong_undef.deinit(gpa);
3921 elf.globals.weak_undef.deinit(gpa);
4739 elf.globals.deinit(gpa);
4740 elf.globals_by_name.deinit(gpa);
4741 elf.default_version_globals.deinit(gpa);
4742 elf.unknown_globals.deinit(gpa);
4743 elf.defined_alias_globals.deinit(gpa);
39224744 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);
39244748 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);
39264752 elf.shstrtab.map.deinit(gpa);
39274753 elf.strtab.map.deinit(gpa);
39284754 elf.dynstr.map.deinit(gpa);
......@@ -4015,6 +4841,9 @@ fn initHeaders(
40154841 shnum += 1; // .dynstr
40164842 shnum += 1; // .dynsym
40174843 shnum += 1; // .hash
4844 shnum += 1; // .gnu.version
4845 shnum += 1; // .gnu.version_d
4846 shnum += 1; // .gnu.version_r
40184847 shnum += 1; // .rela.dyn
40194848 shnum += 1; // .rela.plt
40204849 }
......@@ -4788,6 +5617,66 @@ fn initHeaders(
47885617 .addralign = addr_align,
47895618 .manual_size = true,
47905619 });
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
47915680 switch (elf.targetDynsymHashInfo()) {
47925681 inline else => |info| {
47935682 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
......@@ -4974,11 +5863,10 @@ fn initHeaders(
49745863 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
49755864 // 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);
49785866 // Despite the name, `__dso_handle` is necessary even in static binaries.
4979 _ = elf.addGlobalSymbolAssumeCapacity(.{
5867 _ = elf.addGlobalSymbol(.{
49805868 .node = .wrap(Section.Index.text.get(elf).ni),
4981 .name = try .string(elf, "__dso_handle"),
5869 .name = "__dso_handle",
49825870 .value = Section.Index.text.vaddr(elf),
49835871 .size = 0,
49845872 .type = .NOTYPE,
......@@ -4987,10 +5875,13 @@ fn initHeaders(
49875875 .shndx = .text,
49885876 }) catch |err| switch (err) {
49895877 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,
49905881 };
4991 _ = elf.addGlobalSymbolAssumeCapacity(.{
5882 _ = elf.addGlobalSymbol(.{
49925883 .node = .wrap(elf.shndx.plt.get(elf).ni),
4993 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),
5884 .name = "_PROCEDURE_LINKAGE_TABLE_",
49945885 .value = elf.shndx.plt.vaddr(elf),
49955886 .size = 0,
49965887 .type = .NOTYPE,
......@@ -4999,10 +5890,13 @@ fn initHeaders(
49995890 .shndx = elf.shndx.plt,
50005891 }) catch |err| switch (err) {
50015892 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,
50025896 };
5003 _ = elf.addGlobalSymbolAssumeCapacity(.{
5897 _ = elf.addGlobalSymbol(.{
50045898 .node = .wrap(elf.shndx.got.get(elf).ni),
5005 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
5899 .name = "_GLOBAL_OFFSET_TABLE_",
50065900 .value = switch (machine) {
50075901 .AARCH64,
50085902 .LOONGARCH,
......@@ -5023,10 +5917,13 @@ fn initHeaders(
50235917 .shndx = elf.shndx.got,
50245918 }) catch |err| switch (err) {
50255919 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,
50265923 };
5027 _ = elf.addGlobalSymbolAssumeCapacity(.{
5924 _ = elf.addGlobalSymbol(.{
50285925 .node = .none,
5029 .name = try .string(elf, "__init_array_start"),
5926 .name = "__init_array_start",
50305927 .value = 0,
50315928 .size = 0,
50325929 .type = .NOTYPE,
......@@ -5035,10 +5932,13 @@ fn initHeaders(
50355932 .shndx = .ABS,
50365933 }) catch |err| switch (err) {
50375934 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,
50385938 };
5039 _ = elf.addGlobalSymbolAssumeCapacity(.{
5939 _ = elf.addGlobalSymbol(.{
50405940 .node = .none,
5041 .name = try .string(elf, "__init_array_end"),
5941 .name = "__init_array_end",
50425942 .value = 0,
50435943 .size = 0,
50445944 .type = .NOTYPE,
......@@ -5047,10 +5947,13 @@ fn initHeaders(
50475947 .shndx = .ABS,
50485948 }) catch |err| switch (err) {
50495949 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,
50505953 };
5051 _ = elf.addGlobalSymbolAssumeCapacity(.{
5954 _ = elf.addGlobalSymbol(.{
50525955 .node = .none,
5053 .name = try .string(elf, "__fini_array_start"),
5956 .name = "__fini_array_start",
50545957 .value = 0,
50555958 .size = 0,
50565959 .type = .NOTYPE,
......@@ -5059,10 +5962,13 @@ fn initHeaders(
50595962 .shndx = .ABS,
50605963 }) catch |err| switch (err) {
50615964 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,
50625968 };
5063 _ = elf.addGlobalSymbolAssumeCapacity(.{
5969 _ = elf.addGlobalSymbol(.{
50645970 .node = .none,
5065 .name = try .string(elf, "__fini_array_end"),
5971 .name = "__fini_array_end",
50665972 .value = 0,
50675973 .size = 0,
50685974 .type = .NOTYPE,
......@@ -5071,10 +5977,13 @@ fn initHeaders(
50715977 .shndx = .ABS,
50725978 }) catch |err| switch (err) {
50735979 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,
50745983 };
5075 _ = elf.addGlobalSymbolAssumeCapacity(.{
5984 _ = elf.addGlobalSymbol(.{
50765985 .node = .none,
5077 .name = try .string(elf, "__preinit_array_start"),
5986 .name = "__preinit_array_start",
50785987 .value = 0,
50795988 .size = 0,
50805989 .type = .NOTYPE,
......@@ -5083,10 +5992,13 @@ fn initHeaders(
50835992 .shndx = .ABS,
50845993 }) catch |err| switch (err) {
50855994 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,
50865998 };
5087 _ = elf.addGlobalSymbolAssumeCapacity(.{
5999 _ = elf.addGlobalSymbol(.{
50886000 .node = .none,
5089 .name = try .string(elf, "__preinit_array_end"),
6001 .name = "__preinit_array_end",
50906002 .value = 0,
50916003 .size = 0,
50926004 .type = .NOTYPE,
......@@ -5095,11 +6007,14 @@ fn initHeaders(
50956007 .shndx = .ABS,
50966008 }) catch |err| switch (err) {
50976009 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,
50986013 };
50996014 if (have_dynamic) {
5100 _ = elf.addGlobalSymbolAssumeCapacity(.{
6015 _ = elf.addGlobalSymbol(.{
51016016 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
5102 .name = try .string(elf, "_DYNAMIC"),
6017 .name = "_DYNAMIC",
51036018 .value = elf.shndx.dynamic.vaddr(elf),
51046019 .size = 0,
51056020 .type = .NOTYPE,
......@@ -5108,6 +6023,9 @@ fn initHeaders(
51086023 .shndx = elf.shndx.dynamic,
51096024 }) catch |err| switch (err) {
51106025 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,
51116029 };
51126030 }
51136031 } else {
......@@ -5898,6 +6816,39 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
58986816 }
58996817}
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
59016852fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
59026853 const comp = elf.base.comp;
59036854 const any_non_single_threaded = comp.config.any_non_single_threaded;
......@@ -5945,8 +6896,8 @@ fn mapInputSection(elf: *Elf, opts: struct {
59456896 .EXEC, .DYN => name: {
59466897 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
59476898 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";
5948 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
59496899 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";
59506901 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
59516902 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
59526903 // TODO: actually generate a bss section!
......@@ -6019,7 +6970,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
60196970 const ip = &zcu.intern_pool;
60206971 const nav = ip.getNav(nav_index);
60216972
6022 try elf.ensureUnusedSymbolCapacity(1, .all_local);
60236973 try elf.nodes.ensureUnusedCapacity(gpa, 1);
60246974 try elf.navs.ensureUnusedCapacity(gpa, 1);
60256975
......@@ -6083,9 +7033,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
60837033 .alignment = alignment,
60847034 }), .{ .nav = nmi });
60857035 nav_gop.value_ptr.* = .{
6086 .lsi = elf.addLocalSymbolAssumeCapacity(.{
7036 .lsi = try elf.addLocalSymbol(.{
60877037 .node = .wrap(node),
6088 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
7038 .name = nav.fqn.toSlice(ip),
60897039 .value = 0,
60907040 .size = 0,
60917041 .type = elf.navType(nav.resolved.?),
......@@ -6106,7 +7056,6 @@ fn uavMapIndex(
61067056 const gpa = elf.base.comp.gpa;
61077057 const zcu = elf.base.comp.zcu.?;
61087058
6109 try elf.ensureUnusedSymbolCapacity(1, .all_local);
61107059 try elf.nodes.ensureUnusedCapacity(gpa, 1);
61117060 try elf.uavs.ensureUnusedCapacity(gpa, 1);
61127061 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
......@@ -6129,9 +7078,9 @@ fn uavMapIndex(
61297078 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
61307079 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
61317080 uav_gop.value_ptr.* = .{
6132 .lsi = elf.addLocalSymbolAssumeCapacity(.{
7081 .lsi = try elf.addLocalSymbol(.{
61337082 .node = .wrap(node),
6134 .name = try elf.string(.strtab, name),
7083 .name = name,
61357084 .value = 0,
61367085 .size = 0,
61377086 .type = .OBJECT,
......@@ -6410,10 +7359,9 @@ fn loadObject(
64107359 }
64117360
64127361 elf.input_pending_index += 1;
6413 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6414 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
7362 input.extra = .{ .file_symbol = try elf.addLocalSymbol(.{
64157363 .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),
64177365 .value = 0,
64187366 .size = 0,
64197367 .type = .FILE,
......@@ -6641,7 +7589,6 @@ fn loadObject(
66417589 ), 1) catch continue;
66427590 symmap.clearRetainingCapacity();
66437591 try symmap.resize(gpa, symnum);
6644 try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
66457592 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
66467593 for (symmap.items) |*si| {
66477594 si.* = .null;
......@@ -6665,9 +7612,9 @@ fn loadObject(
66657612 ),
66667613 .LOCAL => continue,
66677614 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6668 si.* = elf.addGlobalSymbolAssumeCapacity(.{
7615 si.* = .global(elf.addGlobalSymbol(.{
66697616 .node = .none,
6670 .name = try .string(elf, name),
7617 .name = name,
66717618 .value = input_sym.value,
66727619 .size = input_sym.size,
66737620 .type = sym_type,
......@@ -6680,7 +7627,14 @@ fn loadObject(
66807627 .shndx = .UNDEF,
66817628 }) catch |err| switch (err) {
66827629 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 });
66847638 continue;
66857639 },
66867640 };
......@@ -6694,9 +7648,9 @@ fn loadObject(
66947648 .{ name, bind },
66957649 ),
66967650 .LOCAL => {
6697 const lsi = elf.addLocalSymbolAssumeCapacity(.{
7651 const lsi = try elf.addLocalSymbol(.{
66987652 .node = .wrap(input_section_node),
6699 .name = try elf.string(.strtab, name),
7653 .name = name,
67007654 .value = input_sym.value,
67017655 .size = input_sym.size,
67027656 .type = sym_type,
......@@ -6705,9 +7659,9 @@ fn loadObject(
67057659 si.* = .local(lsi);
67067660 },
67077661 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6708 si.* = elf.addGlobalSymbolAssumeCapacity(.{
7662 si.* = .global(elf.addGlobalSymbol(.{
67097663 .node = .wrap(input_section_node),
6710 .name = try .string(elf, name),
7664 .name = name,
67117665 .value = input_sym.value,
67127666 .size = input_sym.size,
67137667 .type = sym_type,
......@@ -6724,7 +7678,14 @@ fn loadObject(
67247678 "multiple definitions of '{s}'",
67257679 .{name},
67267680 ),
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 });
67287689 },
67297690 }
67307691 }
......@@ -6877,73 +7838,199 @@ fn loadDso(
68777838 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
68787839 if (ehdr.machine != elf.ehdrMachine().toElf())
68797840 return diags.failParse(path, "bad machine", .{});
6880 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
68817841 // We're going to need to know the alignment of every section later.
68827842 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
68837843 defer gpa.free(section_aligns);
6884 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
6885 var dynamic_sh: ?ElfN.Shdr = null;
6886 var dynsym_sh: ?ElfN.Shdr = null;
6887 for (section_aligns) |*section_align| {
7844 const shdr: struct {
7845 dynamic: ElfN.Shdr,
7846 dynsym: ElfN.Shdr,
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| {
68887861 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
68897862 try r.discardAll(ehdr.shentsize);
68907863 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
68917864 usize,
68927865 @intCast(@max(sh.addralign, 1)),
68937866 ));
6894 switch (sh.type) {
6895 else => {},
6896 .DYNAMIC => dynamic_sh = sh,
6897 .DYNSYM => dynsym_sh = sh,
7867 const ptr: *?InputSh = switch (sh.type) {
7868 else => continue,
7869 .DYNAMIC => &opt_dynamic,
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});
68987876 }
7877 ptr.* = .{ .shndx = @intCast(shndx), .sh = sh };
68997878 }
6900 break :sh .{
6901 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),
6902 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),
7879
7880 const dynamic = opt_dynamic orelse {
7881 return diags.failParse(path, "missing SHT_DYNAMIC section", .{});
69037882 };
6904 };
6905 const dynstr_sh: ElfN.Shdr = sh: {
6906 if (dynsym_sh.link >= ehdr.shnum) {
6907 return diags.failParse(path, "bad dynamic string table section index", .{});
7883 const dynsym = opt_dynsym orelse {
7884 return diags.failParse(path, "missing SHT_DYNSYM section", .{});
7885 };
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", .{});
69087901 }
6909 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);
6910 break :sh try r.peekStruct(ElfN.Shdr, target_endian);
7902 if (opt_verdef) |verdef| if (verdef.sh.link != dynstr_shndx) {
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 };
69117916 };
69127917
6913 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {
6914 return diags.failParse(path, "bad dynamic section entsize", .{});
7918 if (shdr.dynamic.entsize != @sizeOf(ElfN.Addr) * 2) {
7919 return diags.failParse(path, "bad SHT_DYNAMIC section entsize", .{});
69157920 }
69167921 const dynnum = std.math.divExact(
69177922 u32,
6918 @intCast(dynamic_sh.size),
7923 @intCast(shdr.dynamic.size),
69197924 @sizeOf(ElfN.Addr) * 2,
69207925 ) catch return diags.failParse(
69217926 path,
6922 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
6923 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
7927 "SHT_DYNAMIC section size (0x{x}) is not a multiple of entsize (0x{x})",
7928 .{ shdr.dynamic.size, @sizeOf(ElfN.Addr) * 2 },
69247929 );
69257930
6926 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
6927 return diags.failParse(path, "bad dynsym entsize", .{});
7931 if (shdr.dynsym.entsize < @sizeOf(ElfN.Sym)) {
7932 return diags.failParse(path, "dynamic symbol table has invalid entsize", .{});
69287933 }
69297934 const symnum = std.math.divExact(
69307935 u32,
6931 @intCast(dynsym_sh.size),
6932 @intCast(dynsym_sh.entsize),
7936 @intCast(shdr.dynsym.size),
7937 @intCast(shdr.dynsym.entsize),
69337938 ) catch return diags.failParse(
69347939 path,
6935 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",
6936 .{ dynsym_sh.size, dynsym_sh.entsize },
7940 "dynamic symbol table size (0x{x}) is not a multiple of entsize (0x{x})",
7941 .{ shdr.dynsym.size, shdr.dynsym.entsize },
69377942 );
69387943
6939 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));
7944 const dynstr = try gpa.alloc(u8, @intCast(shdr.dynstr.size));
69407945 defer gpa.free(dynstr);
6941 try fr.seekTo(dynstr_sh.offset);
7946 try fr.seekTo(shdr.dynstr.offset);
69427947 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
69448031 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
6945 try fr.seekTo(dynamic_sh.offset);
6946 const soname: []const u8 = for (0..dynnum) |_| {
8032 try fr.seekTo(shdr.dynamic.offset);
8033 const soname_slice: []const u8 = for (0..dynnum) |_| {
69478034 const tag = try r.takeInt(ElfN.Addr, target_endian);
69488035 const val = try r.takeInt(ElfN.Addr, target_endian);
69498036 if (tag == std.elf.DT_SONAME) {
......@@ -6957,16 +8044,16 @@ fn loadDso(
69578044 .basename => std.fs.path.basename(path.sub_path),
69588045 .full_path => try path.toString(comp.arena),
69598046 };
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
69628050 // Scan the symbol table and populate `elf.dso_globals`.
6963 const first_global = @min(dynsym_sh.info, symnum);
6964 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
6965 try elf.ensureUnusedPltCapacity(symnum - first_global);
6966 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
6967 for (first_global..symnum) |_| {
8051 const first_global = @min(shdr.dynsym.info, symnum);
8052 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
8053 try fr.seekTo(shdr.dynsym.offset + first_global * shdr.dynsym.entsize);
8054 for (first_global..symnum) |in_dynsym_index| {
69688055 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
69718058 switch (sym.info.bind) {
69728059 else => continue,
......@@ -6986,96 +8073,76 @@ fn loadDso(
69868073 return diags.failParse(path, "bad symbol name string", .{});
69878074 }
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
69898086 // 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`.
69918088 const sym_align: Alignment = switch (sym.value) {
69928089 0 => section_aligns[sym.shndx],
69938090 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
69948091 };
69958092
6996 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
6997 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
8093 const name = std.mem.sliceTo(dynstr[sym.name..], 0);
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) {
7000 if (sym.size > gop.value_ptr.size or
7001 sym_align.compare(.gt, gop.value_ptr.alignment))
8102 if (gop.found_existing and gop.key_ptr.type != .NOTYPE) {
8103 if (sym.size > gop.key_ptr.size or
8104 sym_align.compare(.gt, gop.key_ptr.alignment))
70028105 {
7003 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
7004 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);
7005 if (elf.copied_globals.get(name)) |copied_global| {
7006 // We have a copy relocation for this global, but the amount of space we
7007 // reserved for it could be too small or underaligned!
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 }
8106 gop.key_ptr.size = @max(gop.key_ptr.size, sym.size);
8107 gop.key_ptr.alignment = gop.key_ptr.alignment.max(sym_align);
70238108 }
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 };
70258125 }
70268126
7027 gop.value_ptr.* = .{
7028 .type = sym.info.type,
7029 .size = sym.size,
7030 .alignment = sym_align,
7031 };
7032
7033 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
7034 // its type now.
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);
8127 if (is_default_version) {
8128 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
8129 .dso_globals = &elf.dso_globals,
8130 };
8131 const default_gop = try elf.dso_globals.default_sym_vers.getOrPutAdapted(gpa, name, adapter);
8132 if (!default_gop.found_existing) {
8133 default_gop.key_ptr.* = @intCast(gop.index);
8134 }
70458135 }
70468136
7047 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
7048 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
7049
7050 switch (elf.targetLoad(&sym_ptr.other).visibility) {
7051 .HIDDEN, .INTERNAL, .PROTECTED => continue,
7052 .DEFAULT => {},
8137 // Update any global(s) which may care about this input DSO global.
8138 if (maybe_version.unwrap()) |version| {
8139 if (elf.globalByName(.{ .name = name, .version = version.slice(&elf.dso_globals) })) |gsi| {
8140 try elf.updateGlobalDynamic(gsi, false);
8141 }
70538142 }
7054
7055 const cur_info = elf.targetLoad(&sym_ptr.info);
7056 if (cur_info.type == .NOTYPE) {
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);
8143 if (maybe_version == .none or is_default_version) {
8144 if (elf.globalByName(.{ .name = name, .version = null })) |gsi| {
8145 try elf.updateGlobalDynamic(gsi, false);
70798146 }
70808147 }
70818148 }
......@@ -7089,9 +8156,11 @@ fn loadDso(
70898156/// `error.AlreadyReported`, but if the magic number is missing or incorrect, returns
70908157/// `error.BadMagic` instead.
70918158///
8159/// If necessary, modifies our own ident to use `ELF_OSABI_GNU` instead of `ELF_OSABI_NONE`.
8160///
70928161/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.
70938162fn checkInputIdent(
7094 elf: *const Elf,
8163 elf: *Elf,
70958164 path: std.Build.Cache.Path,
70968165 r: *Io.Reader,
70978166) error{ BadMagic, EndOfStream, AlreadyReported, ReadFailed }!void {
......@@ -7106,8 +8175,9 @@ fn checkInputIdent(
71068175 }
71078176
71088177 const ident = try r.peekStructPointer(std.elf.Ident);
7109 const target: *const std.elf.Ident =
7110 @ptrCast(elf.ni.elf.sliceConst(&elf.mf)[0..@sizeOf(std.elf.Ident)]);
8178 const target: *std.elf.Ident = @ptrCast(
8179 elf.ni.elf.slice(&elf.mf)[0..@sizeOf(std.elf.Ident)],
8180 );
71118181
71128182 if (ident.class != target.class) return diags.failParse(
71138183 path,
......@@ -7124,17 +8194,24 @@ fn checkInputIdent(
71248194 "bad ELF version ({d})",
71258195 .{ident.version},
71268196 );
7127
7128 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
7129 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
7130 const expect_abiversion: u8 = abiver: {
7131 if (ident.osabi == .NONE) break :abiver 0;
7132 if (ident.osabi == target.osabi) break :abiver target.abiversion;
7133 return diags.failParse(
8197 // OSABI is a bit more complex.
8198 const expect_abiversion: u8 = switch (ident.osabi) {
8199 .NONE => 0,
8200 .GNU => abiversion: {
8201 // If we're currently emitting `ELF_OSABI_NONE` then prefer `ELF_OSABI_GNU` to signify
8202 // the GNU-specific features, but if we're already emitting a target-specific osabi then
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(
71348211 path,
71358212 "bad ELF OS/ABI ({?s})",
71368213 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
7137 );
8214 ),
71388215 };
71398216 if (ident.abiversion != expect_abiversion) return diags.failParse(
71408217 path,
......@@ -7166,25 +8243,32 @@ fn createInitFiniArraySection(
71668243 .manual_size = true,
71678244 });
71688245 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
7169 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
7170 // These symbols definitely already have strong definitions, because we added them alongside the
7171 // other linker-defined symbols, all the way back in `initHeaders`.
7172 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");
7173 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");
7174 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{
8246 // These symbols definitely already exist with strong definitions, because we added them
8247 // alongside the other linker-defined symbols, all the way back in `initHeaders`.
8248 const start_gsi = elf.globalByName(.{
8249 .name = "__" ++ name ++ "_start",
8250 .version = null,
8251 }).?;
8252 const end_gsi = elf.globalByName(.{
8253 .name = "__" ++ name ++ "_end",
8254 .version = null,
8255 }).?;
8256 try elf.setGlobalSymbolValue(start_gsi, .{
71758257 .node = .wrap(shndx.get(elf).ni),
71768258 .value = shndx.vaddr(elf),
71778259 .size = 0,
71788260 .type = .NOTYPE,
71798261 .shndx = shndx.*,
71808262 });
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, .{
71828265 .node = .wrap(shndx.get(elf).ni),
71838266 .value = shndx.vaddr(elf),
71848267 .size = 0,
71858268 .type = .NOTYPE,
71868269 .shndx = shndx.*,
71878270 });
8271 try elf.updateGlobalDynamic(end_gsi, true);
71888272}
71898273fn updateInitFiniArraySectionSize(
71908274 elf: *Elf,
......@@ -7194,8 +8278,11 @@ fn updateInitFiniArraySectionSize(
71948278 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
71958279 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
71968280 };
7197 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");
7198 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
8281 const end_sym_gsi = elf.globalByName(.{
8282 .name = "__" ++ name ++ "_end",
8283 .version = null,
8284 }).?;
8285 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);
71998286}
72008287
72018288pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
......@@ -7215,13 +8302,12 @@ fn prelinkInner(elf: *Elf) Error!void {
72158302 if (comp.config.use_llvm) break :self_hosted_codegen;
72168303
72178304 // We're using self-hosted codegen---add an input representing the Zig "object".
7218 try elf.ensureUnusedSymbolCapacity(1, .all_local);
72198305 try elf.inputs.ensureUnusedCapacity(gpa, 1);
72208306 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
72218307 defer gpa.free(zcu_name);
7222 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
8308 const zcu_file_symbol = try elf.addLocalSymbol(.{
72238309 .node = .none,
7224 .name = try elf.string(.strtab, zcu_name),
8310 .name = zcu_name,
72258311 .value = 0,
72268312 .size = 0,
72278313 .type = .FILE,
......@@ -7512,8 +8598,10 @@ fn prepareDynamic(elf: *Elf) Error!void {
75128598 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
75138599 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
75148600 @as(usize, @intFromBool(use_plt)) * 4 +
8601 @as(usize, @intFromBool(elf.verneed.count() > 0)) * 2 +
8602 @as(usize, @intFromBool(elf.verdef.count() > 0)) * 2 +
75158603 @intFromBool(comp.config.output_mode == .Exe) +
7516 @intFromBool(elf.textrel_count > 0) + 9;
8604 @intFromBool(elf.textrel_count > 0) + 10;
75178605
75188606 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
75198607
......@@ -7612,8 +8700,25 @@ fn flushDynamic(elf: *Elf) void {
76128700 };
76138701 dynamic_index += 4;
76148702 }
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].* = .{
76178722 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
76188723 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
76198724 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
......@@ -7621,10 +8726,11 @@ fn flushDynamic(elf: *Elf) void {
76218726 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
76228727 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
76238728 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
8729 .{ std.elf.DT_VERSYM, @intCast(elf.shndx.gnu_version.vaddr(elf)) },
76248730 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
76258731 .{ std.elf.DT_NULL, 0 },
76268732 };
7627 dynamic_index += 9;
8733 dynamic_index += 10;
76288734
76298735 assert(dynamic_index == dynamic_entries.len);
76308736 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 {
76618767 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
76628768 else => false,
76638769 };
7664 if (want_symbol) try elf.ensureUnusedSymbolCapacity(1, .all_local);
76658770
76668771 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
76678772 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 {
77088813 });
77098814 const addr = elf.computeNodeVAddr(ni);
77108815 elf.shdrs.appendAssumeCapacity(.{
7711 .lsi = if (want_symbol) elf.addLocalSymbolAssumeCapacity(.{
8816 .lsi = if (want_symbol) try elf.addLocalSymbol(.{
77128817 .node = ni.toOptional(),
7713 .name = .empty,
8818 .name = "",
77148819 .value = addr,
77158820 .size = 0,
77168821 .type = .SECTION,
......@@ -8187,10 +9292,27 @@ fn addSymbolRelocAssumeCapacity(
81879292 => false,
81889293 },
81899294 };
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
81919303 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 },
81939311 .static_relative => {
9312 switch (target.unwrap()) {
9313 .local => {},
9314 .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true,
9315 }
81949316 switch (@"type".target) {
81959317 // Only relocations which resolve to absolute addresses require runtime
81969318 // `R_*_RELATIVE` relocations.
......@@ -8217,13 +9339,8 @@ fn addSymbolRelocAssumeCapacity(
82179339 .addend = 0,
82189340 }).toOptional();
82199341 },
8220 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {
8221 switch (elf.classifySymbolValue(target)) {
8222 .static => continue :classify .static,
8223 .static_relative => continue :classify .static_relative,
8224 .dynamic => unreachable, // we just added a copy relocation
8225 }
8226 } else {
9342 .dynamic => {
9343 target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true;
82279344 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
82289345 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
82299346 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
......@@ -8255,7 +9372,7 @@ fn addSymbolRelocAssumeCapacity(
82559372 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
82569373 .type = dynamic_reloc_type,
82579374 .offset = node_vaddr + offset,
8258 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,
9375 .raw_sym_index = target.unwrap().global.dynsymIndex(elf).?,
82599376 .addend = addend,
82609377 }).toOptional();
82619378 },
......@@ -8498,21 +9615,25 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
84989615 const sym_value = sym_id.value(elf);
84999616 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
85009617 }
8501 break :val switch (sym_id.unwrap()) {
8502 // For global symbols, just target the right dynsym with no addend.
8503 .global => |name| .{ .reloc = .{
8504 .type = .tpOff(elf),
8505 .dynsym_index = elf.globalByName(name).?.dynsym_index,
8506 .addend = 0,
8507 } },
8508 // For local symbols, target the null symbol (index 0) so we get the offset to the
8509 // base of our TLS block, and then use `addend` to offset to the right symbol.
8510 .local => .{ .reloc = .{
8511 .type = .tpOff(elf),
8512 .dynsym_index = 0,
8513 .addend = @intCast(sym_id.value(elf)),
8514 } },
8515 };
9618 switch (sym_id.unwrap()) {
9619 .global => |gsi| if (gsi.dynsymIndex(elf)) |dynsym_index| {
9620 // When there is a dynsym entry, just target that with no addend.
9621 break :val .{ .reloc = .{
9622 .type = .tpOff(elf),
9623 .dynsym_index = dynsym_index,
9624 .addend = 0,
9625 } };
9626 },
9627 .local => {},
9628 }
9629 // Otherwise, we know the offset of the symbol into our own TLS block, so target the
9630 // null symbol (index 0) so we get the offset to the base of that block, and use the
9631 // addend to offset to the correct symbol.
9632 break :val .{ .reloc = .{
9633 .type = .tpOff(elf),
9634 .dynsym_index = 0,
9635 .addend = @intCast(sym_id.value(elf)),
9636 } };
85169637 },
85179638 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
85189639 .static => .{ .unsigned = sym.value(elf) },
......@@ -8523,7 +9644,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85239644 } },
85249645 .dynamic => .{ .reloc = .{
85259646 .type = .globDat(elf),
8526 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
9647 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
85279648 .addend = 0,
85289649 } },
85299650 },
......@@ -8532,7 +9653,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85329653 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
85339654 .dynamic => .{ .reloc = .{
85349655 .type = .dtpOff(elf),
8535 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
9656 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
85369657 .addend = 0,
85379658 } },
85389659 },
......@@ -8545,7 +9666,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
85459666 .type = .dtpMod(elf),
85469667 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
85479668 .static, .static_relative => 0,
8548 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
9669 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
85499670 },
85509671 .addend = 0,
85519672 } },
......@@ -8609,7 +9730,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
86099730
86109731/// If `node` cannot contain runtime relocations, returns `.no`.
86119732///
8612/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires
9733/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires
86139734/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
86149735fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
86159736 const shndx = elf.getNodeShndx(node);
......@@ -8621,83 +9742,6 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
86219742 return .yes;
86229743}
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
87019745pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
87029746 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
87039747 else => |e| return e,
......@@ -9232,14 +10276,18 @@ fn flushInner(
923210276
923310277 try elf.flushFiles();
923410278
9235 if (comp.config.output_mode == .Exe) {
9236 var any_undef = false;
9237 for (elf.globals.strong_undef.keys()) |name| {
9238 if (elf.dso_globals.contains(name)) continue;
9239 any_undef = true;
9240 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
10279 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {
10280 for (elf.unknown_globals.keys()) |gsi| {
10281 diags.addError("undefined global symbol '{s}'", .{gsi.rawName(elf).slice(elf)});
10282 }
10283 return error.AlreadyReported;
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)});
924110289 }
9242 if (any_undef) return error.AlreadyReported;
10290 return error.AlreadyReported;
924310291 }
924410292
924510293 try elf.prepareDynamic();
......@@ -9279,7 +10327,7 @@ fn flushInner(
927910327 elf.flushDynamic();
928010328
928110329 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) {
928310331 .default => switch (comp.config.output_mode) {
928410332 .Exe => continue :name .enabled,
928510333 .Lib, .Obj => continue :name .disabled,
......@@ -9288,9 +10336,11 @@ fn flushInner(
928810336 .enabled => "_start",
928910337 .named => |named| named,
929010338 };
9291 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);
9292 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
9293 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
10339 const gsi = elf.globalByName(.{
10340 .name = sym_name,
10341 .version = null,
10342 }) orelse break :entry 0;
10343 break :entry Symbol.Id.global(gsi).value(elf);
929410344 };
929510345 switch (elf.ehdrPtr()) {
929610346 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 {
935910409 break :task;
936010410 }
936110411 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);
936310415 defer idle_prog_node.end();
936410416
9365 const global_name = kv.key;
9366 const global = elf.globalByName(global_name).?;
9367 const sym_id: Symbol.Id = .global(global_name);
9368 const sym = global.symtab_index.ptr(elf);
10417 const sym_id: Symbol.Id = .global(gsi);
10418 const symtab_index = gsi.ptr(elf).symtab_index;
936910419
937010420 switch (elf.ehdrType()) {
937110421 .REL => {
937210422 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
937310423 // all of the output relocations.
9374 const symtab_index = @backingInt(global.symtab_index);
9375 var ri = sym.first_target_reloc;
10424 var ri = symtab_index.ptr(elf).first_target_reloc;
937610425 while (ri != .none) {
937710426 const reloc = ri.get(elf);
937810427 assert(reloc.target == sym_id);
937910428 // In relocatables, every symbol relocation has an output relocation.
938010429 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));
938210431 ri = reloc.next;
938310432 }
938410433 },
......@@ -9391,29 +10440,32 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
939110440 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
939210441 // don't care about the dynsym index). The only exception is a copy reloc
939310442 // could exist (and be the *reason* the symbol value is statically known).
9394 if (elf.copied_globals.get(global_name)) |copied| {
9395 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);
10443 if (elf.copied_globals.get(gsi)) |copied| {
10444 const dynsym_index = gsi.dynsymIndex(elf).?;
10445 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);
939610446 }
939710447 },
939810448 .dynamic => {
9399 assert(!elf.copied_globals.contains(global_name)); // value would be statically known
10449 assert(!elf.copied_globals.contains(gsi)); // value would be statically known
10450
10451 const dynsym_index = gsi.dynsymIndex(elf).?;
940010452
940110453 // Update symbol relocs:
9402 var ri = sym.first_target_reloc;
10454 var ri = symtab_index.ptr(elf).first_target_reloc;
940310455 while (ri != .none) {
940410456 const reloc = ri.get(elf);
940510457 assert(reloc.target == sym_id);
940610458 // There may or may not be a runtime relocation for this symbol reloc.
940710459 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);
940910461 }
941010462 ri = reloc.next;
941110463 }
941210464
941310465 // 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| {
941510467 // 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);
941710469 }
941810470
941910471 // Update relocs for any relevant GOT entries:
......@@ -9577,6 +10629,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
957710629 .code_view => unreachable,
957810630 }
957910631 try elf.genPendingDebug(gpa);
10632 assert(elf.pending_uavs.items.len == 0); // no UAVs added by lazy code/data or by debug info
958010633}
958110634fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
958210635 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
987210925 }
987310926
987410927 // Update global symbols targeting this section
9875 if (elf.node_global_symbols.get(ni)) |first_name| {
9876 assert(first_name != .empty);
9877 var name = first_name;
9878 while (name != .empty) {
9879 const old_sym_addr = Symbol.Id.global(name).value(elf);
9880 Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr);
9881 name = elf.globalByName(name).?.next_in_node;
10928 if (elf.node_global_symbols.get(ni)) |first_gsi| {
10929 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
10930 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
10931 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
10932 Symbol.Id.global(gsi).flushMoved(elf, old_sym_addr - old_addr + addr);
988210933 }
988310934 }
988410935
......@@ -9940,16 +10991,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
994010991 }
994110992
994210993 // Update global symbols
9943 if (elf.node_global_symbols.get(ni)) |first_name| {
9944 assert(first_name != .empty);
9945 var name = first_name;
9946 while (name != .empty) {
9947 const old_sym_addr = Symbol.Id.global(name).value(elf);
9948 Symbol.Id.global(name).flushMoved(
10994 if (elf.node_global_symbols.get(ni)) |first_gsi| {
10995 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
10996 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
10997 const old_sym_addr = Symbol.Id.global(gsi).value(elf);
10998 Symbol.Id.global(gsi).flushMoved(
994910999 elf,
995011000 old_sym_addr - old_section_addr + new_section_addr,
995111001 );
9952 name = elf.globalByName(name).?.next_in_node;
995311002 }
995411003 }
995511004
......@@ -9961,7 +11010,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
996111010 .copied_global => |global_name| {
996211011 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
996311012 // 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`.
996511014 return;
996611015 };
996711016 assert(copied_global.node == ni);
......@@ -9974,12 +11023,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
997411023 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {
997511024 const new_addr = elf.computeNodeVAddr(ni);
997611025 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
9977 if (elf.node_global_symbols.get(ni)) |first_name| {
9978 assert(first_name != .empty);
9979 var name = first_name;
9980 while (name != .empty) {
9981 Symbol.Id.global(name).flushMoved(elf, new_addr);
9982 name = elf.globalByName(name).?.next_in_node;
11026 if (elf.node_global_symbols.get(ni)) |first_gsi| {
11027 var opt_gsi: Symbol.Global.Index.Optional = .wrap(first_gsi);
11028 while (opt_gsi.unwrap()) |gsi| : (opt_gsi = gsi.ptr(elf).next_in_node) {
11029 Symbol.Id.global(gsi).flushMoved(elf, new_addr);
998311030 }
998411031 }
998511032 elf.flushMovedNodeRelocs(ni, new_addr, .{
......@@ -10712,42 +11759,78 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1071211759 }
1071311760}
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 {
1071611767 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
1071811796 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
1071911797 // free-list for the PLT itself---see `pltEntryIsDead` for details.
1072011798 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
1072111799 .type = .jumpSlot(elf),
1072211800 .offset = 0, // populated later
10723 .raw_sym_index = dynsym_index,
11801 .raw_sym_index = gsi.ownedDynsymIndex(elf).?,
1072411802 .addend = 0,
1072511803 }));
1072611804
1072711805 // 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: {
10729 const plt = elf.targetPltInfo();
10730 break :got_plt if (plt.got_plt) |got_plt| .{
10731 elf.shndx.got_plt,
10732 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
10733 } else .{
10734 elf.shndx.plt,
10735 plt.entry_size * (plt.header_entries + plt_index),
10736 };
11806 const got_plt_section: Section.Index, const got_plt_offset: u64 = if (plt.got_plt) |got_plt| .{
11807 elf.shndx.got_plt,
11808 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
11809 } else .{
11810 elf.shndx.plt,
11811 plt.entry_size * (plt.header_entries + plt_index),
1073711812 };
1073811813
1073911814 // Now that we know the index, we can set the relocation's offset.
1074011815 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
1074111816
1074211817 if (plt_index < elf.plt.count()) {
10743 // We reused a free entry, so we're already done!
10744 elf.plt.setKey(plt_index, global_name);
10745 return;
11818 // We reused a free entry, so we're already done! However, if this global has a dead PLT
11819 // entry, then `gsi` is already a key in `elf.plt`, so we'll need to swap it out for some
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;
1074611827 }
1074711828
11829 assert(maybe_dead_plt_index == null); // if there were a dead entry we would have reused it
11830
1074811831 // We added a new entry, so we now need to extend the PLT sections.
1074911832 assert(plt_index == elf.plt.count());
10750 elf.plt.putAssumeCapacityNoClobber(global_name, {});
11833 elf.plt.putAssumeCapacityNoClobber(gsi, {});
1075111834
1075211835 switch (elf.ehdrMachine()) {
1075311836 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
......@@ -10906,6 +11989,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1090611989 }
1090711990 },
1090811991 }
11992
11993 return true;
1090911994}
1091011995fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
1091111996 const target_endian = elf.targetEndian();
......@@ -10919,8 +12004,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
1091912004 // its relocations are probably going through the PLT, so we don't bother with
1092012005 // specific tracking for PLT relocations---instead just re-apply all relocations
1092112006 // targeting symbols with PLT entries.
10922 for (elf.plt.keys()) |name| {
10923 Symbol.Id.global(name).applyTargetRelocs(elf);
12007 for (elf.plt.keys()) |gsi| {
12008 Symbol.Id.global(gsi).applyTargetRelocs(elf);
1092412009 }
1092512010 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
1092612011 // 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
1097612061 // its relocations are probably going through the PLT, so we don't bother with
1097712062 // specific tracking for PLT relocations---instead just re-apply all relocations
1097812063 // targeting symbols with PLT entries.
10979 for (elf.plt.keys()) |name| {
10980 Symbol.Id.global(name).applyTargetRelocs(elf);
12064 for (elf.plt.keys()) |gsi| {
12065 Symbol.Id.global(gsi).applyTargetRelocs(elf);
1098112066 }
1098212067 // We also need to update all of the references from `.plt` to `.got.plt`.
1098312068 // 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
1104312128 // its relocations are probably going through the PLT, so we don't bother with
1104412129 // specific tracking for PLT relocations---instead just re-apply all relocations
1104512130 // targeting symbols with PLT entries.
11046 for (elf.plt.keys()) |name| {
11047 Symbol.Id.global(name).applyTargetRelocs(elf);
12131 for (elf.plt.keys()) |gsi| {
12132 Symbol.Id.global(gsi).applyTargetRelocs(elf);
1104812133 }
1104912134 // Update the offsets of the relocation entries in `.rela.plt`.
1105012135 const rela_plt_shndx = elf.shndx.rela_plt;
......@@ -11069,6 +12154,7 @@ pub fn updateExports(
1106912154 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
1107012155 };
1107112156 }
12157 try elf.genPending(pt);
1107212158}
1107312159fn updateExportInner(
1107412160 elf: *Elf,
......@@ -11087,7 +12173,6 @@ fn updateExportInner(
1108712173 Value.fromInterned(uav).fmtValue(pt),
1108812174 }),
1108912175 }
11090 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
1109112176 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
1109212177 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
1109312178 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
......@@ -11106,9 +12191,9 @@ fn updateExportInner(
1110612191 };
1110712192
1110812193 const name = @"export".opts.name.toSlice(ip);
11109 _ = elf.addGlobalSymbolAssumeCapacity(.{
12194 _ = elf.addGlobalSymbol(.{
1111012195 .node = exported_lsi.index().ptr(elf).node,
11111 .name = try .string(elf, name),
12196 .name = name,
1111212197 .value = cur_value,
1111312198 .size = cur_size,
1111412199 .type = @"type",
......@@ -11130,8 +12215,12 @@ fn updateExportInner(
1113012215 // get these errors on every non-initial incremental update. Hack around that by
1113112216 // only emitting this error if the symbol we're conflicting with comes from an input
1113212217 // section (as opposed to the ZCU).
11133 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
11134 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
12218 const parsed = parseVersionedSymbolName(name);
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| {
1113512224 if (elf.getNode(conflicting_node) == .input_section) {
1113612225 return elf.base.comp.link_diags.fail(
1113712226 "multiple definitions of '{s}'",
......@@ -11140,6 +12229,12 @@ fn updateExportInner(
1114012229 }
1114112230 }
1114212231 },
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,
1114312238 };
1114412239}
1114512240
......@@ -11204,7 +12299,7 @@ pub fn printNode(
1120412299 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
1120512300 });
1120612301 },
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)}),
1120812303 .nav => |nmi| {
1120912304 const zcu = elf.base.comp.zcu.?;
1121012305 const ip = &zcu.intern_pool;
......@@ -11392,7 +12487,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato
1139212487fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
1139312488 const index = switch (sym.unwrap()) {
1139412489 .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,
1139612491 };
1139712492 if (elf.pltEntryIsDead(index)) return null;
1139812493 const plt = elf.targetPltInfo();
......@@ -11402,3 +12497,256 @@ fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
1140212497 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
1140312498 }
1140412499}
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 {
285285 };
286286}
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
288299/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.
289300pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {
290301 return switch (ofmt) {