| ... | ... | @@ -244,7 +244,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), |
| 244 | 244 | changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void), |
| 245 | 245 | /// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL` |
| 246 | 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`. |
| 248 | 248 | textrel_count: u32, |
| 249 | 249 | |
| 250 | 250 | dwarf: Dwarf, |
| ... | ... | @@ -259,6 +259,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl), |
| 259 | 259 | |
| 260 | 260 | overflowed_reloc_count: u32, |
| 261 | 261 | misaligned_reloc_count: u32, |
| 262 | illegal_dynamic_type_reloc_count: u32, |
| 263 | illegal_dynamic_section_reloc_count: u32, |
| 262 | 264 | |
| 263 | 265 | const_prog_node: std.Progress.Node, |
| 264 | 266 | input_prog_node: std.Progress.Node, |
| ... | ... | @@ -1409,7 +1411,13 @@ const SymbolReloc = struct { |
| 1409 | 1411 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 1410 | 1412 | /// statically resolve the relocation. |
| 1411 | 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 | }, |
| 1413 | 1421 | |
| 1414 | 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 | 1969 | .ok => {}, |
| 1962 | 1970 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1963 | 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 | 1977 | if (reloc.applyInner(elf)) { |
| 1966 | 1978 | @branchHint(.likely); |
| ... | ... | @@ -2071,6 +2083,8 @@ const SymbolReloc = struct { |
| 2071 | 2083 | .ok => {}, |
| 2072 | 2084 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 2073 | 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 | } |
| 2075 | 2089 | |
| 2076 | 2090 | reloc.* = undefined; |
| ... | ... | @@ -2084,14 +2098,211 @@ const SymbolReloc = struct { |
| 2084 | 2098 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 2085 | 2099 | switch (elf.ehdrType()) { |
| 2086 | 2100 | .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, |
| 2091 | 2105 | }, |
| 2092 | 2106 | } |
| 2093 | 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 | }; |
| 2096 | 2307 | |
| 2097 | 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 | 3120 | } |
| 2910 | 3121 | } |
| 2911 | 3122 | |
| 2912 | | if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) { |
| 2913 | | force_apply_relocs = true; |
| 2914 | | } |
| 3123 | elf.mergeGlobalSymbolVisibility(gsi, opts.visibility); |
| 2915 | 3124 | |
| 2916 | 3125 | // There's already a symbol with this name, we just need to potentially set its value, and |
| 2917 | 3126 | // to update its bind and visibility. |
| ... | ... | @@ -3167,14 +3376,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3167 | 3376 | }, |
| 3168 | 3377 | }; |
| 3169 | 3378 | |
| 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 | }, |
| 3176 | 3394 | }; |
| 3177 | | if (omit_dynsym) { |
| 3395 | if (!have_dynsym) { |
| 3178 | 3396 | if (try elf.deleteOwnedDynsym(gsi)) { |
| 3179 | 3397 | apply_relocs = true; |
| 3180 | 3398 | } |
| ... | ... | @@ -3201,9 +3419,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3201 | 3419 | if (elf.ehdrType() != .REL) { |
| 3202 | 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 | 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 | } |
| 3208 | 3431 | |
| 3209 | 3432 | if (apply_relocs) { |
| ... | ... | @@ -3462,10 +3685,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3462 | 3685 | _ = elf.unknown_globals.swapRemove(gsi); |
| 3463 | 3686 | } |
| 3464 | 3687 | |
| 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; |
| 3469 | 3694 | } |
| 3470 | 3695 | } |
| 3471 | 3696 | |
| ... | ... | @@ -3588,15 +3813,12 @@ fn setGlobalSymbolBind( |
| 3588 | 3813 | /// undefined---their visibility values are combined to determine the resulting visibility. |
| 3589 | 3814 | /// |
| 3590 | 3815 | /// 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. |
| 3595 | 3817 | fn mergeGlobalSymbolVisibility( |
| 3596 | 3818 | elf: *Elf, |
| 3597 | 3819 | gsi: Symbol.Global.Index, |
| 3598 | 3820 | other_visibility: std.elf.STV, |
| 3599 | | ) bool { |
| 3821 | ) void { |
| 3600 | 3822 | const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) { |
| 3601 | 3823 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 3602 | 3824 | }; |
| ... | ... | @@ -3628,13 +3850,13 @@ fn mergeGlobalSymbolVisibility( |
| 3628 | 3850 | ), |
| 3629 | 3851 | } |
| 3630 | 3852 | |
| 3631 | | if (elf.ehdrType() == .REL) { |
| 3632 | | return false; |
| 3633 | | } |
| 3853 | if (elf.ehdrType() == .REL) return; |
| 3634 | 3854 | |
| 3635 | 3855 | // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or |
| 3636 | 3856 | // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to |
| 3637 | 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 | 3860 | const old_demoted: bool = switch (old_visibility) { |
| 3639 | 3861 | .INTERNAL, .HIDDEN => true, |
| 3640 | 3862 | .DEFAULT, .PROTECTED => false, |
| ... | ... | @@ -3652,16 +3874,6 @@ fn mergeGlobalSymbolVisibility( |
| 3652 | 3874 | } |
| 3653 | 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 | 3878 | /// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`), |
| 3667 | 3879 | /// 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 | 4250 | return null; |
| 4039 | 4251 | } |
| 4040 | 4252 | |
| 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`. |
| 4043 | 4255 | /// |
| 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; |
| 4047 | 4260 | const sym_id: Symbol.Id = .global(gsi); |
| 4261 | |
| 4048 | 4262 | switch (elf.classifySymbolValue(sym_id)) { |
| 4049 | 4263 | .static => { |
| 4050 | 4264 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and |
| 4051 | 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 | 4270 | .static_relative => { |
| 4060 | 4271 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and |
| 4061 | 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 | 4290 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4075 | 4291 | while (ri != .none) { |
| 4076 | 4292 | const reloc = ri.get(elf); |
| 4077 | 4293 | assert(reloc.target == sym_id); |
| 4078 | | reloc.deleteOutputRel(elf); |
| 4294 | try reloc.updateDynamicReloc(elf); |
| 4079 | 4295 | ri = reloc.next; |
| 4080 | 4296 | } |
| 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; |
| 4120 | 4299 | } |
| 4121 | 4300 | |
| 4122 | 4301 | const Optional = enum(u32) { |
| ... | ... | @@ -4370,61 +4549,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 4370 | 4549 | .EXEC => false, |
| 4371 | 4550 | }; |
| 4372 | 4551 | |
| 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 | 4552 | const resolved_sym = sym.resolveAlias(elf); |
| 4379 | 4553 | |
| 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 | 4555 | inline else => |sym_ptr| .{ |
| 4382 | 4556 | .fromSection(elf.targetLoad(&sym_ptr.shndx)), |
| 4383 | 4557 | elf.targetLoad(&sym_ptr.other).visibility, |
| 4384 | 4558 | }, |
| 4385 | 4559 | }; |
| 4386 | 4560 | |
| 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); |
| 4390 | 4564 | assert(visibility == .DEFAULT); |
| 4565 | break :shndx symtab_shndx; |
| 4391 | 4566 | }, |
| 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 | }; |
| 4401 | 4572 | |
| 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 | } |
| 4411 | 4583 | |
| 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; |
| 4415 | 4585 | }, |
| 4586 | }; |
| 4416 | 4587 | |
| 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 | } |
| 4418 | 4597 | |
| 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; |
| 4428 | 4599 | } |
| 4429 | 4600 | |
| 4430 | 4601 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| ... | ... | @@ -4994,6 +5165,8 @@ fn create( |
| 4994 | 5165 | |
| 4995 | 5166 | .overflowed_reloc_count = 0, |
| 4996 | 5167 | .misaligned_reloc_count = 0, |
| 5168 | .illegal_dynamic_type_reloc_count = 0, |
| 5169 | .illegal_dynamic_section_reloc_count = 0, |
| 4997 | 5170 | |
| 4998 | 5171 | .const_prog_node = .none, |
| 4999 | 5172 | .input_prog_node = .none, |
| ... | ... | @@ -9701,18 +9874,21 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9701 | 9874 | ) Error!void { |
| 9702 | 9875 | assert(elf.ehdrType() != .REL); |
| 9703 | 9876 | |
| 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) { |
| 9716 | 9892 | .rel, .abs => true, |
| 9717 | 9893 | |
| 9718 | 9894 | .pltrel, |
| ... | ... | @@ -9741,92 +9917,13 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9741 | 9917 | => false, |
| 9742 | 9918 | }, |
| 9743 | 9919 | }; |
| 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; |
| 9775 | 9921 | |
| 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); |
| 9828 | 9925 | } |
| 9829 | | }; |
| 9926 | } |
| 9830 | 9927 | |
| 9831 | 9928 | const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); |
| 9832 | 9929 | const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; |
| ... | ... | @@ -9841,12 +9938,13 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9841 | 9938 | .type = @"type", |
| 9842 | 9939 | .next = next, |
| 9843 | 9940 | .prev = .none, |
| 9844 | | .rela_index = rela_index, |
| 9941 | .rela_index = .none, |
| 9845 | 9942 | .result = .ok, |
| 9846 | 9943 | }); |
| 9847 | 9944 | if (@"type".dependsOnTlsSize(elf)) { |
| 9848 | 9945 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 9849 | 9946 | } |
| 9947 | try ri.get(elf).updateDynamicReloc(elf); |
| 9850 | 9948 | |
| 9851 | 9949 | // Actually apply the new relocation! |
| 9852 | 9950 | ri.get(elf).apply(elf); |
| ... | ... | @@ -10177,18 +10275,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 10177 | 10275 | }; |
| 10178 | 10276 | } |
| 10179 | 10277 | |
| 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 requires |
| 10183 | | /// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise. |
| 10184 | | fn 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. |
| 10283 | fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } { |
| 10185 | 10284 | const shndx = elf.getNodeShndx(node); |
| 10186 | 10285 | const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 10187 | 10286 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| 10188 | 10287 | }; |
| 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; |
| 10192 | 10291 | } |
| 10193 | 10292 | |
| 10194 | 10293 | pub 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 | 10308 | const nav = ip.getNav(nav_index); |
| 10210 | 10309 | const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?; |
| 10211 | 10310 | switch (ip.indexToKey(nav.resolved.?.value)) { |
| 10212 | | .@"extern" => |@"extern"| { |
| 10311 | .@"extern" => { |
| 10213 | 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 | 10313 | const dwarf_gi = try elf.dwarf.getGlobal(nav_index); |
| 10222 | 10314 | const dwarf_global = dwarf_gi.get(&elf.dwarf); |
| 10223 | 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 | 10318 | debug_info_ni.writer(gpa, &elf.mf, &di_nw); |
| 10227 | 10319 | defer di_nw.deinit(); |
| 10228 | 10320 | 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); |
| 10233 | 10322 | }, |
| 10234 | 10323 | else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) { |
| 10235 | 10324 | const nmi = try elf.navMapIndex(zcu, nav_index); |
| ... | ... | @@ -10757,10 +10846,28 @@ fn flushInner( |
| 10757 | 10846 | defer elf.mf.nodes_lock.unlock(); |
| 10758 | 10847 | |
| 10759 | 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 | 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 | } |
| 10765 | 10872 | |
| 10766 | 10873 | if (elf.archive) |*archive| { |