authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-17 21:40:42+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-19 18:30:34+01:00
log4317e1b12a084284a3cd3bf660c49cd42a627266
tree3ed9084df4f760b3d7d0a31ce19d1ff47e29e855
parentd070d85471004dfaaf4e865d64e4405116822d2a
signature Commit is signed but in an unrecognized format.

Elf2: fix dynamic symbol edge cases, add missing error reporting

...and fix our symbol binding logic to be more standard.

3 files changed, 375 insertions(+), 283 deletions(-)

src/link/Dwarf2.zig+13-24
......@@ -2101,11 +2101,9 @@ pub fn updateExtern(
21012101 dwarf: *Dwarf,
21022102 pt: Zcu.PerThread,
21032103 di_nw: *link.MappedFile.Node.Writer,
2104 arch: std.Target.Cpu.Arch,
2105 si: link.File.SymbolId,
21062104 global: InternPool.Nav.Index,
21072105) link.Error!void {
2108 dwarf.updateExternInner(pt, di_nw, arch, si, global) catch |err| switch (err) {
2106 dwarf.updateExternInner(pt, di_nw, global) catch |err| switch (err) {
21092107 else => |e| return e,
21102108 error.WriteFailed => return dwarf.reportWriteError(di_nw),
21112109 };
......@@ -2114,20 +2112,17 @@ fn updateExternInner(
21142112 dwarf: *Dwarf,
21152113 pt: Zcu.PerThread,
21162114 di_nw: *link.MappedFile.Node.Writer,
2117 arch: std.Target.Cpu.Arch,
2118 si: link.File.SymbolId,
21192115 global: InternPool.Nav.Index,
21202116) link.EmitError!void {
21212117 const zcu = pt.zcu;
21222118 const ip = &zcu.intern_pool;
21232119 const nav = ip.getNav(global);
21242120 log.debug("updateExtern({f})", .{nav.fqn.fmt(ip)});
2125 const nav_align, const nav_const, const nav_threadlocal, const nav_extern = nav_resolved: {
2121 const nav_align, const nav_const, const nav_extern = nav_resolved: {
21262122 const nav_resolved = nav.resolved.?;
21272123 break :nav_resolved .{
21282124 nav_resolved.@"align",
21292125 nav_resolved.@"const",
2130 nav_resolved.@"threadlocal",
21312126 ip.indexToKey(nav_resolved.value).@"extern",
21322127 };
21332128 };
......@@ -2162,7 +2157,7 @@ fn updateExternInner(
21622157 try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);
21632158 try dwarf.strx(di_nw, nav.name.toSlice(ip));
21642159 } else {
2165 try dwarf.abbrevCode(di_nw, .decl_var);
2160 try dwarf.abbrevCode(di_nw, .decl_extern_var);
21662161 try dwarf.refType(pt, di_nw, .fromInterned(zcu.fileRootType(inst_info.file)));
21672162 try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian);
21682163 try di_w.writeUleb128(decl.src_column + 1);
......@@ -2173,7 +2168,6 @@ fn updateExternInner(
21732168 try dwarf.strx(di_nw, nav_extern.name.toSlice(ip));
21742169 if (maybe_func_type) |func_type| {
21752170 try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type));
2176 try dwarf.addrSym(di_nw, si, 0);
21772171 try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
21782172 for (func_type.param_types.get(ip)) |param_type| {
21792173 try dwarf.abbrevCode(di_nw, .extern_param);
......@@ -2182,23 +2176,13 @@ fn updateExternInner(
21822176 if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args);
21832177 if (!is_empty) try di_w.writeUleb128(@backingInt(AbbrevCode.null));
21842178 } else {
2185 const addr: Loc = .{ .addr_sym = .{ .si = si } };
2186 const loc: Loc = if (nav_threadlocal) switch (arch) {
2187 .x86_64 => .{ .form_tls_address = &addr },
2188 else => unreachable,
2189 } else addr;
21902179 const type_offset = di_w.end;
21912180 if (nav_const)
21922181 try dwarf.secOffsetPlaceholder(di_w)
21932182 else
21942183 try dwarf.refType(pt, di_nw, .fromInterned(nav_extern.ty));
2195 try dwarf.exprLoc(di_nw, loc);
21962184 try di_w.writeUleb128(nav_align.toByteUnits() orelse
21972185 Type.fromInterned(nav_extern.ty).abiAlignment(zcu).toByteUnits().?);
2198 switch (nav_extern.source) {
2199 .builtin => {},
2200 .syntax => try di_w.writeByte(@intFromBool(decl.linkage != .normal)),
2201 }
22022186 if (nav_const) {
22032187 try dwarf.secOffsetFinish(di_nw, type_offset, di_nw.ni, di_w.end);
22042188 try dwarf.abbrevCode(di_nw, .is_const);
......@@ -4800,6 +4784,7 @@ pub const AbbrevCode = enum {
48004784 decl_func_generic,
48014785 decl_extern_empty_func,
48024786 decl_extern_func,
4787 decl_extern_var,
48034788 decl_specification_empty_struct,
48044789 decl_specification_struct,
48054790 type_decl_specification_empty_struct,
......@@ -5285,7 +5270,6 @@ pub const AbbrevCode = enum {
52855270 .attrs = decl_attrs ++ .{
52865271 .{ .linkage_name, .strx },
52875272 .{ .type, .ref_addr },
5288 .{ .low_pc, .addr },
52895273 .{ .external, .flag_present },
52905274 .{ .noreturn, .flag },
52915275 },
......@@ -5296,11 +5280,19 @@ pub const AbbrevCode = enum {
52965280 .attrs = decl_attrs ++ .{
52975281 .{ .linkage_name, .strx },
52985282 .{ .type, .ref_addr },
5299 .{ .low_pc, .addr },
53005283 .{ .external, .flag_present },
53015284 .{ .noreturn, .flag },
53025285 },
53035286 },
5287 .decl_extern_var = .{
5288 .tag = .variable,
5289 .attrs = decl_attrs ++ .{
5290 .{ .linkage_name, .strx },
5291 .{ .type, .ref_addr },
5292 .{ .alignment, .udata },
5293 .{ .external, .flag_present },
5294 },
5295 },
53045296 .decl_specification_empty_struct = .{
53055297 .tag = .structure_type,
53065298 .attrs = decl_specification_attrs,
......@@ -5995,7 +5987,6 @@ pub const AbbrevCode = enum {
59955987 .{ .ZIG_parent, .ref_addr },
59965988 .{ .linkage_name, .strx },
59975989 .{ .type, .ref_addr },
5998 .{ .low_pc, .addr },
59995990 .{ .external, .flag_present },
60005991 .{ .noreturn, .flag },
60015992 },
......@@ -6007,7 +5998,6 @@ pub const AbbrevCode = enum {
60075998 .{ .ZIG_parent, .ref_addr },
60085999 .{ .linkage_name, .strx },
60096000 .{ .type, .ref_addr },
6010 .{ .low_pc, .addr },
60116001 .{ .external, .flag_present },
60126002 .{ .noreturn, .flag },
60136003 },
......@@ -6018,7 +6008,6 @@ pub const AbbrevCode = enum {
60186008 .{ .ZIG_parent, .ref_addr },
60196009 .{ .linkage_name, .strx },
60206010 .{ .type, .ref_addr },
6021 .{ .location, .exprloc },
60226011 .{ .alignment, .udata },
60236012 .{ .external, .flag_present },
60246013 },
src/link/Elf2.zig+362-255
......@@ -244,7 +244,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
244244changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
245245/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
246246/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
247/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
247/// section in `flush` only when it is actually necessary. See also `nodeAllowsDynamicReloc`.
248248textrel_count: u32,
249249
250250dwarf: Dwarf,
......@@ -259,6 +259,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl),
259259
260260overflowed_reloc_count: u32,
261261misaligned_reloc_count: u32,
262illegal_dynamic_type_reloc_count: u32,
263illegal_dynamic_section_reloc_count: u32,
262264
263265const_prog_node: std.Progress.Node,
264266input_prog_node: std.Progress.Node,
......@@ -1409,7 +1411,13 @@ const SymbolReloc = struct {
14091411 /// relocation entry. The entry will be removed if we discover a definition which allows us to
14101412 /// statically resolve the relocation.
14111413 rela_index: Section.RelaIndex.Optional,
1412 result: enum(u8) { ok, overflowed, misaligned },
1414 result: enum(u8) {
1415 ok,
1416 overflowed,
1417 misaligned,
1418 illegal_dynamic_type,
1419 illegal_dynamic_section,
1420 },
14131421
14141422 /// Determines the section in which this relocation will be placed if it is outstanding.
14151423 ///
......@@ -1961,6 +1969,10 @@ const SymbolReloc = struct {
19611969 .ok => {},
19621970 .overflowed => elf.overflowed_reloc_count -= 1,
19631971 .misaligned => elf.misaligned_reloc_count -= 1,
1972 .illegal_dynamic_type, .illegal_dynamic_section => {
1973 // This status won't change until a call to `updateDynamicReloc`.
1974 return;
1975 },
19641976 }
19651977 if (reloc.applyInner(elf)) {
19661978 @branchHint(.likely);
......@@ -2071,6 +2083,8 @@ const SymbolReloc = struct {
20712083 .ok => {},
20722084 .overflowed => elf.overflowed_reloc_count -= 1,
20732085 .misaligned => elf.misaligned_reloc_count -= 1,
2086 .illegal_dynamic_type => elf.illegal_dynamic_type_reloc_count -= 1,
2087 .illegal_dynamic_section => elf.illegal_dynamic_section_reloc_count -= 1,
20742088 }
20752089
20762090 reloc.* = undefined;
......@@ -2084,14 +2098,211 @@ const SymbolReloc = struct {
20842098 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
20852099 switch (elf.ehdrType()) {
20862100 .REL => {},
2087 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) {
2088 .no => unreachable, // there *was* a dynamic relocation!
2089 .yes => {},
2090 .yes_textrel => elf.textrel_count -= 1,
2101 .EXEC, .DYN => switch (elf.nodeAllowsDynamicReloc(reloc.node.unwrap().?)) {
2102 .not_alloc => unreachable, // there *was* a dynamic relocation!
2103 .ok => {},
2104 .ok_textrel => elf.textrel_count -= 1,
20912105 },
20922106 }
20932107 reloc.rela_index = .none;
20942108 }
2109
2110 fn updateDynamicReloc(reloc: *SymbolReloc, elf: *Elf) Error!void {
2111 assert(elf.ehdrType() != .REL);
2112 const reloc_node = reloc.node.unwrap().?; // deleted
2113
2114 // Delete any current output relocation or error.
2115 switch (reloc.result) {
2116 .ok, .overflowed, .misaligned => {
2117 reloc.deleteOutputRel(elf);
2118 },
2119 .illegal_dynamic_type => {
2120 elf.illegal_dynamic_type_reloc_count -= 1;
2121 reloc.result = .ok;
2122 },
2123 .illegal_dynamic_section => {
2124 elf.illegal_dynamic_section_reloc_count -= 1;
2125 reloc.result = .ok;
2126 },
2127 }
2128
2129 assert(reloc.rela_index == .none);
2130
2131 const symbol_class = elf.classifySymbolValue(reloc.target);
2132
2133 switch (reloc.target.unwrap()) {
2134 .local => {},
2135 .global => |gsi| switch (symbol_class) {
2136 .static => gsi.ptr(elf).status.any_static_target_relocs = true,
2137 .static_relative => gsi.ptr(elf).status.any_static_relative_target_relocs = true,
2138 .dynamic => gsi.ptr(elf).status.any_dynamic_target_relocs = true,
2139 },
2140 }
2141
2142 const want_dynamic_reloc: enum {
2143 /// `reloc` cannot be statically resolved, but is not an allowed dynamic relocation.
2144 illegal,
2145 relative,
2146 abs_addr,
2147 dtp_off,
2148 tp_off,
2149 size32,
2150 size64,
2151 } = switch (symbol_class) {
2152 .static => return,
2153
2154 .static_relative => switch (reloc.type.target) {
2155 // We need a runtime relocation if the relocation depends on our load address, which
2156 // is the case iff the relocation resolves to the absolute value of a symbol. Relocs
2157 // which are PC-relative, or which resolve to something other than a symbol address
2158 // (e.g. a symbol's size), are fine.
2159
2160 .pltrel,
2161 .rel,
2162 .size,
2163 .dtpoff,
2164 .tpoff,
2165 => return,
2166
2167 .abs, .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2168 true => .relative,
2169 false => .illegal,
2170 },
2171
2172 .special => switch (reloc.type.action.special) {
2173 .larch_pcala_hi20,
2174 .larch_pcala64_lo20,
2175 .larch_pcala64_hi12,
2176 .larch_b21,
2177 .larch_b26,
2178 .larch_call36,
2179 .sparc_wdisp10,
2180 .sparc_wdisp16,
2181 .sparc_le_hix22,
2182 => return,
2183
2184 .sparc_olo10,
2185 .sparc_hix22,
2186 => .illegal,
2187 },
2188 },
2189
2190 .dynamic => switch (reloc.type.target) {
2191 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
2192 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
2193 .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2194 true => .relative,
2195 false => .illegal,
2196 },
2197 // ...although PC-relative PLT relocations don't even need that that!
2198 .pltrel => return,
2199 // PC-relative relocations are not valid dynamic relocations.
2200 .rel => .illegal,
2201 // On the few targets supporting size relocations, they are valid at runtime.
2202 .size => switch (reloc.type.action.simple.dest) {
2203 .@"32" => .size32,
2204 .@"64" => .size64,
2205 else => .illegal,
2206 },
2207 // Absolute addresses and TLS offsets can be lowered at runtime provided they
2208 // are address-sized.
2209 .dtpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2210 true => .dtp_off,
2211 false => .illegal,
2212 },
2213 .tpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2214 true => .tp_off,
2215 false => .illegal,
2216 },
2217 .abs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2218 true => .abs_addr,
2219 false => .illegal,
2220 },
2221
2222 .special => switch (reloc.type.action.special) {
2223 .larch_b21,
2224 .larch_b26,
2225 .larch_call36,
2226 .sparc_wdisp10,
2227 .sparc_wdisp16,
2228 => return,
2229
2230 .larch_pcala_hi20,
2231 .larch_pcala64_lo20,
2232 .larch_pcala64_hi12,
2233 .sparc_olo10,
2234 .sparc_hix22,
2235 .sparc_le_hix22,
2236 => .illegal,
2237 },
2238 },
2239 };
2240
2241 const is_textrel: bool = switch (elf.nodeAllowsDynamicReloc(reloc_node)) {
2242 .not_alloc => {
2243 // The section containing the relocation is not loaded into memory. These sections
2244 // have slightly different rules. In other ELF linkers, they basically just let any
2245 // relocation through at any time---they will bind to a local symbol definition if
2246 // it exists, or just to 0 otherwise. I think that's a little scary though, so until
2247 // I come across a compelling reason for us to mimic that behaviour, let's be more
2248 // conservative and only allow what seems necessary:
2249 const allow: bool = switch (want_dynamic_reloc) {
2250 // Relocations which target the value/[d]tpoff of a symbol with a local
2251 // definition, and which write their result to a full address-sized word, have
2252 // clear use cases (e.g. DWARF uses these), so let them through.
2253 .relative,
2254 .abs_addr,
2255 .dtp_off,
2256 .tp_off,
2257 => switch (reloc.target.unwrap()) {
2258 .local => true,
2259 .global => |gsi| gsi.resolveAlias(elf).defined(elf),
2260 },
2261
2262 // Everything else I'm going to disallow for now. Remember that we're already in
2263 // the dynamic case here, so this isn't disallowing (e.g.) all size relocations
2264 // in these sections, just size relocations *targeting dynamic symbols*!
2265 .size32, .size64, .illegal => false,
2266 };
2267 if (!allow) {
2268 // ...but in this case we actually do need to trigger an error.
2269 reloc.result = .illegal_dynamic_section;
2270 elf.illegal_dynamic_section_reloc_count += 1;
2271 }
2272 return;
2273 },
2274 .ok => false,
2275 .ok_textrel => true,
2276 };
2277
2278 const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) {
2279 .illegal => {
2280 reloc.result = .illegal_dynamic_type;
2281 elf.illegal_dynamic_type_reloc_count += 1;
2282 return;
2283 },
2284 .relative => .relative(elf),
2285 .abs_addr => .absAddr(elf),
2286 .dtp_off => .dtpOff(elf),
2287 .tp_off => .tpOff(elf),
2288 .size32 => MachineRelocType.size32(elf) orelse continue :rel .illegal,
2289 .size64 => MachineRelocType.size64(elf) orelse continue :rel .illegal,
2290 };
2291 const target_dynsym: u32 = switch (want_dynamic_reloc) {
2292 .illegal => unreachable, // handled above
2293 .relative => 0,
2294 else => reloc.target.unwrap().global.dynsymIndex(elf).?,
2295 };
2296
2297 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1);
2298 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
2299 .type = rel_type,
2300 .offset = elf.getNodeVAddr(reloc_node) + reloc.offset,
2301 .raw_sym_index = target_dynsym,
2302 .addend = reloc.addend.normal,
2303 }).toOptional();
2304 if (is_textrel) elf.textrel_count += 1;
2305 }
20952306};
20962307
20972308/// A relocation targeting an arbitrary node (within a section) with a fixed addend.
......@@ -2909,9 +3120,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
29093120 }
29103121 }
29113122
2912 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {
2913 force_apply_relocs = true;
2914 }
3123 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility);
29153124
29163125 // There's already a symbol with this name, we just need to potentially set its value, and
29173126 // to update its bind and visibility.
......@@ -3167,14 +3376,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
31673376 },
31683377 };
31693378
3170 const omit_dynsym: bool = omit: {
3171 if (elf.shndx.dynsym == .UNDEF) break :omit true;
3172 break :omit switch (visibility) {
3173 .HIDDEN, .INTERNAL => true,
3174 .DEFAULT, .PROTECTED => false,
3175 };
3379 const have_dynsym: bool = switch (elf.shndx.dynsym) {
3380 .UNDEF => false,
3381 else => switch (visibility) {
3382 .HIDDEN, .INTERNAL => false,
3383 // An undefined protected dynamic symbol would be pointless, because protected symbols
3384 // don't bind to external definitions; so only add to dynsym if we have a definition.
3385 .PROTECTED => gsi.defined(elf),
3386 // Executables conventionally do not put symbols in their dynamic symbol table unless a
3387 // definition is seen, while dynamic libraries just chuck 'em all in.
3388 .DEFAULT => switch (elf.base.comp.config.output_mode) {
3389 .Exe => gsi.defined(elf) or maybe_dso_global != null,
3390 .Lib => true,
3391 .Obj => unreachable, // we have a dynamic symbol table
3392 },
3393 },
31763394 };
3177 if (omit_dynsym) {
3395 if (!have_dynsym) {
31783396 if (try elf.deleteOwnedDynsym(gsi)) {
31793397 apply_relocs = true;
31803398 }
......@@ -3201,9 +3419,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
32013419 if (elf.ehdrType() != .REL) {
32023420 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
32033421 }
3204 gsi.deleteDynamicTargetRelocs(elf);
3422
3423 if (try gsi.updateDynamicTargetRelocs(elf)) {
3424 apply_relocs = true;
3425 }
32053426 if (maybe_alias_gsi) |alias_gsi| {
3206 alias_gsi.deleteDynamicTargetRelocs(elf);
3427 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3428 apply_relocs = true;
3429 }
32073430 }
32083431
32093432 if (apply_relocs) {
......@@ -3462,10 +3685,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
34623685 _ = elf.unknown_globals.swapRemove(gsi);
34633686 }
34643687
3465 if (elf.classifySymbolValue(.global(gsi)) != .dynamic) {
3466 gsi.deleteDynamicTargetRelocs(elf);
3467 if (maybe_alias_gsi) |alias_gsi| {
3468 alias_gsi.deleteDynamicTargetRelocs(elf);
3688 if (try gsi.updateDynamicTargetRelocs(elf)) {
3689 apply_relocs = true;
3690 }
3691 if (maybe_alias_gsi) |alias_gsi| {
3692 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3693 apply_relocs = true;
34693694 }
34703695 }
34713696
......@@ -3588,15 +3813,12 @@ fn setGlobalSymbolBind(
35883813/// undefined---their visibility values are combined to determine the resulting visibility.
35893814///
35903815/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes
3591/// to symbol visibility can affect the dynamic symbol table entry. If this function returns `true`,
3592/// then that `updateGlobalDynamic` call must have the `force_apply_relocs` argument set to `true`,
3593/// because this symbol's classification has changed (it is no longer a dynamic symbol) so
3594/// relocations targeting the symbol must be re-applied.
3816/// to symbol visibility can affect the dynamic symbol table entry.
35953817fn mergeGlobalSymbolVisibility(
35963818 elf: *Elf,
35973819 gsi: Symbol.Global.Index,
35983820 other_visibility: std.elf.STV,
3599) bool {
3821) void {
36003822 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
36013823 inline else => |sym| elf.targetLoad(&sym.other).visibility,
36023824 };
......@@ -3628,13 +3850,13 @@ fn mergeGlobalSymbolVisibility(
36283850 ),
36293851 }
36303852
3631 if (elf.ehdrType() == .REL) {
3632 return false;
3633 }
3853 if (elf.ehdrType() == .REL) return;
36343854
36353855 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
36363856 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
36373857 // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output.
3858 // TODO: after the latest batch of refactors, I'm unsure if this logic should be here anymore. I
3859 // think it should probably live in `updateGlobalDynamic` instead.
36383860 const old_demoted: bool = switch (old_visibility) {
36393861 .INTERNAL, .HIDDEN => true,
36403862 .DEFAULT, .PROTECTED => false,
......@@ -3652,16 +3874,6 @@ fn mergeGlobalSymbolVisibility(
36523874 }
36533875 elf.moveDemotedGlobal(gsi);
36543876 }
3655
3656 const old_protected: bool = switch (old_visibility) {
3657 .PROTECTED, .INTERNAL, .HIDDEN => true,
3658 .DEFAULT => false,
3659 };
3660 const new_protected: bool = switch (new_visibility) {
3661 .PROTECTED, .INTERNAL, .HIDDEN => true,
3662 .DEFAULT => false,
3663 };
3664 return old_protected != new_protected;
36653877}
36663878/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
36673879/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
......@@ -4038,85 +4250,52 @@ const Symbol = struct {
40384250 return null;
40394251 }
40404252
4041 /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation
4042 /// entries, and adds `R_*_RELATIVE` relocation entries as needed.
4253 /// Updates the dynamic relocations associated with relocations targeting `gsi`, adding
4254 /// or removing them as needed based on the current classification of `gsi`.
40434255 ///
4044 /// Asserts we are creating a DSO.
4045 fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void {
4046 if (elf.shndx.dynamic == .UNDEF) return;
4256 /// Returns `true` if any relocations were updated, in which case relocations targeting
4257 /// `gsi` must be re-applied by the caller.
4258 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4259 if (elf.shndx.dynamic == .UNDEF) return false;
40474260 const sym_id: Symbol.Id = .global(gsi);
4261
40484262 switch (elf.classifySymbolValue(sym_id)) {
40494263 .static => {
40504264 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
40514265 !gsi.ptr(elf).status.any_static_relative_target_relocs)
40524266 {
4053 return;
4267 return false;
40544268 }
4055 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
4056 gsi.ptr(elf).status.any_static_relative_target_relocs = false;
4057 gsi.ptr(elf).status.any_static_target_relocs = true;
40584269 },
40594270 .static_relative => {
40604271 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
40614272 !gsi.ptr(elf).status.any_static_target_relocs)
40624273 {
4063 return;
4274 return false;
4275 }
4276 },
4277 .dynamic => {
4278 if (!gsi.ptr(elf).status.any_static_target_relocs and
4279 !gsi.ptr(elf).status.any_static_relative_target_relocs)
4280 {
4281 return false;
40644282 }
4065 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
4066 gsi.ptr(elf).status.any_static_target_relocs = false;
4067 gsi.ptr(elf).status.any_static_relative_target_relocs = true;
40684283 },
4069 // TODO: this function needs to support a symbol becoming dynamic which wasn't
4070 // previously dynamic (and when it does, the function should be renamed to
4071 // `updateTargetOutputRelocs`). But for now, this isn't supported.
4072 .dynamic => unreachable,
40734284 }
4285
4286 gsi.ptr(elf).status.any_static_target_relocs = false;
4287 gsi.ptr(elf).status.any_static_relative_target_relocs = false;
4288 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
4289
40744290 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
40754291 while (ri != .none) {
40764292 const reloc = ri.get(elf);
40774293 assert(reloc.target == sym_id);
4078 reloc.deleteOutputRel(elf);
4294 try reloc.updateDynamicReloc(elf);
40794295 ri = reloc.next;
40804296 }
4081 switch (elf.classifySymbolValue(sym_id)) {
4082 .static => return,
4083 .static_relative => {},
4084 .dynamic => unreachable,
4085 }
4086 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
4087 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4088 while (ri != .none) {
4089 const reloc = ri.get(elf);
4090 ri = reloc.next;
4091 assert(reloc.target == sym_id);
4092 switch (reloc.type.target) {
4093 // Only relocations which resolve to absolute addresses require runtime
4094 // `R_*_RELATIVE` relocations.
4095 .special,
4096 .pltrel,
4097 .rel,
4098 .dtpoff,
4099 .tpoff,
4100 .size,
4101 => continue,
4102
4103 .abs, .pltabs => {},
4104 }
4105 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
4106 const node = reloc.node.unwrap().?;
4107 switch (elf.nodeWantsDsoRelocation(node)) {
4108 .no => continue,
4109 .yes_textrel => elf.textrel_count += 1,
4110 .yes => {},
4111 }
4112 // There is capacity for a relocation because we just deleted one earlier.
4113 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
4114 .type = .relative(elf),
4115 .offset = elf.getNodeVAddr(node) + reloc.offset,
4116 .raw_sym_index = 0,
4117 .addend = 0,
4118 }).toOptional();
4119 }
4297
4298 return true;
41204299 }
41214300
41224301 const Optional = enum(u32) {
......@@ -4370,61 +4549,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
43704549 .EXEC => false,
43714550 };
43724551
4373 if (elf.shndx.dynamic == .UNDEF) {
4374 // This is a static non-PIE executable---every symbol has a statically known value.
4375 return .static;
4376 }
4377
43784552 const resolved_sym = sym.resolveAlias(elf);
43794553
4380 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) {
4554 const symtab_shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) {
43814555 inline else => |sym_ptr| .{
43824556 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
43834557 elf.targetLoad(&sym_ptr.other).visibility,
43844558 },
43854559 };
43864560
4387 switch (resolved_sym.unwrap()) {
4388 .local => {
4389 assert(shndx != .UNDEF);
4561 const shndx: Section.Index = switch (resolved_sym.unwrap()) {
4562 .local => shndx: {
4563 assert(symtab_shndx != .UNDEF);
43904564 assert(visibility == .DEFAULT);
4565 break :shndx symtab_shndx;
43914566 },
4392 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
4393 // An unprotected symbol in a DSO which is not an executable is subject to runtime
4394 // preemption, so a dynamic relocation is required for it even if we have a definition.
4395 return .dynamic;
4396 } else if (elf.copied_globals.contains(gsi)) {
4397 // This becomes a locally-defined symbol in `.data`.
4398 return if (runtime_load_addr) .static_relative else .static;
4399 },
4400 }
4567 .global => |gsi| shndx: {
4568 const shndx: Section.Index = switch (symtab_shndx) {
4569 .UNDEF => if (elf.copied_globals.contains(gsi)) .data else .UNDEF,
4570 else => |shndx| shndx,
4571 };
44014572
4402 return switch (shndx) {
4403 .UNDEF => switch (visibility) {
4404 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
4405 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
4406 // This is a static PIE---we'll see a definition at some point, so there'd be no
4407 // point in adding an invalid dynamic relocation (the only valid dynamic relation
4408 // type in a static PIE is `R_*_RELATIVE`).
4409 return .static;
4410 } else .dynamic, // external symbol
4573 // Check for dynamic symbols before falling into the static path.
4574 if (gsi.dynsymIndex(elf) != null and visibility == .DEFAULT) {
4575 if (shndx == .UNDEF) {
4576 // There is no local definition of this symbol.
4577 return .dynamic;
4578 } else if (comp.config.output_mode != .Exe) {
4579 // There is a local definition, but it is preemptible at runtime.
4580 return .dynamic;
4581 }
4582 }
44114583
4412 // If the symbol *cannot* be external, then there's no point making a dynamic relocation
4413 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
4414 .INTERNAL, .HIDDEN, .PROTECTED => .static,
4584 break :shndx shndx;
44154585 },
4586 };
44164587
4417 .ABS => .static,
4588 if (runtime_load_addr and
4589 shndx != .UNDEF and
4590 shndx != .ABS and
4591 shndx.flags(elf).ALLOC and
4592 !shndx.flags(elf).TLS)
4593 {
4594 // This symbol's value is a virtual address relative to a runtime-known load address.
4595 return .static_relative;
4596 }
44184597
4419 else => if (runtime_load_addr and
4420 shndx.flags(elf).ALLOC and
4421 !shndx.flags(elf).TLS)
4422 {
4423 return .static_relative;
4424 } else {
4425 return .static;
4426 },
4427 };
4598 return .static;
44284599}
44294600
44304601pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
......@@ -4994,6 +5165,8 @@ fn create(
49945165
49955166 .overflowed_reloc_count = 0,
49965167 .misaligned_reloc_count = 0,
5168 .illegal_dynamic_type_reloc_count = 0,
5169 .illegal_dynamic_section_reloc_count = 0,
49975170
49985171 .const_prog_node = .none,
49995172 .input_prog_node = .none,
......@@ -9701,18 +9874,21 @@ fn addSymbolRelocAssumeCapacityInner(
97019874) Error!void {
97029875 assert(elf.ehdrType() != .REL);
97039876
9704 const rela_index: Section.RelaIndex.Optional = r: {
9705 if (elf.shndx.dynamic == .UNDEF) break :r .none;
9706
9707 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
9708 // determine the vaddr of `node`.
9709 const node_vaddr = elf.getNodeVAddr(node);
9710
9711 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
9712 // not locally defined. If the relocation value is always computed from the target symbol's
9713 // value (even for an external target symbol), and if the target symbol might be of type
9714 // STT_OBJECT, this should probably be `true`.
9715 const try_copy_reloc: bool = switch (@"type".target) {
9877 request_static_target: {
9878 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;
9879 const gsi: Symbol.Global.Index = switch (target.unwrap()) {
9880 .local => break :request_static_target,
9881 .global => |gsi| gsi,
9882 };
9883 // The target symbol may be dynamic, in which case its value cannot be resolved statically.
9884 // However, there are strategies (such as copy relocations) the linker can employ which can
9885 // effectively convert dynamic symbols into static symbols. If statically resolving this
9886 // relocation would require knowing the symbol value, we can set a flag so that those
9887 // strategies are used when possible to make this relocation statically resolvable.
9888 //
9889 // (This `switch` should return `false` for TLS relocations, because we can't do anything
9890 // about TLS symbols being dynamic.)
9891 const want_static_value: bool = switch (@"type".target) {
97169892 .rel, .abs => true,
97179893
97189894 .pltrel,
......@@ -9741,92 +9917,13 @@ fn addSymbolRelocAssumeCapacityInner(
97419917 => false,
97429918 },
97439919 };
9744 if (try_copy_reloc) switch (target.unwrap()) {
9745 .local => {},
9746 .global => |gsi| if (!gsi.ptr(elf).status.want_static_value) {
9747 gsi.ptr(elf).status.want_static_value = true;
9748 try elf.updateGlobalDynamic(gsi, false);
9749 },
9750 };
9751
9752 classify: switch (elf.classifySymbolValue(target)) {
9753 .static => {
9754 switch (target.unwrap()) {
9755 .local => {},
9756 .global => |gsi| gsi.ptr(elf).status.any_static_target_relocs = true,
9757 }
9758 break :r .none;
9759 },
9760 .static_relative => {
9761 switch (target.unwrap()) {
9762 .local => {},
9763 .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true,
9764 }
9765 switch (@"type".target) {
9766 // Only relocations which resolve to absolute addresses require runtime
9767 // `R_*_RELATIVE` relocations.
9768 .special,
9769 .pltrel,
9770 .rel,
9771 .dtpoff,
9772 .tpoff,
9773 .size,
9774 => break :r .none,
9920 if (!want_static_value) break :request_static_target;
97759921
9776 .abs, .pltabs => {},
9777 }
9778 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;
9779 switch (elf.nodeWantsDsoRelocation(node)) {
9780 .no => break :r .none,
9781 .yes => {},
9782 .yes_textrel => elf.textrel_count += 1,
9783 }
9784 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
9785 .type = .relative(elf),
9786 .offset = node_vaddr + offset,
9787 .raw_sym_index = 0,
9788 .addend = 0,
9789 }).toOptional();
9790 },
9791 .dynamic => {
9792 target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true;
9793 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
9794 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
9795 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
9796 .pltabs => continue :classify .static_relative,
9797 // ...although PC-relative PLT relocations don't even need that!
9798 .pltrel => break :r .none,
9799 // Weird sizes or computations are not supported as runtime relocations.
9800 .special => break :r .none,
9801 // Relative addresses are not supported as runtime relocations.
9802 .rel => break :r .none,
9803
9804 // On the few targets supporting size relocations, they are valid at runtime.
9805 .size => switch (@"type".action.simple.dest) {
9806 .@"32" => MachineRelocType.size32(elf) orelse break :r .none,
9807 .@"64" => MachineRelocType.size64(elf) orelse break :r .none,
9808 else => break :r .none,
9809 },
9810 // Absolute addresses and TLS offsets can be lowered at runtime provided they
9811 // are address-sized.
9812 .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none,
9813 .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none,
9814 .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none,
9815 };
9816 switch (elf.nodeWantsDsoRelocation(node)) {
9817 .no => break :r .none,
9818 .yes => {},
9819 .yes_textrel => elf.textrel_count += 1,
9820 }
9821 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
9822 .type = dynamic_reloc_type,
9823 .offset = node_vaddr + offset,
9824 .raw_sym_index = target.unwrap().global.dynsymIndex(elf).?,
9825 .addend = addend.normal,
9826 }).toOptional();
9827 },
9922 if (!gsi.ptr(elf).status.want_static_value) {
9923 gsi.ptr(elf).status.want_static_value = true;
9924 try elf.updateGlobalDynamic(gsi, false);
98289925 }
9829 };
9926 }
98309927
98319928 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
98329929 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
......@@ -9841,12 +9938,13 @@ fn addSymbolRelocAssumeCapacityInner(
98419938 .type = @"type",
98429939 .next = next,
98439940 .prev = .none,
9844 .rela_index = rela_index,
9941 .rela_index = .none,
98459942 .result = .ok,
98469943 });
98479944 if (@"type".dependsOnTlsSize(elf)) {
98489945 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
98499946 }
9947 try ri.get(elf).updateDynamicReloc(elf);
98509948
98519949 // Actually apply the new relocation!
98529950 ri.get(elf).apply(elf);
......@@ -10177,18 +10275,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
1017710275 };
1017810276}
1017910277
10180/// If `node` cannot contain runtime relocations, returns `.no`.
10278/// If `node` cannot contain dynamic relocations because it is not loaded into memory at runtime,
10279/// returns `.not_alloc`.
1018110280///
10182/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires
10183/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
10184fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
10281/// Otherwise, returns `.ok_textrel` if placing a dynamic relocation in this node requires the
10282/// presence of a `DT_TEXTREL` dynamic entry, or `.ok` otherwise.
10283fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } {
1018510284 const shndx = elf.getNodeShndx(node);
1018610285 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
1018710286 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
1018810287 };
10189 if (!shf.ALLOC) return .no;
10190 if (!shf.WRITE) return .yes_textrel;
10191 return .yes;
10288 if (!shf.ALLOC) return .not_alloc;
10289 if (!shf.WRITE) return .ok_textrel;
10290 return .ok;
1019210291}
1019310292
1019410293pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
......@@ -10209,15 +10308,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
1020910308 const nav = ip.getNav(nav_index);
1021010309 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;
1021110310 switch (ip.indexToKey(nav.resolved.?.value)) {
10212 .@"extern" => |@"extern"| {
10311 .@"extern" => {
1021310312 if (mod.strip or elf.ehdrMachine() != .X86_64) return;
10214 const si = try elf.externSymbol(.{
10215 .name = @"extern".name.toSlice(ip),
10216 .lib_name = @"extern".lib_name.toSlice(ip),
10217 .type = elf.navType(nav.resolved.?),
10218 .linkage = @"extern".linkage,
10219 .visibility = @"extern".visibility,
10220 });
1022110313 const dwarf_gi = try elf.dwarf.getGlobal(nav_index);
1022210314 const dwarf_global = dwarf_gi.get(&elf.dwarf);
1022310315 const debug_info_ni = dwarf_global.debug_info_ni.unwrap().?;
......@@ -10226,10 +10318,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
1022610318 debug_info_ni.writer(gpa, &elf.mf, &di_nw);
1022710319 defer di_nw.deinit();
1022810320 elf.resetNodeRelocs(debug_info_ni);
10229 try elf.dwarf.updateExtern(pt, &di_nw, switch (elf.ehdrMachine()) {
10230 else => unreachable,
10231 .X86_64 => .x86_64,
10232 }, si, nav_index);
10321 try elf.dwarf.updateExtern(pt, &di_nw, nav_index);
1023310322 },
1023410323 else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
1023510324 const nmi = try elf.navMapIndex(zcu, nav_index);
......@@ -10757,10 +10846,28 @@ fn flushInner(
1075710846 defer elf.mf.nodes_lock.unlock();
1075810847
1075910848 if (elf.overflowed_reloc_count > 0) {
10760 diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count});
10849 diags.addError(
10850 "failed to apply {d} relocations: overflow",
10851 .{elf.overflowed_reloc_count},
10852 );
1076110853 }
1076210854 if (elf.misaligned_reloc_count > 0) {
10763 diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count});
10855 diags.addError(
10856 "failed to apply {d} relocations: misaligned value",
10857 .{elf.misaligned_reloc_count},
10858 );
10859 }
10860 if (elf.illegal_dynamic_type_reloc_count > 0) {
10861 diags.addError(
10862 "refusing to emit {d} dynamic relocations with illegal types",
10863 .{elf.illegal_dynamic_type_reloc_count},
10864 );
10865 }
10866 if (elf.illegal_dynamic_section_reloc_count > 0) {
10867 diags.addError(
10868 "refusing to emit {d} dynamic relocations in non-allocated sections",
10869 .{elf.illegal_dynamic_section_reloc_count},
10870 );
1076410871 }
1076510872
1076610873 if (elf.archive) |*archive| {
test/debugger/storage-4
......@@ -67,12 +67,8 @@ Process #... stopped
6767(u64) global_threadlocal1 = 0xb4d643528c042121
6868(u64) global_threadlocal2 = 0x43faea1cf5ad7a22
6969(u64) extern_const = 0x1b0c91ea0470b0b2
70(u64) extern_const = 0x1b0c91ea0470b0b2
71(u64) extern_var = 0x19bc17b3f0b61ebc
7270(u64) extern_var = 0x19bc17b3f0b61ebc
7371(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
74(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
75(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
7672(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
7773(lldb) frame variable --show-all-children --show-types --format hex -- param1 param2 param3 param4 param5 param6 param7 param8 local_comptime_val 'local_comptime_ptr.@"0"' local_comptime_ptr[0] local_const local_var
7874(u64) param1 = 0x6a607e08125c7e00