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(...@@ -2101,11 +2101,9 @@ pub fn updateExtern(
2101 dwarf: *Dwarf,2101 dwarf: *Dwarf,
2102 pt: Zcu.PerThread,2102 pt: Zcu.PerThread,
2103 di_nw: *link.MappedFile.Node.Writer,2103 di_nw: *link.MappedFile.Node.Writer,
2104 arch: std.Target.Cpu.Arch,
2105 si: link.File.SymbolId,
2106 global: InternPool.Nav.Index,2104 global: InternPool.Nav.Index,
2107) link.Error!void {2105) 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) {
2109 else => |e| return e,2107 else => |e| return e,
2110 error.WriteFailed => return dwarf.reportWriteError(di_nw),2108 error.WriteFailed => return dwarf.reportWriteError(di_nw),
2111 };2109 };
...@@ -2114,20 +2112,17 @@ fn updateExternInner(...@@ -2114,20 +2112,17 @@ fn updateExternInner(
2114 dwarf: *Dwarf,2112 dwarf: *Dwarf,
2115 pt: Zcu.PerThread,2113 pt: Zcu.PerThread,
2116 di_nw: *link.MappedFile.Node.Writer,2114 di_nw: *link.MappedFile.Node.Writer,
2117 arch: std.Target.Cpu.Arch,
2118 si: link.File.SymbolId,
2119 global: InternPool.Nav.Index,2115 global: InternPool.Nav.Index,
2120) link.EmitError!void {2116) link.EmitError!void {
2121 const zcu = pt.zcu;2117 const zcu = pt.zcu;
2122 const ip = &zcu.intern_pool;2118 const ip = &zcu.intern_pool;
2123 const nav = ip.getNav(global);2119 const nav = ip.getNav(global);
2124 log.debug("updateExtern({f})", .{nav.fqn.fmt(ip)});2120 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: {
2126 const nav_resolved = nav.resolved.?;2122 const nav_resolved = nav.resolved.?;
2127 break :nav_resolved .{2123 break :nav_resolved .{
2128 nav_resolved.@"align",2124 nav_resolved.@"align",
2129 nav_resolved.@"const",2125 nav_resolved.@"const",
2130 nav_resolved.@"threadlocal",
2131 ip.indexToKey(nav_resolved.value).@"extern",2126 ip.indexToKey(nav_resolved.value).@"extern",
2132 };2127 };
2133 };2128 };
...@@ -2162,7 +2157,7 @@ fn updateExternInner(...@@ -2162,7 +2157,7 @@ fn updateExternInner(
2162 try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);2157 try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);
2163 try dwarf.strx(di_nw, nav.name.toSlice(ip));2158 try dwarf.strx(di_nw, nav.name.toSlice(ip));
2164 } else {2159 } else {
2165 try dwarf.abbrevCode(di_nw, .decl_var);2160 try dwarf.abbrevCode(di_nw, .decl_extern_var);
2166 try dwarf.refType(pt, di_nw, .fromInterned(zcu.fileRootType(inst_info.file)));2161 try dwarf.refType(pt, di_nw, .fromInterned(zcu.fileRootType(inst_info.file)));
2167 try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian);2162 try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian);
2168 try di_w.writeUleb128(decl.src_column + 1);2163 try di_w.writeUleb128(decl.src_column + 1);
...@@ -2173,7 +2168,6 @@ fn updateExternInner(...@@ -2173,7 +2168,6 @@ fn updateExternInner(
2173 try dwarf.strx(di_nw, nav_extern.name.toSlice(ip));2168 try dwarf.strx(di_nw, nav_extern.name.toSlice(ip));
2174 if (maybe_func_type) |func_type| {2169 if (maybe_func_type) |func_type| {
2175 try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type));2170 try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type));
2176 try dwarf.addrSym(di_nw, si, 0);
2177 try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));2171 try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
2178 for (func_type.param_types.get(ip)) |param_type| {2172 for (func_type.param_types.get(ip)) |param_type| {
2179 try dwarf.abbrevCode(di_nw, .extern_param);2173 try dwarf.abbrevCode(di_nw, .extern_param);
...@@ -2182,23 +2176,13 @@ fn updateExternInner(...@@ -2182,23 +2176,13 @@ fn updateExternInner(
2182 if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args);2176 if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args);
2183 if (!is_empty) try di_w.writeUleb128(@backingInt(AbbrevCode.null));2177 if (!is_empty) try di_w.writeUleb128(@backingInt(AbbrevCode.null));
2184 } else {2178 } 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;
2190 const type_offset = di_w.end;2179 const type_offset = di_w.end;
2191 if (nav_const)2180 if (nav_const)
2192 try dwarf.secOffsetPlaceholder(di_w)2181 try dwarf.secOffsetPlaceholder(di_w)
2193 else2182 else
2194 try dwarf.refType(pt, di_nw, .fromInterned(nav_extern.ty));2183 try dwarf.refType(pt, di_nw, .fromInterned(nav_extern.ty));
2195 try dwarf.exprLoc(di_nw, loc);
2196 try di_w.writeUleb128(nav_align.toByteUnits() orelse2184 try di_w.writeUleb128(nav_align.toByteUnits() orelse
2197 Type.fromInterned(nav_extern.ty).abiAlignment(zcu).toByteUnits().?);2185 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 }
2202 if (nav_const) {2186 if (nav_const) {
2203 try dwarf.secOffsetFinish(di_nw, type_offset, di_nw.ni, di_w.end);2187 try dwarf.secOffsetFinish(di_nw, type_offset, di_nw.ni, di_w.end);
2204 try dwarf.abbrevCode(di_nw, .is_const);2188 try dwarf.abbrevCode(di_nw, .is_const);
...@@ -4800,6 +4784,7 @@ pub const AbbrevCode = enum {...@@ -4800,6 +4784,7 @@ pub const AbbrevCode = enum {
4800 decl_func_generic,4784 decl_func_generic,
4801 decl_extern_empty_func,4785 decl_extern_empty_func,
4802 decl_extern_func,4786 decl_extern_func,
4787 decl_extern_var,
4803 decl_specification_empty_struct,4788 decl_specification_empty_struct,
4804 decl_specification_struct,4789 decl_specification_struct,
4805 type_decl_specification_empty_struct,4790 type_decl_specification_empty_struct,
...@@ -5285,7 +5270,6 @@ pub const AbbrevCode = enum {...@@ -5285,7 +5270,6 @@ pub const AbbrevCode = enum {
5285 .attrs = decl_attrs ++ .{5270 .attrs = decl_attrs ++ .{
5286 .{ .linkage_name, .strx },5271 .{ .linkage_name, .strx },
5287 .{ .type, .ref_addr },5272 .{ .type, .ref_addr },
5288 .{ .low_pc, .addr },
5289 .{ .external, .flag_present },5273 .{ .external, .flag_present },
5290 .{ .noreturn, .flag },5274 .{ .noreturn, .flag },
5291 },5275 },
...@@ -5296,11 +5280,19 @@ pub const AbbrevCode = enum {...@@ -5296,11 +5280,19 @@ pub const AbbrevCode = enum {
5296 .attrs = decl_attrs ++ .{5280 .attrs = decl_attrs ++ .{
5297 .{ .linkage_name, .strx },5281 .{ .linkage_name, .strx },
5298 .{ .type, .ref_addr },5282 .{ .type, .ref_addr },
5299 .{ .low_pc, .addr },
5300 .{ .external, .flag_present },5283 .{ .external, .flag_present },
5301 .{ .noreturn, .flag },5284 .{ .noreturn, .flag },
5302 },5285 },
5303 },5286 },
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 },
5304 .decl_specification_empty_struct = .{5296 .decl_specification_empty_struct = .{
5305 .tag = .structure_type,5297 .tag = .structure_type,
5306 .attrs = decl_specification_attrs,5298 .attrs = decl_specification_attrs,
...@@ -5995,7 +5987,6 @@ pub const AbbrevCode = enum {...@@ -5995,7 +5987,6 @@ pub const AbbrevCode = enum {
5995 .{ .ZIG_parent, .ref_addr },5987 .{ .ZIG_parent, .ref_addr },
5996 .{ .linkage_name, .strx },5988 .{ .linkage_name, .strx },
5997 .{ .type, .ref_addr },5989 .{ .type, .ref_addr },
5998 .{ .low_pc, .addr },
5999 .{ .external, .flag_present },5990 .{ .external, .flag_present },
6000 .{ .noreturn, .flag },5991 .{ .noreturn, .flag },
6001 },5992 },
...@@ -6007,7 +5998,6 @@ pub const AbbrevCode = enum {...@@ -6007,7 +5998,6 @@ pub const AbbrevCode = enum {
6007 .{ .ZIG_parent, .ref_addr },5998 .{ .ZIG_parent, .ref_addr },
6008 .{ .linkage_name, .strx },5999 .{ .linkage_name, .strx },
6009 .{ .type, .ref_addr },6000 .{ .type, .ref_addr },
6010 .{ .low_pc, .addr },
6011 .{ .external, .flag_present },6001 .{ .external, .flag_present },
6012 .{ .noreturn, .flag },6002 .{ .noreturn, .flag },
6013 },6003 },
...@@ -6018,7 +6008,6 @@ pub const AbbrevCode = enum {...@@ -6018,7 +6008,6 @@ pub const AbbrevCode = enum {
6018 .{ .ZIG_parent, .ref_addr },6008 .{ .ZIG_parent, .ref_addr },
6019 .{ .linkage_name, .strx },6009 .{ .linkage_name, .strx },
6020 .{ .type, .ref_addr },6010 .{ .type, .ref_addr },
6021 .{ .location, .exprloc },
6022 .{ .alignment, .udata },6011 .{ .alignment, .udata },
6023 .{ .external, .flag_present },6012 .{ .external, .flag_present },
6024 },6013 },
src/link/Elf2.zig+362-255
...@@ -244,7 +244,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),...@@ -244,7 +244,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
244changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),244changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
245/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`245/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
246/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`246/// 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`.
248textrel_count: u32,248textrel_count: u32,
249249
250dwarf: Dwarf,250dwarf: Dwarf,
...@@ -259,6 +259,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl),...@@ -259,6 +259,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl),
259259
260overflowed_reloc_count: u32,260overflowed_reloc_count: u32,
261misaligned_reloc_count: u32,261misaligned_reloc_count: u32,
262illegal_dynamic_type_reloc_count: u32,
263illegal_dynamic_section_reloc_count: u32,
262264
263const_prog_node: std.Progress.Node,265const_prog_node: std.Progress.Node,
264input_prog_node: std.Progress.Node,266input_prog_node: std.Progress.Node,
...@@ -1409,7 +1411,13 @@ const SymbolReloc = struct {...@@ -1409,7 +1411,13 @@ const SymbolReloc = struct {
1409 /// relocation entry. The entry will be removed if we discover a definition which allows us to1411 /// relocation entry. The entry will be removed if we discover a definition which allows us to
1410 /// statically resolve the relocation.1412 /// statically resolve the relocation.
1411 rela_index: Section.RelaIndex.Optional,1413 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
1414 /// Determines the section in which this relocation will be placed if it is outstanding.1422 /// Determines the section in which this relocation will be placed if it is outstanding.
1415 ///1423 ///
...@@ -1961,6 +1969,10 @@ const SymbolReloc = struct {...@@ -1961,6 +1969,10 @@ const SymbolReloc = struct {
1961 .ok => {},1969 .ok => {},
1962 .overflowed => elf.overflowed_reloc_count -= 1,1970 .overflowed => elf.overflowed_reloc_count -= 1,
1963 .misaligned => elf.misaligned_reloc_count -= 1,1971 .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 },
1964 }1976 }
1965 if (reloc.applyInner(elf)) {1977 if (reloc.applyInner(elf)) {
1966 @branchHint(.likely);1978 @branchHint(.likely);
...@@ -2071,6 +2083,8 @@ const SymbolReloc = struct {...@@ -2071,6 +2083,8 @@ const SymbolReloc = struct {
2071 .ok => {},2083 .ok => {},
2072 .overflowed => elf.overflowed_reloc_count -= 1,2084 .overflowed => elf.overflowed_reloc_count -= 1,
2073 .misaligned => elf.misaligned_reloc_count -= 1,2085 .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,
2074 }2088 }
20752089
2076 reloc.* = undefined;2090 reloc.* = undefined;
...@@ -2084,14 +2098,211 @@ const SymbolReloc = struct {...@@ -2084,14 +2098,211 @@ const SymbolReloc = struct {
2084 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);2098 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
2085 switch (elf.ehdrType()) {2099 switch (elf.ehdrType()) {
2086 .REL => {},2100 .REL => {},
2087 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) {2101 .EXEC, .DYN => switch (elf.nodeAllowsDynamicReloc(reloc.node.unwrap().?)) {
2088 .no => unreachable, // there *was* a dynamic relocation!2102 .not_alloc => unreachable, // there *was* a dynamic relocation!
2089 .yes => {},2103 .ok => {},
2090 .yes_textrel => elf.textrel_count -= 1,2104 .ok_textrel => elf.textrel_count -= 1,
2091 },2105 },
2092 }2106 }
2093 reloc.rela_index = .none;2107 reloc.rela_index = .none;
2094 }2108 }
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 }
2095};2306};
20962307
2097/// A relocation targeting an arbitrary node (within a section) with a fixed addend.2308/// 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{...@@ -2909,9 +3120,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2909 }3120 }
2910 }3121 }
29113122
2912 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {3123 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility);
2913 force_apply_relocs = true;
2914 }
29153124
2916 // There's already a symbol with this name, we just need to potentially set its value, and3125 // There's already a symbol with this name, we just need to potentially set its value, and
2917 // to update its bind and visibility.3126 // to update its bind and visibility.
...@@ -3167,14 +3376,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3167,14 +3376,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3167 },3376 },
3168 };3377 };
31693378
3170 const omit_dynsym: bool = omit: {3379 const have_dynsym: bool = switch (elf.shndx.dynsym) {
3171 if (elf.shndx.dynsym == .UNDEF) break :omit true;3380 .UNDEF => false,
3172 break :omit switch (visibility) {3381 else => switch (visibility) {
3173 .HIDDEN, .INTERNAL => true,3382 .HIDDEN, .INTERNAL => false,
3174 .DEFAULT, .PROTECTED => false,3383 // An undefined protected dynamic symbol would be pointless, because protected symbols
3175 };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 },
3176 };3394 };
3177 if (omit_dynsym) {3395 if (!have_dynsym) {
3178 if (try elf.deleteOwnedDynsym(gsi)) {3396 if (try elf.deleteOwnedDynsym(gsi)) {
3179 apply_relocs = true;3397 apply_relocs = true;
3180 }3398 }
...@@ -3201,9 +3419,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3201,9 +3419,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3201 if (elf.ehdrType() != .REL) {3419 if (elf.ehdrType() != .REL) {
3202 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);3420 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
3203 }3421 }
3204 gsi.deleteDynamicTargetRelocs(elf);3422
3423 if (try gsi.updateDynamicTargetRelocs(elf)) {
3424 apply_relocs = true;
3425 }
3205 if (maybe_alias_gsi) |alias_gsi| {3426 if (maybe_alias_gsi) |alias_gsi| {
3206 alias_gsi.deleteDynamicTargetRelocs(elf);3427 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3428 apply_relocs = true;
3429 }
3207 }3430 }
32083431
3209 if (apply_relocs) {3432 if (apply_relocs) {
...@@ -3462,10 +3685,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3462,10 +3685,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3462 _ = elf.unknown_globals.swapRemove(gsi);3685 _ = elf.unknown_globals.swapRemove(gsi);
3463 }3686 }
34643687
3465 if (elf.classifySymbolValue(.global(gsi)) != .dynamic) {3688 if (try gsi.updateDynamicTargetRelocs(elf)) {
3466 gsi.deleteDynamicTargetRelocs(elf);3689 apply_relocs = true;
3467 if (maybe_alias_gsi) |alias_gsi| {3690 }
3468 alias_gsi.deleteDynamicTargetRelocs(elf);3691 if (maybe_alias_gsi) |alias_gsi| {
3692 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3693 apply_relocs = true;
3469 }3694 }
3470 }3695 }
34713696
...@@ -3588,15 +3813,12 @@ fn setGlobalSymbolBind(...@@ -3588,15 +3813,12 @@ fn setGlobalSymbolBind(
3588/// undefined---their visibility values are combined to determine the resulting visibility.3813/// undefined---their visibility values are combined to determine the resulting visibility.
3589///3814///
3590/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes3815/// 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`,3816/// to symbol visibility can affect the dynamic symbol table entry.
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.
3595fn mergeGlobalSymbolVisibility(3817fn mergeGlobalSymbolVisibility(
3596 elf: *Elf,3818 elf: *Elf,
3597 gsi: Symbol.Global.Index,3819 gsi: Symbol.Global.Index,
3598 other_visibility: std.elf.STV,3820 other_visibility: std.elf.STV,
3599) bool {3821) void {
3600 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {3822 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3601 inline else => |sym| elf.targetLoad(&sym.other).visibility,3823 inline else => |sym| elf.targetLoad(&sym.other).visibility,
3602 };3824 };
...@@ -3628,13 +3850,13 @@ fn mergeGlobalSymbolVisibility(...@@ -3628,13 +3850,13 @@ fn mergeGlobalSymbolVisibility(
3628 ),3850 ),
3629 }3851 }
36303852
3631 if (elf.ehdrType() == .REL) {3853 if (elf.ehdrType() == .REL) return;
3632 return false;
3633 }
36343854
3635 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or3855 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
3636 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to3856 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
3637 // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output.3857 // 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.
3638 const old_demoted: bool = switch (old_visibility) {3860 const old_demoted: bool = switch (old_visibility) {
3639 .INTERNAL, .HIDDEN => true,3861 .INTERNAL, .HIDDEN => true,
3640 .DEFAULT, .PROTECTED => false,3862 .DEFAULT, .PROTECTED => false,
...@@ -3652,16 +3874,6 @@ fn mergeGlobalSymbolVisibility(...@@ -3652,16 +3874,6 @@ fn mergeGlobalSymbolVisibility(
3652 }3874 }
3653 elf.moveDemotedGlobal(gsi);3875 elf.moveDemotedGlobal(gsi);
3654 }3876 }
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;
3665}3877}
3666/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),3878/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
3667/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF3879/// 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 {...@@ -4038,85 +4250,52 @@ const Symbol = struct {
4038 return null;4250 return null;
4039 }4251 }
40404252
4041 /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation4253 /// Updates the dynamic relocations associated with relocations targeting `gsi`, adding
4042 /// entries, and adds `R_*_RELATIVE` relocation entries as needed.4254 /// or removing them as needed based on the current classification of `gsi`.
4043 ///4255 ///
4044 /// Asserts we are creating a DSO.4256 /// Returns `true` if any relocations were updated, in which case relocations targeting
4045 fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void {4257 /// `gsi` must be re-applied by the caller.
4046 if (elf.shndx.dynamic == .UNDEF) return;4258 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4259 if (elf.shndx.dynamic == .UNDEF) return false;
4047 const sym_id: Symbol.Id = .global(gsi);4260 const sym_id: Symbol.Id = .global(gsi);
4261
4048 switch (elf.classifySymbolValue(sym_id)) {4262 switch (elf.classifySymbolValue(sym_id)) {
4049 .static => {4263 .static => {
4050 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and4264 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
4051 !gsi.ptr(elf).status.any_static_relative_target_relocs)4265 !gsi.ptr(elf).status.any_static_relative_target_relocs)
4052 {4266 {
4053 return;4267 return false;
4054 }4268 }
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;
4058 },4269 },
4059 .static_relative => {4270 .static_relative => {
4060 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and4271 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
4061 !gsi.ptr(elf).status.any_static_target_relocs)4272 !gsi.ptr(elf).status.any_static_target_relocs)
4062 {4273 {
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;
4064 }4282 }
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;
4068 },4283 },
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,
4073 }4284 }
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
4074 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;4290 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4075 while (ri != .none) {4291 while (ri != .none) {
4076 const reloc = ri.get(elf);4292 const reloc = ri.get(elf);
4077 assert(reloc.target == sym_id);4293 assert(reloc.target == sym_id);
4078 reloc.deleteOutputRel(elf);4294 try reloc.updateDynamicReloc(elf);
4079 ri = reloc.next;4295 ri = reloc.next;
4080 }4296 }
4081 switch (elf.classifySymbolValue(sym_id)) {4297
4082 .static => return,4298 return true;
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 }
4120 }4299 }
41214300
4122 const Optional = enum(u32) {4301 const Optional = enum(u32) {
...@@ -4370,61 +4549,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -4370,61 +4549,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
4370 .EXEC => false,4549 .EXEC => false,
4371 };4550 };
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
4378 const resolved_sym = sym.resolveAlias(elf);4552 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))) {
4381 inline else => |sym_ptr| .{4555 inline else => |sym_ptr| .{
4382 .fromSection(elf.targetLoad(&sym_ptr.shndx)),4556 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
4383 elf.targetLoad(&sym_ptr.other).visibility,4557 elf.targetLoad(&sym_ptr.other).visibility,
4384 },4558 },
4385 };4559 };
43864560
4387 switch (resolved_sym.unwrap()) {4561 const shndx: Section.Index = switch (resolved_sym.unwrap()) {
4388 .local => {4562 .local => shndx: {
4389 assert(shndx != .UNDEF);4563 assert(symtab_shndx != .UNDEF);
4390 assert(visibility == .DEFAULT);4564 assert(visibility == .DEFAULT);
4565 break :shndx symtab_shndx;
4391 },4566 },
4392 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {4567 .global => |gsi| shndx: {
4393 // An unprotected symbol in a DSO which is not an executable is subject to runtime4568 const shndx: Section.Index = switch (symtab_shndx) {
4394 // preemption, so a dynamic relocation is required for it even if we have a definition.4569 .UNDEF => if (elf.copied_globals.contains(gsi)) .data else .UNDEF,
4395 return .dynamic;4570 else => |shndx| shndx,
4396 } else if (elf.copied_globals.contains(gsi)) {4571 };
4397 // This becomes a locally-defined symbol in `.data`.
4398 return if (runtime_load_addr) .static_relative else .static;
4399 },
4400 }
44014572
4402 return switch (shndx) {4573 // Check for dynamic symbols before falling into the static path.
4403 .UNDEF => switch (visibility) {4574 if (gsi.dynsymIndex(elf) != null and visibility == .DEFAULT) {
4404 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {4575 if (shndx == .UNDEF) {
4405 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section4576 // There is no local definition of this symbol.
4406 // This is a static PIE---we'll see a definition at some point, so there'd be no4577 return .dynamic;
4407 // point in adding an invalid dynamic relocation (the only valid dynamic relation4578 } else if (comp.config.output_mode != .Exe) {
4408 // type in a static PIE is `R_*_RELATIVE`).4579 // There is a local definition, but it is preemptible at runtime.
4409 return .static;4580 return .dynamic;
4410 } else .dynamic, // external symbol4581 }
4582 }
44114583
4412 // If the symbol *cannot* be external, then there's no point making a dynamic relocation4584 break :shndx shndx;
4413 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
4414 .INTERNAL, .HIDDEN, .PROTECTED => .static,
4415 },4585 },
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 and4598 return .static;
4420 shndx.flags(elf).ALLOC and
4421 !shndx.flags(elf).TLS)
4422 {
4423 return .static_relative;
4424 } else {
4425 return .static;
4426 },
4427 };
4428}4599}
44294600
4430pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {4601pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
...@@ -4994,6 +5165,8 @@ fn create(...@@ -4994,6 +5165,8 @@ fn create(
49945165
4995 .overflowed_reloc_count = 0,5166 .overflowed_reloc_count = 0,
4996 .misaligned_reloc_count = 0,5167 .misaligned_reloc_count = 0,
5168 .illegal_dynamic_type_reloc_count = 0,
5169 .illegal_dynamic_section_reloc_count = 0,
49975170
4998 .const_prog_node = .none,5171 .const_prog_node = .none,
4999 .input_prog_node = .none,5172 .input_prog_node = .none,
...@@ -9701,18 +9874,21 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9701,18 +9874,21 @@ fn addSymbolRelocAssumeCapacityInner(
9701) Error!void {9874) Error!void {
9702 assert(elf.ehdrType() != .REL);9875 assert(elf.ehdrType() != .REL);
97039876
9704 const rela_index: Section.RelaIndex.Optional = r: {9877 request_static_target: {
9705 if (elf.shndx.dynamic == .UNDEF) break :r .none;9878 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;
97069879 const gsi: Symbol.Global.Index = switch (target.unwrap()) {
9707 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to9880 .local => break :request_static_target,
9708 // determine the vaddr of `node`.9881 .global => |gsi| gsi,
9709 const node_vaddr = elf.getNodeVAddr(node);9882 };
97109883 // The target symbol may be dynamic, in which case its value cannot be resolved statically.
9711 // If this is `true`, we will try to create a copy relocation for the target symbol if it is9884 // However, there are strategies (such as copy relocations) the linker can employ which can
9712 // not locally defined. If the relocation value is always computed from the target symbol's9885 // effectively convert dynamic symbols into static symbols. If statically resolving this
9713 // value (even for an external target symbol), and if the target symbol might be of type9886 // relocation would require knowing the symbol value, we can set a flag so that those
9714 // STT_OBJECT, this should probably be `true`.9887 // strategies are used when possible to make this relocation statically resolvable.
9715 const try_copy_reloc: bool = switch (@"type".target) {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) {
9716 .rel, .abs => true,9892 .rel, .abs => true,
97179893
9718 .pltrel,9894 .pltrel,
...@@ -9741,92 +9917,13 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9741,92 +9917,13 @@ fn addSymbolRelocAssumeCapacityInner(
9741 => false,9917 => false,
9742 },9918 },
9743 };9919 };
9744 if (try_copy_reloc) switch (target.unwrap()) {9920 if (!want_static_value) break :request_static_target;
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,
97759921
9776 .abs, .pltabs => {},9922 if (!gsi.ptr(elf).status.want_static_value) {
9777 }9923 gsi.ptr(elf).status.want_static_value = true;
9778 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;9924 try elf.updateGlobalDynamic(gsi, false);
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 },
9828 }9925 }
9829 };9926 }
98309927
9831 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));9928 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
9832 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;9929 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
...@@ -9841,12 +9938,13 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9841,12 +9938,13 @@ fn addSymbolRelocAssumeCapacityInner(
9841 .type = @"type",9938 .type = @"type",
9842 .next = next,9939 .next = next,
9843 .prev = .none,9940 .prev = .none,
9844 .rela_index = rela_index,9941 .rela_index = .none,
9845 .result = .ok,9942 .result = .ok,
9846 });9943 });
9847 if (@"type".dependsOnTlsSize(elf)) {9944 if (@"type".dependsOnTlsSize(elf)) {
9848 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});9945 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
9849 }9946 }
9947 try ri.get(elf).updateDynamicReloc(elf);
98509948
9851 // Actually apply the new relocation!9949 // Actually apply the new relocation!
9852 ri.get(elf).apply(elf);9950 ri.get(elf).apply(elf);
...@@ -10177,18 +10275,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -10177,18 +10275,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
10177 };10275 };
10178}10276}
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`.
10181///10280///
10182/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires10281/// Otherwise, returns `.ok_textrel` if placing a dynamic relocation in this node requires the
10183/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.10282/// presence of a `DT_TEXTREL` dynamic entry, or `.ok` otherwise.
10184fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {10283fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } {
10185 const shndx = elf.getNodeShndx(node);10284 const shndx = elf.getNodeShndx(node);
10186 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {10285 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
10187 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,10286 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
10188 };10287 };
10189 if (!shf.ALLOC) return .no;10288 if (!shf.ALLOC) return .not_alloc;
10190 if (!shf.WRITE) return .yes_textrel;10289 if (!shf.WRITE) return .ok_textrel;
10191 return .yes;10290 return .ok;
10192}10291}
1019310292
10194pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {10293pub 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)...@@ -10209,15 +10308,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
10209 const nav = ip.getNav(nav_index);10308 const nav = ip.getNav(nav_index);
10210 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;10309 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;
10211 switch (ip.indexToKey(nav.resolved.?.value)) {10310 switch (ip.indexToKey(nav.resolved.?.value)) {
10212 .@"extern" => |@"extern"| {10311 .@"extern" => {
10213 if (mod.strip or elf.ehdrMachine() != .X86_64) return;10312 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 });
10221 const dwarf_gi = try elf.dwarf.getGlobal(nav_index);10313 const dwarf_gi = try elf.dwarf.getGlobal(nav_index);
10222 const dwarf_global = dwarf_gi.get(&elf.dwarf);10314 const dwarf_global = dwarf_gi.get(&elf.dwarf);
10223 const debug_info_ni = dwarf_global.debug_info_ni.unwrap().?;10315 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)...@@ -10226,10 +10318,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
10226 debug_info_ni.writer(gpa, &elf.mf, &di_nw);10318 debug_info_ni.writer(gpa, &elf.mf, &di_nw);
10227 defer di_nw.deinit();10319 defer di_nw.deinit();
10228 elf.resetNodeRelocs(debug_info_ni);10320 elf.resetNodeRelocs(debug_info_ni);
10229 try elf.dwarf.updateExtern(pt, &di_nw, switch (elf.ehdrMachine()) {10321 try elf.dwarf.updateExtern(pt, &di_nw, nav_index);
10230 else => unreachable,
10231 .X86_64 => .x86_64,
10232 }, si, nav_index);
10233 },10322 },
10234 else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {10323 else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
10235 const nmi = try elf.navMapIndex(zcu, nav_index);10324 const nmi = try elf.navMapIndex(zcu, nav_index);
...@@ -10757,10 +10846,28 @@ fn flushInner(...@@ -10757,10 +10846,28 @@ fn flushInner(
10757 defer elf.mf.nodes_lock.unlock();10846 defer elf.mf.nodes_lock.unlock();
1075810847
10759 if (elf.overflowed_reloc_count > 0) {10848 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 );
10761 }10853 }
10762 if (elf.misaligned_reloc_count > 0) {10854 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 );
10764 }10871 }
1076510872
10766 if (elf.archive) |*archive| {10873 if (elf.archive) |*archive| {
test/debugger/storage-4
...@@ -67,12 +67,8 @@ Process #... stopped...@@ -67,12 +67,8 @@ Process #... stopped
67(u64) global_threadlocal1 = 0xb4d643528c04212167(u64) global_threadlocal1 = 0xb4d643528c042121
68(u64) global_threadlocal2 = 0x43faea1cf5ad7a2268(u64) global_threadlocal2 = 0x43faea1cf5ad7a22
69(u64) extern_const = 0x1b0c91ea0470b0b269(u64) extern_const = 0x1b0c91ea0470b0b2
70(u64) extern_const = 0x1b0c91ea0470b0b2
71(u64) extern_var = 0x19bc17b3f0b61ebc
72(u64) extern_var = 0x19bc17b3f0b61ebc70(u64) extern_var = 0x19bc17b3f0b61ebc
73(u64) extern_threadlocal1 = 0x3034c4ce967ed64d71(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
74(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
75(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
76(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb72(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
77(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_var73(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
78(u64) param1 = 0x6a607e08125c7e0074(u64) param1 = 0x6a607e08125c7e00