| ... | ... | @@ -244,8 +244,9 @@ 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 | entry_gsi: Symbol.Global.Index.Optional, |
| 249 | 250 | |
| 250 | 251 | dwarf: Dwarf, |
| 251 | 252 | dwarf_shared: std.enums.EnumArray(Dwarf.SharedSection, dwarf_relocs.Shared), |
| ... | ... | @@ -259,6 +260,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl), |
| 259 | 260 | |
| 260 | 261 | overflowed_reloc_count: u32, |
| 261 | 262 | misaligned_reloc_count: u32, |
| 263 | illegal_dynamic_type_reloc_count: u32, |
| 264 | illegal_dynamic_section_reloc_count: u32, |
| 262 | 265 | |
| 263 | 266 | const_prog_node: std.Progress.Node, |
| 264 | 267 | input_prog_node: std.Progress.Node, |
| ... | ... | @@ -308,9 +311,6 @@ const Node = union(enum) { |
| 308 | 311 | /// emitted a copy relocation. |
| 309 | 312 | /// |
| 310 | 313 | /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those. |
| 311 | | /// |
| 312 | | /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because |
| 313 | | /// `MappedFile` does not (yet?) support deleting nodes. See logic in `updateGlobalDynamic`. |
| 314 | 314 | copied_global: Symbol.Global.Index, |
| 315 | 315 | /// May contain relocations. |
| 316 | 316 | nav: NavMapIndex, |
| ... | ... | @@ -464,6 +464,135 @@ const Node = union(enum) { |
| 464 | 464 | fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index { |
| 465 | 465 | return @fromBackingInt(@backingInt(atom)); |
| 466 | 466 | } |
| 467 | |
| 468 | fn fmtDebug(ni: MappedFile.Node.Index, elf: *Elf) FormatDebug { |
| 469 | return .{ .ni = ni, .elf = elf }; |
| 470 | } |
| 471 | const FormatDebug = struct { |
| 472 | ni: MappedFile.Node.Index, |
| 473 | elf: *Elf, |
| 474 | pub fn format(ctx: FormatDebug, w: *Io.Writer) Io.Writer.Error!void { |
| 475 | const elf = ctx.elf; |
| 476 | const node = elf.getNode(ctx.ni); |
| 477 | try w.writeAll(@tagName(node)); |
| 478 | switch (node) { |
| 479 | // nodes with no payload |
| 480 | .deleted, |
| 481 | .archive, |
| 482 | .archive_header, |
| 483 | .elf, |
| 484 | .ehdr, |
| 485 | .shdr, |
| 486 | .debug_addr, |
| 487 | .eh_frame_footer, |
| 488 | .debug_str_offsets, |
| 489 | .unit_padding, |
| 490 | .archive_elf_member_header, |
| 491 | => |tag| comptime assert(@TypeOf(tag) == void), |
| 492 | |
| 493 | .archive_input_member => |ii| try w.print("({f}{f})", .{ |
| 494 | ii.path(elf).fmtEscapeString(), |
| 495 | fmtMemberString(ii.member(elf)), |
| 496 | }), |
| 497 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 498 | inline else => |phdr| { |
| 499 | const ph = &phdr[phndx]; |
| 500 | try w.writeByte('('); |
| 501 | const pt = elf.targetLoad(&ph.type); |
| 502 | if (std.enums.tagName(std.elf.PT, pt)) |pt_name| |
| 503 | try w.writeAll(pt_name) |
| 504 | else inline for (@typeInfo(std.elf.PT).@"enum".decl_names) |decl_name| { |
| 505 | const decl_val = @field(std.elf.PT, decl_name); |
| 506 | if (@TypeOf(decl_val) != std.elf.PT) continue; |
| 507 | if (pt == @field(std.elf.PT, decl_name)) break try w.writeAll(decl_name); |
| 508 | } else try w.print("0x{x}", .{pt}); |
| 509 | try w.writeAll(", "); |
| 510 | const pf = elf.targetLoad(&ph.flags); |
| 511 | if (pf.R) try w.writeByte('R'); |
| 512 | if (pf.W) try w.writeByte('W'); |
| 513 | if (pf.X) try w.writeByte('X'); |
| 514 | try w.writeByte(')'); |
| 515 | }, |
| 516 | }, |
| 517 | .section, .section_manual_size => |shndx| try w.print("({q})", .{ |
| 518 | shndx.name(elf).slice(elf), |
| 519 | }), |
| 520 | .input_section => |isi| { |
| 521 | const ii = isi.input(elf); |
| 522 | try w.print("({f}{f}, {q})", .{ |
| 523 | ii.path(elf).fmtEscapeString(), |
| 524 | fmtMemberString(ii.member(elf)), |
| 525 | elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), |
| 526 | }); |
| 527 | }, |
| 528 | .copied_global => |gsi| try w.print("({q})", .{ |
| 529 | gsi.rawName(elf).slice(elf), |
| 530 | }), |
| 531 | .nav => |nmi| { |
| 532 | const ip = &elf.base.comp.zcu.?.intern_pool; |
| 533 | try w.print("({qf})", .{ |
| 534 | ip.getNav(nmi.nav(elf)).fqn.fmt(ip), |
| 535 | }); |
| 536 | }, |
| 537 | .uav => |umi| { |
| 538 | const zcu = elf.base.comp.zcu.?; |
| 539 | const val: Value = .fromInterned(umi.uavValue(elf)); |
| 540 | try w.print("({f}, {f})", .{ |
| 541 | val.typeOf(zcu).fmt(zcu), |
| 542 | val.fmtValue(zcu), |
| 543 | }); |
| 544 | }, |
| 545 | inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{ |
| 546 | Type.fromInterned(lmi.lazySymbol(elf).ty).fmt(elf.base.comp.zcu.?), |
| 547 | }), |
| 548 | |
| 549 | .debug_shared => |ss| try w.print("({t})", .{ss}), |
| 550 | |
| 551 | .unit_frame, |
| 552 | .unit_frame_cie, |
| 553 | .unit_debug_info, |
| 554 | .unit_debug_info_header, |
| 555 | .unit_debug_info_footer, |
| 556 | .unit_debug_line, |
| 557 | .unit_debug_line_header, |
| 558 | .unit_debug_rnglists, |
| 559 | => |ui| try w.print("({q})", .{ |
| 560 | ui.mod(&elf.dwarf).fully_qualified_name, |
| 561 | }), |
| 562 | |
| 563 | .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) { |
| 564 | .generic_poison_type => try w.writeAll("(anytype)"), |
| 565 | else => |val| try w.print("({f})", .{ |
| 566 | Value.fromInterned(val).fmtValue(elf.base.comp.zcu.?), |
| 567 | }), |
| 568 | }, |
| 569 | .global_debug_info => |gi| { |
| 570 | const ip = &elf.base.comp.zcu.?.intern_pool; |
| 571 | try w.print("({qf})", .{ |
| 572 | ip.getNav(gi.nav(&elf.dwarf)).fqn.fmt(ip), |
| 573 | }); |
| 574 | }, |
| 575 | .func_frame_fde, .func_debug_info, .func_debug_line => |fi| { |
| 576 | const ip = &elf.base.comp.zcu.?.intern_pool; |
| 577 | try w.print("({qf})", .{ |
| 578 | ip.getNav(fi.nav(&elf.dwarf)).fqn.fmt(ip), |
| 579 | }); |
| 580 | }, |
| 581 | .decl_debug_info => |di| { |
| 582 | const comp = elf.base.comp; |
| 583 | const zcu = comp.zcu.?; |
| 584 | const ip = &zcu.intern_pool; |
| 585 | const src_inst = di.srcInst(&elf.dwarf); |
| 586 | const path = zcu.fileByIndex(src_inst.resolveFile(ip)).path; |
| 587 | if (src_inst.resolve(ip)) |inst| { |
| 588 | try w.print("({f}, %{d})", .{ path.fmt(comp), inst }); |
| 589 | } else { |
| 590 | try w.print("({f}, lost)", .{path.fmt(comp)}); |
| 591 | } |
| 592 | }, |
| 593 | } |
| 594 | } |
| 595 | }; |
| 467 | 596 | }; |
| 468 | 597 | |
| 469 | 598 | const InputSection = struct { |
| ... | ... | @@ -947,10 +1076,14 @@ const dwarf_relocs = struct { |
| 947 | 1076 | const Addr = struct { |
| 948 | 1077 | first_target_reloc: NodeReloc.Index, |
| 949 | 1078 | symbol_relocs: std.ArrayList(SymbolReloc.Index), |
| 1079 | /// Index into `symbol_relocs`. |
| 1080 | pending_reloc_index: usize, |
| 950 | 1081 | }; |
| 951 | 1082 | const StrOffsets = struct { |
| 952 | 1083 | first_target_reloc: NodeReloc.Index, |
| 953 | 1084 | node_relocs: std.ArrayList(NodeReloc.Index), |
| 1085 | /// Index into `node_relocs`. |
| 1086 | pending_reloc_index: usize, |
| 954 | 1087 | }; |
| 955 | 1088 | const Unit = struct { |
| 956 | 1089 | frame_cie_first_target_reloc: NodeReloc.Index, |
| ... | ... | @@ -1279,7 +1412,13 @@ const SymbolReloc = struct { |
| 1279 | 1412 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 1280 | 1413 | /// statically resolve the relocation. |
| 1281 | 1414 | rela_index: Section.RelaIndex.Optional, |
| 1282 | | result: enum(u8) { ok, overflowed, misaligned }, |
| 1415 | result: enum(u8) { |
| 1416 | ok, |
| 1417 | overflowed, |
| 1418 | misaligned, |
| 1419 | illegal_dynamic_type, |
| 1420 | illegal_dynamic_section, |
| 1421 | }, |
| 1283 | 1422 | |
| 1284 | 1423 | /// Determines the section in which this relocation will be placed if it is outstanding. |
| 1285 | 1424 | /// |
| ... | ... | @@ -1813,32 +1952,28 @@ const SymbolReloc = struct { |
| 1813 | 1952 | } |
| 1814 | 1953 | }; |
| 1815 | 1954 | |
| 1816 | | fn flushMovedNode(reloc: *SymbolReloc, elf: *Elf, node_vaddr: u64) void { |
| 1817 | | if (reloc.rela_index.unwrap()) |rela_index| { |
| 1818 | | // The node has moved, so the offset of the relocation within the section might have |
| 1819 | | // changed, so update the `offset` field of the `ElfN.Rela` entry. |
| 1820 | | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 1821 | | } |
| 1822 | | // This is not just the inverse of the above condition, because if `reloc` is relative |
| 1823 | | // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we |
| 1824 | | // still need to call `SymbolReloc.apply` to update that relocation's addend. |
| 1825 | | if (elf.ehdrType() != .REL) { |
| 1826 | | reloc.apply(elf); |
| 1827 | | } |
| 1828 | | } |
| 1829 | | |
| 1830 | 1955 | fn apply(reloc: *SymbolReloc, elf: *Elf) void { |
| 1831 | | assert(elf.ehdrType() != .REL); |
| 1832 | 1956 | const node = reloc.node.unwrap() orelse return; // deleted |
| 1833 | 1957 | if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1834 | 1958 | // There's no point applying the relocation now, because it will be re-applied by |
| 1835 | 1959 | // `flushMoved` at some point anyway. |
| 1836 | 1960 | return; |
| 1837 | 1961 | } |
| 1962 | if (elf.ehdrType() == .REL) { |
| 1963 | // All we need to do is update the output relocation. |
| 1964 | const rela_index = reloc.rela_index.unwrap().?; |
| 1965 | const reloc_addr = elf.getNodeVAddr(node) + reloc.offset; |
| 1966 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, reloc_addr); |
| 1967 | return; |
| 1968 | } |
| 1838 | 1969 | switch (reloc.result) { |
| 1839 | 1970 | .ok => {}, |
| 1840 | 1971 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1841 | 1972 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 1973 | .illegal_dynamic_type, .illegal_dynamic_section => { |
| 1974 | // This status won't change until a call to `updateDynamicReloc`. |
| 1975 | return; |
| 1976 | }, |
| 1842 | 1977 | } |
| 1843 | 1978 | if (reloc.applyInner(elf)) { |
| 1844 | 1979 | @branchHint(.likely); |
| ... | ... | @@ -1900,7 +2035,11 @@ const SymbolReloc = struct { |
| 1900 | 2035 | // is required, meaning we can handle it now and return early. |
| 1901 | 2036 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { |
| 1902 | 2037 | .static => unreachable, |
| 1903 | | .dynamic => return, // the relocation happens at runtime |
| 2038 | .dynamic => { |
| 2039 | // The relocation happens at runtime; we just need to update its offset. |
| 2040 | elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr); |
| 2041 | return; |
| 2042 | }, |
| 1904 | 2043 | .static_relative => { |
| 1905 | 2044 | // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address |
| 1906 | 2045 | // relocation. The value computed above is valid, but instead of writing it to the |
| ... | ... | @@ -1912,6 +2051,7 @@ const SymbolReloc = struct { |
| 1912 | 2051 | } |
| 1913 | 2052 | assert(reloc.type.action.simple.cast == .unsigned); |
| 1914 | 2053 | assert(reloc.type.action.simple.shift == .@"0"); |
| 2054 | elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr); |
| 1915 | 2055 | elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val); |
| 1916 | 2056 | return; |
| 1917 | 2057 | }, |
| ... | ... | @@ -1944,6 +2084,8 @@ const SymbolReloc = struct { |
| 1944 | 2084 | .ok => {}, |
| 1945 | 2085 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1946 | 2086 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 2087 | .illegal_dynamic_type => elf.illegal_dynamic_type_reloc_count -= 1, |
| 2088 | .illegal_dynamic_section => elf.illegal_dynamic_section_reloc_count -= 1, |
| 1947 | 2089 | } |
| 1948 | 2090 | |
| 1949 | 2091 | reloc.* = undefined; |
| ... | ... | @@ -1957,14 +2099,211 @@ const SymbolReloc = struct { |
| 1957 | 2099 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 1958 | 2100 | switch (elf.ehdrType()) { |
| 1959 | 2101 | .REL => {}, |
| 1960 | | .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) { |
| 1961 | | .no => unreachable, // there *was* a dynamic relocation! |
| 1962 | | .yes => {}, |
| 1963 | | .yes_textrel => elf.textrel_count -= 1, |
| 2102 | .EXEC, .DYN => switch (elf.nodeAllowsDynamicReloc(reloc.node.unwrap().?)) { |
| 2103 | .not_alloc => unreachable, // there *was* a dynamic relocation! |
| 2104 | .ok => {}, |
| 2105 | .ok_textrel => elf.textrel_count -= 1, |
| 1964 | 2106 | }, |
| 1965 | 2107 | } |
| 1966 | 2108 | reloc.rela_index = .none; |
| 1967 | 2109 | } |
| 2110 | |
| 2111 | fn updateDynamicReloc(reloc: *SymbolReloc, elf: *Elf) Error!void { |
| 2112 | assert(elf.ehdrType() != .REL); |
| 2113 | const reloc_node = reloc.node.unwrap().?; // deleted |
| 2114 | |
| 2115 | // Delete any current output relocation or error. |
| 2116 | switch (reloc.result) { |
| 2117 | .ok, .overflowed, .misaligned => { |
| 2118 | reloc.deleteOutputRel(elf); |
| 2119 | }, |
| 2120 | .illegal_dynamic_type => { |
| 2121 | elf.illegal_dynamic_type_reloc_count -= 1; |
| 2122 | reloc.result = .ok; |
| 2123 | }, |
| 2124 | .illegal_dynamic_section => { |
| 2125 | elf.illegal_dynamic_section_reloc_count -= 1; |
| 2126 | reloc.result = .ok; |
| 2127 | }, |
| 2128 | } |
| 2129 | |
| 2130 | assert(reloc.rela_index == .none); |
| 2131 | |
| 2132 | const symbol_class = elf.classifySymbolValue(reloc.target); |
| 2133 | |
| 2134 | switch (reloc.target.unwrap()) { |
| 2135 | .local => {}, |
| 2136 | .global => |gsi| switch (symbol_class) { |
| 2137 | .static => gsi.ptr(elf).status.any_static_target_relocs = true, |
| 2138 | .static_relative => gsi.ptr(elf).status.any_static_relative_target_relocs = true, |
| 2139 | .dynamic => gsi.ptr(elf).status.any_dynamic_target_relocs = true, |
| 2140 | }, |
| 2141 | } |
| 2142 | |
| 2143 | const want_dynamic_reloc: enum { |
| 2144 | /// `reloc` cannot be statically resolved, but is not an allowed dynamic relocation. |
| 2145 | illegal, |
| 2146 | relative, |
| 2147 | abs_addr, |
| 2148 | dtp_off, |
| 2149 | tp_off, |
| 2150 | size32, |
| 2151 | size64, |
| 2152 | } = switch (symbol_class) { |
| 2153 | .static => return, |
| 2154 | |
| 2155 | .static_relative => switch (reloc.type.target) { |
| 2156 | // We need a runtime relocation if the relocation depends on our load address, which |
| 2157 | // is the case iff the relocation resolves to the absolute value of a symbol. Relocs |
| 2158 | // which are PC-relative, or which resolve to something other than a symbol address |
| 2159 | // (e.g. a symbol's size), are fine. |
| 2160 | |
| 2161 | .pltrel, |
| 2162 | .rel, |
| 2163 | .size, |
| 2164 | .dtpoff, |
| 2165 | .tpoff, |
| 2166 | => return, |
| 2167 | |
| 2168 | .abs, .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) { |
| 2169 | true => .relative, |
| 2170 | false => .illegal, |
| 2171 | }, |
| 2172 | |
| 2173 | .special => switch (reloc.type.action.special) { |
| 2174 | .larch_pcala_hi20, |
| 2175 | .larch_pcala64_lo20, |
| 2176 | .larch_pcala64_hi12, |
| 2177 | .larch_b21, |
| 2178 | .larch_b26, |
| 2179 | .larch_call36, |
| 2180 | .sparc_wdisp10, |
| 2181 | .sparc_wdisp16, |
| 2182 | .sparc_le_hix22, |
| 2183 | => return, |
| 2184 | |
| 2185 | .sparc_olo10, |
| 2186 | .sparc_hix22, |
| 2187 | => .illegal, |
| 2188 | }, |
| 2189 | }, |
| 2190 | |
| 2191 | .dynamic => switch (reloc.type.target) { |
| 2192 | // PLT relocations targeting dynamic symbols actually target that symbol's PLT |
| 2193 | // entry, so we should emit an `R_*_RELATIVE` relocation instead. |
| 2194 | .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) { |
| 2195 | true => .relative, |
| 2196 | false => .illegal, |
| 2197 | }, |
| 2198 | // ...although PC-relative PLT relocations don't even need that that! |
| 2199 | .pltrel => return, |
| 2200 | // PC-relative relocations are not valid dynamic relocations. |
| 2201 | .rel => .illegal, |
| 2202 | // On the few targets supporting size relocations, they are valid at runtime. |
| 2203 | .size => switch (reloc.type.action.simple.dest) { |
| 2204 | .@"32" => .size32, |
| 2205 | .@"64" => .size64, |
| 2206 | else => .illegal, |
| 2207 | }, |
| 2208 | // Absolute addresses and TLS offsets can be lowered at runtime provided they |
| 2209 | // are address-sized. |
| 2210 | .dtpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) { |
| 2211 | true => .dtp_off, |
| 2212 | false => .illegal, |
| 2213 | }, |
| 2214 | .tpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) { |
| 2215 | true => .tp_off, |
| 2216 | false => .illegal, |
| 2217 | }, |
| 2218 | .abs => switch (reloc.type.action.simple.dest.isAddr(elf)) { |
| 2219 | true => .abs_addr, |
| 2220 | false => .illegal, |
| 2221 | }, |
| 2222 | |
| 2223 | .special => switch (reloc.type.action.special) { |
| 2224 | .larch_b21, |
| 2225 | .larch_b26, |
| 2226 | .larch_call36, |
| 2227 | .sparc_wdisp10, |
| 2228 | .sparc_wdisp16, |
| 2229 | => return, |
| 2230 | |
| 2231 | .larch_pcala_hi20, |
| 2232 | .larch_pcala64_lo20, |
| 2233 | .larch_pcala64_hi12, |
| 2234 | .sparc_olo10, |
| 2235 | .sparc_hix22, |
| 2236 | .sparc_le_hix22, |
| 2237 | => .illegal, |
| 2238 | }, |
| 2239 | }, |
| 2240 | }; |
| 2241 | |
| 2242 | const is_textrel: bool = switch (elf.nodeAllowsDynamicReloc(reloc_node)) { |
| 2243 | .not_alloc => { |
| 2244 | // The section containing the relocation is not loaded into memory. These sections |
| 2245 | // have slightly different rules. In other ELF linkers, they basically just let any |
| 2246 | // relocation through at any time---they will bind to a local symbol definition if |
| 2247 | // it exists, or just to 0 otherwise. I think that's a little scary though, so until |
| 2248 | // I come across a compelling reason for us to mimic that behaviour, let's be more |
| 2249 | // conservative and only allow what seems necessary: |
| 2250 | const allow: bool = switch (want_dynamic_reloc) { |
| 2251 | // Relocations which target the value/[d]tpoff of a symbol with a local |
| 2252 | // definition, and which write their result to a full address-sized word, have |
| 2253 | // clear use cases (e.g. DWARF uses these), so let them through. |
| 2254 | .relative, |
| 2255 | .abs_addr, |
| 2256 | .dtp_off, |
| 2257 | .tp_off, |
| 2258 | => switch (reloc.target.unwrap()) { |
| 2259 | .local => true, |
| 2260 | .global => |gsi| gsi.resolveAlias(elf).defined(elf), |
| 2261 | }, |
| 2262 | |
| 2263 | // Everything else I'm going to disallow for now. Remember that we're already in |
| 2264 | // the dynamic case here, so this isn't disallowing (e.g.) all size relocations |
| 2265 | // in these sections, just size relocations *targeting dynamic symbols*! |
| 2266 | .size32, .size64, .illegal => false, |
| 2267 | }; |
| 2268 | if (!allow) { |
| 2269 | // ...but in this case we actually do need to trigger an error. |
| 2270 | reloc.result = .illegal_dynamic_section; |
| 2271 | elf.illegal_dynamic_section_reloc_count += 1; |
| 2272 | } |
| 2273 | return; |
| 2274 | }, |
| 2275 | .ok => false, |
| 2276 | .ok_textrel => true, |
| 2277 | }; |
| 2278 | |
| 2279 | const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) { |
| 2280 | .illegal => { |
| 2281 | reloc.result = .illegal_dynamic_type; |
| 2282 | elf.illegal_dynamic_type_reloc_count += 1; |
| 2283 | return; |
| 2284 | }, |
| 2285 | .relative => .relative(elf), |
| 2286 | .abs_addr => .absAddr(elf), |
| 2287 | .dtp_off => .dtpOff(elf), |
| 2288 | .tp_off => .tpOff(elf), |
| 2289 | .size32 => MachineRelocType.size32(elf) orelse continue :rel .illegal, |
| 2290 | .size64 => MachineRelocType.size64(elf) orelse continue :rel .illegal, |
| 2291 | }; |
| 2292 | const target_dynsym: u32 = switch (want_dynamic_reloc) { |
| 2293 | .illegal => unreachable, // handled above |
| 2294 | .relative => 0, |
| 2295 | else => reloc.target.unwrap().global.dynsymIndex(elf).?, |
| 2296 | }; |
| 2297 | |
| 2298 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1); |
| 2299 | reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 2300 | .type = rel_type, |
| 2301 | .offset = elf.getNodeVAddr(reloc_node) + reloc.offset, |
| 2302 | .raw_sym_index = target_dynsym, |
| 2303 | .addend = reloc.addend.normal, |
| 2304 | }).toOptional(); |
| 2305 | if (is_textrel) elf.textrel_count += 1; |
| 2306 | } |
| 1968 | 2307 | }; |
| 1969 | 2308 | |
| 1970 | 2309 | /// A relocation targeting an arbitrary node (within a section) with a fixed addend. |
| ... | ... | @@ -1992,34 +2331,14 @@ const NodeReloc = struct { |
| 1992 | 2331 | } |
| 1993 | 2332 | }; |
| 1994 | 2333 | |
| 1995 | | fn flushMovedNode(reloc: *NodeReloc, elf: *Elf, node_vaddr: u64) void { |
| 1996 | | if (reloc.rela_index.unwrap()) |rela_index| { |
| 1997 | | assert(elf.ehdrType() == .REL); |
| 1998 | | // The node has moved, so the offset of the relocation within the section might have |
| 1999 | | // changed, so update the `offset` field of the `ElfN.Rela` entry. |
| 2000 | | elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 2001 | | } else { |
| 2002 | | assert(elf.ehdrType() != .REL); |
| 2003 | | reloc.apply(elf); |
| 2004 | | } |
| 2005 | | } |
| 2006 | | |
| 2007 | | fn flushMovedTarget(reloc: *NodeReloc, elf: *Elf, target_section_offset: u64) void { |
| 2008 | | if (reloc.rela_index.unwrap()) |rela_index| { |
| 2009 | | assert(elf.ehdrType() == .REL); |
| 2010 | | // The target has moved, so the `addend` field of the `ElfN.Rela` entry needs to be updated. |
| 2011 | | elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetAddend(elf, rela_index, target_section_offset +% @as(u64, @bitCast(reloc.addend))); |
| 2012 | | } else { |
| 2013 | | assert(elf.ehdrType() != .REL); |
| 2014 | | reloc.apply(elf); |
| 2015 | | } |
| 2016 | | } |
| 2017 | | |
| 2018 | 2334 | fn apply(reloc: *NodeReloc, elf: *Elf) void { |
| 2019 | 2335 | const node = reloc.node.unwrap() orelse return; // deleted |
| 2020 | 2336 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 2021 | 2337 | assert(elf.ehdrType() == .REL); |
| 2022 | | _ = rela_index; |
| 2338 | // Ensure the output relocation is up-to-date. |
| 2339 | const rela_shndx = elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx; |
| 2340 | rela_shndx.relaSetOffset(elf, rela_index, elf.getNodeVAddr(node) + reloc.offset); |
| 2341 | rela_shndx.relaSetAddend(elf, rela_index, elf.getNodeVAddr(node) +% @as(u64, @bitCast(reloc.addend))); |
| 2023 | 2342 | } else { |
| 2024 | 2343 | assert(elf.ehdrType() != .REL); |
| 2025 | 2344 | if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) { |
| ... | ... | @@ -2755,6 +3074,8 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 2755 | 3074 | } || Error)!Symbol.Global.Index { |
| 2756 | 3075 | _ = opts.lib_name; // TODO |
| 2757 | 3076 | |
| 3077 | log.debug("addGlobalSymbol({q})", .{opts.name}); |
| 3078 | |
| 2758 | 3079 | const gpa = elf.base.comp.gpa; |
| 2759 | 3080 | |
| 2760 | 3081 | try elf.symtab.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -2802,9 +3123,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 2802 | 3123 | } |
| 2803 | 3124 | } |
| 2804 | 3125 | |
| 2805 | | if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) { |
| 2806 | | force_apply_relocs = true; |
| 2807 | | } |
| 3126 | elf.mergeGlobalSymbolVisibility(gsi, opts.visibility); |
| 2808 | 3127 | |
| 2809 | 3128 | // There's already a symbol with this name, we just need to potentially set its value, and |
| 2810 | 3129 | // to update its bind and visibility. |
| ... | ... | @@ -2988,10 +3307,15 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 2988 | 3307 | /// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as |
| 2989 | 3308 | /// `true` to guarantee this function to do so. |
| 2990 | 3309 | fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void { |
| 3310 | const trace = tracy.trace(@src()); |
| 3311 | defer trace.end(); |
| 3312 | |
| 2991 | 3313 | const gpa = elf.base.comp.gpa; |
| 2992 | 3314 | |
| 2993 | 3315 | const gsi = orig_gsi.resolveAlias(elf); |
| 2994 | 3316 | |
| 3317 | trace.addText(gsi.rawName(elf).slice(elf)); |
| 3318 | |
| 2995 | 3319 | const maybe_alias_gsi = gsi.findAliaser(elf); |
| 2996 | 3320 | |
| 2997 | 3321 | var apply_relocs: bool = force_apply_relocs; |
| ... | ... | @@ -3055,14 +3379,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3055 | 3379 | }, |
| 3056 | 3380 | }; |
| 3057 | 3381 | |
| 3058 | | const omit_dynsym: bool = omit: { |
| 3059 | | if (elf.shndx.dynsym == .UNDEF) break :omit true; |
| 3060 | | break :omit switch (visibility) { |
| 3061 | | .HIDDEN, .INTERNAL => true, |
| 3062 | | .DEFAULT, .PROTECTED => false, |
| 3063 | | }; |
| 3382 | const have_dynsym: bool = switch (elf.shndx.dynsym) { |
| 3383 | .UNDEF => false, |
| 3384 | else => switch (visibility) { |
| 3385 | .HIDDEN, .INTERNAL => false, |
| 3386 | // An undefined protected dynamic symbol would be pointless, because protected symbols |
| 3387 | // don't bind to external definitions; so only add to dynsym if we have a definition. |
| 3388 | .PROTECTED => gsi.defined(elf), |
| 3389 | // Executables conventionally do not put symbols in their dynamic symbol table unless a |
| 3390 | // definition is seen, while dynamic libraries just chuck 'em all in. |
| 3391 | .DEFAULT => switch (elf.base.comp.config.output_mode) { |
| 3392 | .Exe => gsi.defined(elf) or maybe_dso_global != null, |
| 3393 | .Lib => true, |
| 3394 | .Obj => unreachable, // we have a dynamic symbol table |
| 3395 | }, |
| 3396 | }, |
| 3064 | 3397 | }; |
| 3065 | | if (omit_dynsym) { |
| 3398 | if (!have_dynsym) { |
| 3066 | 3399 | if (try elf.deleteOwnedDynsym(gsi)) { |
| 3067 | 3400 | apply_relocs = true; |
| 3068 | 3401 | } |
| ... | ... | @@ -3089,9 +3422,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3089 | 3422 | if (elf.ehdrType() != .REL) { |
| 3090 | 3423 | assert(elf.classifySymbolValue(.global(gsi)) != .dynamic); |
| 3091 | 3424 | } |
| 3092 | | gsi.deleteDynamicTargetRelocs(elf); |
| 3425 | |
| 3426 | if (try gsi.updateDynamicTargetRelocs(elf)) { |
| 3427 | apply_relocs = true; |
| 3428 | } |
| 3093 | 3429 | if (maybe_alias_gsi) |alias_gsi| { |
| 3094 | | alias_gsi.deleteDynamicTargetRelocs(elf); |
| 3430 | if (try alias_gsi.updateDynamicTargetRelocs(elf)) { |
| 3431 | apply_relocs = true; |
| 3432 | } |
| 3095 | 3433 | } |
| 3096 | 3434 | |
| 3097 | 3435 | if (apply_relocs) { |
| ... | ... | @@ -3335,10 +3673,8 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3335 | 3673 | } else { |
| 3336 | 3674 | if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| { |
| 3337 | 3675 | // We previously made a copy relocation, but now have deemed it unnecessary. |
| 3676 | try elf.deleteNode(copied_global_kv.value.node); |
| 3338 | 3677 | elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index); |
| 3339 | | // TODO: once `MappedFile` has a way to delete a node (so it can re-use the |
| 3340 | | // space), we should delete `copied_global_kv.value.node`, which is an |
| 3341 | | // "orphaned" `copied_global` node. |
| 3342 | 3678 | apply_relocs = true; |
| 3343 | 3679 | } |
| 3344 | 3680 | } |
| ... | ... | @@ -3352,10 +3688,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3352 | 3688 | _ = elf.unknown_globals.swapRemove(gsi); |
| 3353 | 3689 | } |
| 3354 | 3690 | |
| 3355 | | if (elf.classifySymbolValue(.global(gsi)) != .dynamic) { |
| 3356 | | gsi.deleteDynamicTargetRelocs(elf); |
| 3357 | | if (maybe_alias_gsi) |alias_gsi| { |
| 3358 | | alias_gsi.deleteDynamicTargetRelocs(elf); |
| 3691 | if (try gsi.updateDynamicTargetRelocs(elf)) { |
| 3692 | apply_relocs = true; |
| 3693 | } |
| 3694 | if (maybe_alias_gsi) |alias_gsi| { |
| 3695 | if (try alias_gsi.updateDynamicTargetRelocs(elf)) { |
| 3696 | apply_relocs = true; |
| 3359 | 3697 | } |
| 3360 | 3698 | } |
| 3361 | 3699 | |
| ... | ... | @@ -3478,15 +3816,12 @@ fn setGlobalSymbolBind( |
| 3478 | 3816 | /// undefined---their visibility values are combined to determine the resulting visibility. |
| 3479 | 3817 | /// |
| 3480 | 3818 | /// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes |
| 3481 | | /// to symbol visibility can affect the dynamic symbol table entry. If this function returns `true`, |
| 3482 | | /// then that `updateGlobalDynamic` call must have the `force_apply_relocs` argument set to `true`, |
| 3483 | | /// because this symbol's classification has changed (it is no longer a dynamic symbol) so |
| 3484 | | /// relocations targeting the symbol must be re-applied. |
| 3819 | /// to symbol visibility can affect the dynamic symbol table entry. |
| 3485 | 3820 | fn mergeGlobalSymbolVisibility( |
| 3486 | 3821 | elf: *Elf, |
| 3487 | 3822 | gsi: Symbol.Global.Index, |
| 3488 | 3823 | other_visibility: std.elf.STV, |
| 3489 | | ) bool { |
| 3824 | ) void { |
| 3490 | 3825 | const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) { |
| 3491 | 3826 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 3492 | 3827 | }; |
| ... | ... | @@ -3518,13 +3853,13 @@ fn mergeGlobalSymbolVisibility( |
| 3518 | 3853 | ), |
| 3519 | 3854 | } |
| 3520 | 3855 | |
| 3521 | | if (elf.ehdrType() == .REL) { |
| 3522 | | return false; |
| 3523 | | } |
| 3856 | if (elf.ehdrType() == .REL) return; |
| 3524 | 3857 | |
| 3525 | 3858 | // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or |
| 3526 | 3859 | // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to |
| 3527 | 3860 | // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output. |
| 3861 | // TODO: after the latest batch of refactors, I'm unsure if this logic should be here anymore. I |
| 3862 | // think it should probably live in `updateGlobalDynamic` instead. |
| 3528 | 3863 | const old_demoted: bool = switch (old_visibility) { |
| 3529 | 3864 | .INTERNAL, .HIDDEN => true, |
| 3530 | 3865 | .DEFAULT, .PROTECTED => false, |
| ... | ... | @@ -3542,16 +3877,6 @@ fn mergeGlobalSymbolVisibility( |
| 3542 | 3877 | } |
| 3543 | 3878 | elf.moveDemotedGlobal(gsi); |
| 3544 | 3879 | } |
| 3545 | | |
| 3546 | | const old_protected: bool = switch (old_visibility) { |
| 3547 | | .PROTECTED, .INTERNAL, .HIDDEN => true, |
| 3548 | | .DEFAULT => false, |
| 3549 | | }; |
| 3550 | | const new_protected: bool = switch (new_visibility) { |
| 3551 | | .PROTECTED, .INTERNAL, .HIDDEN => true, |
| 3552 | | .DEFAULT => false, |
| 3553 | | }; |
| 3554 | | return old_protected != new_protected; |
| 3555 | 3880 | } |
| 3556 | 3881 | /// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`), |
| 3557 | 3882 | /// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF |
| ... | ... | @@ -3613,10 +3938,8 @@ fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Erro |
| 3613 | 3938 | |
| 3614 | 3939 | var apply_relocs: bool = false; |
| 3615 | 3940 | if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| { |
| 3941 | try elf.deleteNode(copied_global_kv.value.node); |
| 3616 | 3942 | elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index); |
| 3617 | | // TODO: once `MappedFile` has a way to delete a node (so it can re-use the |
| 3618 | | // space), we should delete `copied_global_kv.value.node`, which is an |
| 3619 | | // "orphaned" `copied_global` node. |
| 3620 | 3943 | apply_relocs = true; |
| 3621 | 3944 | } |
| 3622 | 3945 | if (elf.plt.getIndex(gsi)) |plt_index| { |
| ... | ... | @@ -3930,85 +4253,52 @@ const Symbol = struct { |
| 3930 | 4253 | return null; |
| 3931 | 4254 | } |
| 3932 | 4255 | |
| 3933 | | /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation |
| 3934 | | /// entries, and adds `R_*_RELATIVE` relocation entries as needed. |
| 4256 | /// Updates the dynamic relocations associated with relocations targeting `gsi`, adding |
| 4257 | /// or removing them as needed based on the current classification of `gsi`. |
| 3935 | 4258 | /// |
| 3936 | | /// Asserts we are creating a DSO. |
| 3937 | | fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void { |
| 3938 | | if (elf.shndx.dynamic == .UNDEF) return; |
| 4259 | /// Returns `true` if any relocations were updated, in which case relocations targeting |
| 4260 | /// `gsi` must be re-applied by the caller. |
| 4261 | fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool { |
| 4262 | if (elf.shndx.dynamic == .UNDEF) return false; |
| 3939 | 4263 | const sym_id: Symbol.Id = .global(gsi); |
| 4264 | |
| 3940 | 4265 | switch (elf.classifySymbolValue(sym_id)) { |
| 3941 | 4266 | .static => { |
| 3942 | 4267 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and |
| 3943 | 4268 | !gsi.ptr(elf).status.any_static_relative_target_relocs) |
| 3944 | 4269 | { |
| 3945 | | return; |
| 4270 | return false; |
| 3946 | 4271 | } |
| 3947 | | gsi.ptr(elf).status.any_dynamic_target_relocs = false; |
| 3948 | | gsi.ptr(elf).status.any_static_relative_target_relocs = false; |
| 3949 | | gsi.ptr(elf).status.any_static_target_relocs = true; |
| 3950 | 4272 | }, |
| 3951 | 4273 | .static_relative => { |
| 3952 | 4274 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and |
| 3953 | 4275 | !gsi.ptr(elf).status.any_static_target_relocs) |
| 3954 | 4276 | { |
| 3955 | | return; |
| 4277 | return false; |
| 4278 | } |
| 4279 | }, |
| 4280 | .dynamic => { |
| 4281 | if (!gsi.ptr(elf).status.any_static_target_relocs and |
| 4282 | !gsi.ptr(elf).status.any_static_relative_target_relocs) |
| 4283 | { |
| 4284 | return false; |
| 3956 | 4285 | } |
| 3957 | | gsi.ptr(elf).status.any_dynamic_target_relocs = false; |
| 3958 | | gsi.ptr(elf).status.any_static_target_relocs = false; |
| 3959 | | gsi.ptr(elf).status.any_static_relative_target_relocs = true; |
| 3960 | 4286 | }, |
| 3961 | | // TODO: this function needs to support a symbol becoming dynamic which wasn't |
| 3962 | | // previously dynamic (and when it does, the function should be renamed to |
| 3963 | | // `updateTargetOutputRelocs`). But for now, this isn't supported. |
| 3964 | | .dynamic => unreachable, |
| 3965 | 4287 | } |
| 4288 | |
| 4289 | gsi.ptr(elf).status.any_static_target_relocs = false; |
| 4290 | gsi.ptr(elf).status.any_static_relative_target_relocs = false; |
| 4291 | gsi.ptr(elf).status.any_dynamic_target_relocs = false; |
| 4292 | |
| 3966 | 4293 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 3967 | 4294 | while (ri != .none) { |
| 3968 | 4295 | const reloc = ri.get(elf); |
| 3969 | 4296 | assert(reloc.target == sym_id); |
| 3970 | | reloc.deleteOutputRel(elf); |
| 4297 | try reloc.updateDynamicReloc(elf); |
| 3971 | 4298 | ri = reloc.next; |
| 3972 | 4299 | } |
| 3973 | | switch (elf.classifySymbolValue(sym_id)) { |
| 3974 | | .static => return, |
| 3975 | | .static_relative => {}, |
| 3976 | | .dynamic => unreachable, |
| 3977 | | } |
| 3978 | | // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed. |
| 3979 | | ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 3980 | | while (ri != .none) { |
| 3981 | | const reloc = ri.get(elf); |
| 3982 | | ri = reloc.next; |
| 3983 | | assert(reloc.target == sym_id); |
| 3984 | | switch (reloc.type.target) { |
| 3985 | | // Only relocations which resolve to absolute addresses require runtime |
| 3986 | | // `R_*_RELATIVE` relocations. |
| 3987 | | .special, |
| 3988 | | .pltrel, |
| 3989 | | .rel, |
| 3990 | | .dtpoff, |
| 3991 | | .tpoff, |
| 3992 | | .size, |
| 3993 | | => continue, |
| 3994 | | |
| 3995 | | .abs, .pltabs => {}, |
| 3996 | | } |
| 3997 | | if (!reloc.type.action.simple.dest.isAddr(elf)) continue; |
| 3998 | | const node = reloc.node.unwrap().?; |
| 3999 | | switch (elf.nodeWantsDsoRelocation(node)) { |
| 4000 | | .no => continue, |
| 4001 | | .yes_textrel => elf.textrel_count += 1, |
| 4002 | | .yes => {}, |
| 4003 | | } |
| 4004 | | // There is capacity for a relocation because we just deleted one earlier. |
| 4005 | | reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 4006 | | .type = .relative(elf), |
| 4007 | | .offset = elf.getNodeVAddr(node) + reloc.offset, |
| 4008 | | .raw_sym_index = 0, |
| 4009 | | .addend = 0, |
| 4010 | | }).toOptional(); |
| 4011 | | } |
| 4300 | |
| 4301 | return true; |
| 4012 | 4302 | } |
| 4013 | 4303 | |
| 4014 | 4304 | const Optional = enum(u32) { |
| ... | ... | @@ -4139,7 +4429,20 @@ const Symbol = struct { |
| 4139 | 4429 | } |
| 4140 | 4430 | |
| 4141 | 4431 | fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void { |
| 4432 | const trace = tracy.trace(@src()); |
| 4433 | defer trace.end(); |
| 4434 | |
| 4142 | 4435 | const sym_index = sym_id.index(elf); |
| 4436 | |
| 4437 | trace.addText(name: { |
| 4438 | // There isn't a general `name` method on `Symbol.Id` because it would be incorrect |
| 4439 | // to ever actually care about the name of a local symbol. |
| 4440 | const name: String(.strtab) = switch (elf.symPtr(sym_index)) { |
| 4441 | inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)), |
| 4442 | }; |
| 4443 | break :name name.slice(elf); |
| 4444 | }); |
| 4445 | |
| 4143 | 4446 | switch (elf.symPtr(sym_index)) { |
| 4144 | 4447 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 4145 | 4448 | } |
| ... | ... | @@ -4164,14 +4467,12 @@ const Symbol = struct { |
| 4164 | 4467 | } |
| 4165 | 4468 | |
| 4166 | 4469 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 4167 | | if (elf.ehdrType() != .REL) { |
| 4168 | | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4169 | | while (ri != .none) { |
| 4170 | | const reloc = ri.get(elf); |
| 4171 | | assert(reloc.target == sym_id); |
| 4172 | | reloc.apply(elf); |
| 4173 | | ri = reloc.next; |
| 4174 | | } |
| 4470 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4471 | while (ri != .none) { |
| 4472 | const reloc = ri.get(elf); |
| 4473 | assert(reloc.target == sym_id); |
| 4474 | reloc.apply(elf); |
| 4475 | ri = reloc.next; |
| 4175 | 4476 | } |
| 4176 | 4477 | |
| 4177 | 4478 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { |
| ... | ... | @@ -4251,61 +4552,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 4251 | 4552 | .EXEC => false, |
| 4252 | 4553 | }; |
| 4253 | 4554 | |
| 4254 | | if (elf.shndx.dynamic == .UNDEF) { |
| 4255 | | // This is a static non-PIE executable---every symbol has a statically known value. |
| 4256 | | return .static; |
| 4257 | | } |
| 4258 | | |
| 4259 | 4555 | const resolved_sym = sym.resolveAlias(elf); |
| 4260 | 4556 | |
| 4261 | | const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) { |
| 4557 | const symtab_shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) { |
| 4262 | 4558 | inline else => |sym_ptr| .{ |
| 4263 | 4559 | .fromSection(elf.targetLoad(&sym_ptr.shndx)), |
| 4264 | 4560 | elf.targetLoad(&sym_ptr.other).visibility, |
| 4265 | 4561 | }, |
| 4266 | 4562 | }; |
| 4267 | 4563 | |
| 4268 | | switch (resolved_sym.unwrap()) { |
| 4269 | | .local => { |
| 4270 | | assert(shndx != .UNDEF); |
| 4564 | const shndx: Section.Index = switch (resolved_sym.unwrap()) { |
| 4565 | .local => shndx: { |
| 4566 | assert(symtab_shndx != .UNDEF); |
| 4271 | 4567 | assert(visibility == .DEFAULT); |
| 4568 | break :shndx symtab_shndx; |
| 4272 | 4569 | }, |
| 4273 | | .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) { |
| 4274 | | // An unprotected symbol in a DSO which is not an executable is subject to runtime |
| 4275 | | // preemption, so a dynamic relocation is required for it even if we have a definition. |
| 4276 | | return .dynamic; |
| 4277 | | } else if (elf.copied_globals.contains(gsi)) { |
| 4278 | | // This becomes a locally-defined symbol in `.data`. |
| 4279 | | return if (runtime_load_addr) .static_relative else .static; |
| 4280 | | }, |
| 4281 | | } |
| 4570 | .global => |gsi| shndx: { |
| 4571 | const shndx: Section.Index = switch (symtab_shndx) { |
| 4572 | .UNDEF => if (elf.copied_globals.contains(gsi)) .data else .UNDEF, |
| 4573 | else => |shndx| shndx, |
| 4574 | }; |
| 4282 | 4575 | |
| 4283 | | return switch (shndx) { |
| 4284 | | .UNDEF => switch (visibility) { |
| 4285 | | .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) { |
| 4286 | | assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section |
| 4287 | | // This is a static PIE---we'll see a definition at some point, so there'd be no |
| 4288 | | // point in adding an invalid dynamic relocation (the only valid dynamic relation |
| 4289 | | // type in a static PIE is `R_*_RELATIVE`). |
| 4290 | | return .static; |
| 4291 | | } else .dynamic, // external symbol |
| 4576 | // Check for dynamic symbols before falling into the static path. |
| 4577 | if (gsi.dynsymIndex(elf) != null and visibility == .DEFAULT) { |
| 4578 | if (shndx == .UNDEF) { |
| 4579 | // There is no local definition of this symbol. |
| 4580 | return .dynamic; |
| 4581 | } else if (comp.config.output_mode != .Exe) { |
| 4582 | // There is a local definition, but it is preemptible at runtime. |
| 4583 | return .dynamic; |
| 4584 | } |
| 4585 | } |
| 4292 | 4586 | |
| 4293 | | // If the symbol *cannot* be external, then there's no point making a dynamic relocation |
| 4294 | | // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`. |
| 4295 | | .INTERNAL, .HIDDEN, .PROTECTED => .static, |
| 4587 | break :shndx shndx; |
| 4296 | 4588 | }, |
| 4589 | }; |
| 4297 | 4590 | |
| 4298 | | .ABS => .static, |
| 4591 | if (runtime_load_addr and |
| 4592 | shndx != .UNDEF and |
| 4593 | shndx != .ABS and |
| 4594 | shndx.flags(elf).ALLOC and |
| 4595 | !shndx.flags(elf).TLS) |
| 4596 | { |
| 4597 | // This symbol's value is a virtual address relative to a runtime-known load address. |
| 4598 | return .static_relative; |
| 4599 | } |
| 4299 | 4600 | |
| 4300 | | else => if (runtime_load_addr and |
| 4301 | | shndx.flags(elf).ALLOC and |
| 4302 | | !shndx.flags(elf).TLS) |
| 4303 | | { |
| 4304 | | return .static_relative; |
| 4305 | | } else { |
| 4306 | | return .static; |
| 4307 | | }, |
| 4308 | | }; |
| 4601 | return .static; |
| 4309 | 4602 | } |
| 4310 | 4603 | |
| 4311 | 4604 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| ... | ... | @@ -4848,6 +5141,7 @@ fn create( |
| 4848 | 5141 | .section_by_name = .empty, |
| 4849 | 5142 | .changed_symtab_index = .empty, |
| 4850 | 5143 | .textrel_count = 0, |
| 5144 | .entry_gsi = undefined, |
| 4851 | 5145 | |
| 4852 | 5146 | .dwarf = .init(&elf.base, switch (comp.config.debug_format) { |
| 4853 | 5147 | .strip => .@"32", // for .eh_frame |
| ... | ... | @@ -4860,10 +5154,12 @@ fn create( |
| 4860 | 5154 | .dwarf_addr = .{ |
| 4861 | 5155 | .first_target_reloc = .none, |
| 4862 | 5156 | .symbol_relocs = .empty, |
| 5157 | .pending_reloc_index = 0, |
| 4863 | 5158 | }, |
| 4864 | 5159 | .dwarf_str_offsets = .{ |
| 4865 | 5160 | .first_target_reloc = .none, |
| 4866 | 5161 | .node_relocs = .empty, |
| 5162 | .pending_reloc_index = 0, |
| 4867 | 5163 | }, |
| 4868 | 5164 | .dwarf_units = &.{}, |
| 4869 | 5165 | .dwarf_consts = .empty, |
| ... | ... | @@ -4873,6 +5169,8 @@ fn create( |
| 4873 | 5169 | |
| 4874 | 5170 | .overflowed_reloc_count = 0, |
| 4875 | 5171 | .misaligned_reloc_count = 0, |
| 5172 | .illegal_dynamic_type_reloc_count = 0, |
| 5173 | .illegal_dynamic_section_reloc_count = 0, |
| 4876 | 5174 | |
| 4877 | 5175 | .const_prog_node = .none, |
| 4878 | 5176 | .input_prog_node = .none, |
| ... | ... | @@ -5078,9 +5376,10 @@ fn initHeaders( |
| 5078 | 5376 | defer phnum += 1; |
| 5079 | 5377 | break :phndx phnum; |
| 5080 | 5378 | } else undefined, |
| 5081 | | // `data` must be assigned after all other loadable segments so that it has the greatest |
| 5082 | | // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions` |
| 5083 | | // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC). |
| 5379 | // `data` must be assigned after all other loadable segments so that it has the last |
| 5380 | // phndx of the loadable segments. This causes `updateSegmentLoadAddresses` to put |
| 5381 | // that segment's virtual address after the others, which is a de-facto requirement |
| 5382 | // on SPARC. More details are in a comment in `updateSegmentLoadAddresses`. |
| 5084 | 5383 | .data = phndx: { |
| 5085 | 5384 | defer phnum += 1; |
| 5086 | 5385 | break :phndx phnum; |
| ... | ... | @@ -5214,7 +5513,7 @@ fn initHeaders( |
| 5214 | 5513 | // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node |
| 5215 | 5514 | .alignment = node_block_align.max(addr_align), |
| 5216 | 5515 | .moved = true, |
| 5217 | | .bubbles_moved = false, |
| 5516 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5218 | 5517 | }), .{ .segment = phndx.rodata }); |
| 5219 | 5518 | elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata); |
| 5220 | 5519 | |
| ... | ... | @@ -5223,14 +5522,13 @@ fn initHeaders( |
| 5223 | 5522 | .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above |
| 5224 | 5523 | .moved = true, |
| 5225 | 5524 | .resized = true, |
| 5226 | | .bubbles_moved = false, |
| 5227 | 5525 | }), .{ .segment = phndx.phdr }); |
| 5228 | 5526 | elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr); |
| 5229 | 5527 | |
| 5230 | 5528 | elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ |
| 5231 | 5529 | .alignment = node_block_align, |
| 5232 | 5530 | .moved = true, |
| 5233 | | .bubbles_moved = false, |
| 5531 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5234 | 5532 | }), .{ .segment = phndx.text }); |
| 5235 | 5533 | elf.phdrs.items[phndx.text] = .wrap(elf.ni.text); |
| 5236 | 5534 | |
| ... | ... | @@ -5238,7 +5536,7 @@ fn initHeaders( |
| 5238 | 5536 | // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node |
| 5239 | 5537 | .alignment = node_block_align.max(addr_align), |
| 5240 | 5538 | .moved = true, |
| 5241 | | .bubbles_moved = false, |
| 5539 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5242 | 5540 | }), .{ .segment = phndx.data }); |
| 5243 | 5541 | elf.phdrs.items[phndx.data] = .wrap(elf.ni.data); |
| 5244 | 5542 | |
| ... | ... | @@ -5246,7 +5544,7 @@ fn initHeaders( |
| 5246 | 5544 | try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ |
| 5247 | 5545 | .alignment = node_block_align, |
| 5248 | 5546 | .moved = true, |
| 5249 | | .bubbles_moved = false, |
| 5547 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5250 | 5548 | }), |
| 5251 | 5549 | .{ .segment = phndx.plt }, |
| 5252 | 5550 | )); |
| ... | ... | @@ -5256,7 +5554,6 @@ fn initHeaders( |
| 5256 | 5554 | // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above. |
| 5257 | 5555 | .alignment = node_block_align.max(addr_align), |
| 5258 | 5556 | .moved = true, |
| 5259 | | .bubbles_moved = false, |
| 5260 | 5557 | }), .{ .segment = phndx.relro }); |
| 5261 | 5558 | elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro); |
| 5262 | 5559 | |
| ... | ... | @@ -5265,7 +5562,7 @@ fn initHeaders( |
| 5265 | 5562 | try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ |
| 5266 | 5563 | .alignment = node_block_align, |
| 5267 | 5564 | .moved = true, |
| 5268 | | .bubbles_moved = false, |
| 5565 | .bubbles_moved = false, // see TLS segment handling in `flushMoved` |
| 5269 | 5566 | }), |
| 5270 | 5567 | .{ .segment = phndx.tls }, |
| 5271 | 5568 | )); |
| ... | ... | @@ -5358,25 +5655,10 @@ fn initHeaders( |
| 5358 | 5655 | const ElfN = ct_class.ElfN(); |
| 5359 | 5656 | const target_endian = elf.targetEndian(); |
| 5360 | 5657 | |
| 5361 | | populate_phdrs: { |
| 5362 | | // Initially we will give every `PT_LOAD` segment this address. When we re-allocate |
| 5363 | | // segments in the virtual address space in `flushMoved` and `flushResized`, we will |
| 5364 | | // move some segments to higher addresses to prevent overlap. This address therefore |
| 5365 | | // becomes the image's "base address"; i.e. the first `PT_LOAD` segment will start |
| 5366 | | // at this address. The base address could eventually end up higher than this due to |
| 5367 | | // how we re-allocate the address space, but never lower. |
| 5368 | | const base_vaddr: u64 = switch (@"type") { |
| 5369 | | .REL => break :populate_phdrs, |
| 5370 | | .DYN => 0, |
| 5371 | | .EXEC => switch (machine) { |
| 5372 | | .AARCH64 => 0x200000, |
| 5373 | | .LOONGARCH => 0x10000, |
| 5374 | | .PPC64 => 0x10000000, |
| 5375 | | .RISCV => 0x10000, |
| 5376 | | .SPARCV9 => 0x100000, |
| 5377 | | .X86_64 => 0x200000, |
| 5378 | | }, |
| 5379 | | }; |
| 5658 | if (@"type" != .REL) { |
| 5659 | // Initially we will give every `PT_LOAD` segment the base address. They will be |
| 5660 | // moved to non-overlapping addresses by `updateSegmentLoadAddresses`. |
| 5661 | const base_vaddr: u64 = elf.baseLoadableSegmentVAddr(); |
| 5380 | 5662 | |
| 5381 | 5663 | // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the |
| 5382 | 5664 | // binary, their *nodes* are not aligned to this boundary---ELF only requires that |
| ... | ... | @@ -5683,7 +5965,6 @@ fn initHeaders( |
| 5683 | 5965 | .size = interp.len + 1, |
| 5684 | 5966 | .moved = true, |
| 5685 | 5967 | .resized = true, |
| 5686 | | .bubbles_moved = false, |
| 5687 | 5968 | }), |
| 5688 | 5969 | .{ .segment = phndx.interp }, |
| 5689 | 5970 | ); |
| ... | ... | @@ -5705,7 +5986,6 @@ fn initHeaders( |
| 5705 | 5986 | try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{ |
| 5706 | 5987 | .alignment = addr_align, |
| 5707 | 5988 | .moved = true, |
| 5708 | | .bubbles_moved = false, |
| 5709 | 5989 | }), |
| 5710 | 5990 | .{ .segment = phndx.dynamic }, |
| 5711 | 5991 | ); |
| ... | ... | @@ -5959,7 +6239,6 @@ fn initHeaders( |
| 5959 | 6239 | .size = @sizeOf(Dwarf.EhFrameHdr), |
| 5960 | 6240 | .alignment = .@"4", |
| 5961 | 6241 | .moved = true, |
| 5962 | | .bubbles_moved = false, |
| 5963 | 6242 | }), |
| 5964 | 6243 | .{ .segment = phndx.gnu_eh_frame }, |
| 5965 | 6244 | ); |
| ... | ... | @@ -6272,6 +6551,31 @@ fn initHeaders( |
| 6272 | 6551 | .code_view => unreachable, |
| 6273 | 6552 | } |
| 6274 | 6553 | |
| 6554 | // If we have an entry point symbol, create the undefined reference now. |
| 6555 | elf.entry_gsi = entry: switch (elf.options.entry) { |
| 6556 | .default => switch (comp.config.output_mode) { |
| 6557 | .Exe => continue :entry .enabled, |
| 6558 | .Lib, .Obj => continue :entry .disabled, |
| 6559 | }, |
| 6560 | .enabled => continue :entry .{ .named = "_start" }, |
| 6561 | .disabled => .none, |
| 6562 | .named => |sym_name| .wrap(elf.addGlobalSymbol(.{ |
| 6563 | .node = .none, |
| 6564 | .name = sym_name, |
| 6565 | .value = 0, |
| 6566 | .size = 0, |
| 6567 | .type = .NOTYPE, |
| 6568 | .bind = .strong, |
| 6569 | .visibility = .DEFAULT, |
| 6570 | .shndx = .UNDEF, |
| 6571 | }) catch |err| switch (err) { |
| 6572 | error.MultipleDefinitions => unreachable, // shndx is undef |
| 6573 | error.MultipleDefaultVersions => unreachable, // not a versioned symbol |
| 6574 | error.UndefinedDefaultVersion => unreachable, // not a versioned symbol |
| 6575 | else => |e| return e, |
| 6576 | }), |
| 6577 | }; |
| 6578 | |
| 6275 | 6579 | assert(elf.nodes.len == expected_nodes_len); |
| 6276 | 6580 | assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF |
| 6277 | 6581 | |
| ... | ... | @@ -6303,21 +6607,7 @@ fn initHeaders( |
| 6303 | 6607 | }, |
| 6304 | 6608 | }; |
| 6305 | 6609 | |
| 6306 | | if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) { |
| 6307 | | .none => {}, |
| 6308 | | .data_last => switch (elf.phdrSlice()) { |
| 6309 | | inline else => |phdr| { |
| 6310 | | // Ensure that the segment after `.data` (if any) is not a loadable segment. |
| 6311 | | const next_phndx = phndx.data + 1; |
| 6312 | | if (next_phndx < phdr.len) { |
| 6313 | | switch (elf.targetLoad(&phdr[next_phndx].type)) { |
| 6314 | | .NULL, .LOAD => unreachable, // data segment should be the last loadable segment |
| 6315 | | else => {}, |
| 6316 | | } |
| 6317 | | } |
| 6318 | | }, |
| 6319 | | }, |
| 6320 | | }; |
| 6610 | try elf.updateSegmentLoadAddresses(); |
| 6321 | 6611 | } |
| 6322 | 6612 | |
| 6323 | 6613 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { |
| ... | ... | @@ -6482,43 +6772,6 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6482 | 6772 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| 6483 | 6773 | return parent_vaddr + offset; |
| 6484 | 6774 | } |
| 6485 | | fn computeNodeSectionOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6486 | | const parent_ni = ni.parent(&elf.mf).unwrap().?; |
| 6487 | | const parent_section_offset = parent_section_offset: switch (elf.getNode(parent_ni)) { |
| 6488 | | .deleted, |
| 6489 | | .archive, |
| 6490 | | .archive_header, |
| 6491 | | .archive_input_member, |
| 6492 | | .archive_elf_member_header, |
| 6493 | | .elf, |
| 6494 | | .ehdr, |
| 6495 | | .shdr, |
| 6496 | | .segment, |
| 6497 | | => unreachable, |
| 6498 | | .section, .section_manual_size => 0, |
| 6499 | | .input_section, .copied_global => unreachable, |
| 6500 | | .nav, .uav, .lazy_code, .lazy_const_data => unreachable, |
| 6501 | | .debug_shared, .debug_addr, .eh_frame_footer, .debug_str_offsets, .unit_padding => unreachable, |
| 6502 | | .unit_frame, .unit_debug_info, .unit_debug_line => { |
| 6503 | | const parent_section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); |
| 6504 | | break :parent_section_offset parent_section_offset; |
| 6505 | | }, |
| 6506 | | .unit_frame_cie, |
| 6507 | | .unit_debug_info_header, |
| 6508 | | .unit_debug_info_footer, |
| 6509 | | .unit_debug_line_header, |
| 6510 | | .unit_debug_rnglists, |
| 6511 | | .const_debug_info, |
| 6512 | | .global_debug_info, |
| 6513 | | .func_frame_fde, |
| 6514 | | .func_debug_info, |
| 6515 | | .func_debug_line, |
| 6516 | | .decl_debug_info, |
| 6517 | | => unreachable, |
| 6518 | | }; |
| 6519 | | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| 6520 | | return parent_section_offset + offset; |
| 6521 | | } |
| 6522 | 6775 | fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6523 | 6776 | return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset; |
| 6524 | 6777 | } |
| ... | ... | @@ -6660,7 +6913,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 6660 | 6913 | fn flushMovedNodeRelocs( |
| 6661 | 6914 | elf: *Elf, |
| 6662 | 6915 | node: MappedFile.Node.Index, |
| 6663 | | node_vaddr: u64, |
| 6664 | 6916 | opts: struct { |
| 6665 | 6917 | first_symbol_reloc: SymbolReloc.Index = .none, |
| 6666 | 6918 | skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, |
| ... | ... | @@ -6676,7 +6928,7 @@ fn flushMovedNodeRelocs( |
| 6676 | 6928 | if (reloc.node == opts.skip_symbol_relocs) continue; |
| 6677 | 6929 | break; |
| 6678 | 6930 | } |
| 6679 | | reloc.flushMovedNode(elf, node_vaddr); |
| 6931 | reloc.apply(elf); |
| 6680 | 6932 | } |
| 6681 | 6933 | } |
| 6682 | 6934 | |
| ... | ... | @@ -6687,7 +6939,7 @@ fn flushMovedNodeRelocs( |
| 6687 | 6939 | if (reloc.node == opts.skip_node_relocs) continue; |
| 6688 | 6940 | break; |
| 6689 | 6941 | } |
| 6690 | | reloc.flushMovedNode(elf, node_vaddr); |
| 6942 | reloc.apply(elf); |
| 6691 | 6943 | } |
| 6692 | 6944 | } |
| 6693 | 6945 | |
| ... | ... | @@ -6755,6 +7007,21 @@ fn ehdrType(elf: *const Elf) EhdrType { |
| 6755 | 7007 | } |
| 6756 | 7008 | } |
| 6757 | 7009 | |
| 7010 | fn baseLoadableSegmentVAddr(elf: *const Elf) u64 { |
| 7011 | return switch (elf.ehdrType()) { |
| 7012 | .REL => unreachable, |
| 7013 | .DYN => 0, |
| 7014 | .EXEC => switch (elf.ehdrMachine()) { |
| 7015 | .AARCH64 => 0x200000, |
| 7016 | .LOONGARCH => 0x10000, |
| 7017 | .PPC64 => 0x10000000, |
| 7018 | .RISCV => 0x10000, |
| 7019 | .SPARCV9 => 0x100000, |
| 7020 | .X86_64 => 0x200000, |
| 7021 | }, |
| 7022 | }; |
| 7023 | } |
| 7024 | |
| 6758 | 7025 | fn targetPtrSize(elf: *const Elf) u8 { |
| 6759 | 7026 | return elf.identClass().size(); |
| 6760 | 7027 | } |
| ... | ... | @@ -6873,30 +7140,6 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { |
| 6873 | 7140 | // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`. |
| 6874 | 7141 | }; |
| 6875 | 7142 | } |
| 6876 | | /// Specifies any restrictions the current target has regarding how segments are ordered in the |
| 6877 | | /// virtual address space. Most targets do not have any such restrictions. |
| 6878 | | fn targetSegmentLoadAddressRestrictions(elf: *const Elf) enum { |
| 6879 | | none, |
| 6880 | | /// The "mutable data" segment must be the last loadable segment in the virtual address space. |
| 6881 | | data_last, |
| 6882 | | } { |
| 6883 | | return switch (elf.ehdrMachine()) { |
| 6884 | | .AARCH64, |
| 6885 | | .PPC64, |
| 6886 | | .RISCV, |
| 6887 | | .X86_64, |
| 6888 | | .LOONGARCH, |
| 6889 | | => .none, |
| 6890 | | |
| 6891 | | // SPARC uses `R_SPARC_PC{10,22}` relocations to construct pointers to the GOT, but these |
| 6892 | | // relocations write an *unsigned* PC-relative offset. This cannot even be worked around by |
| 6893 | | // using a larger code model, because the crt `_start` assembly always uses these specific |
| 6894 | | // relocations. Therefore, to avoid relocation errors, all code must appear before the GOT |
| 6895 | | // in the virtual address space. The easiest way for us to do that is to ensure that the |
| 6896 | | // "mutable data" segment, containing the GOT, is the last segment in the address space. |
| 6897 | | .SPARCV9 => .data_last, |
| 6898 | | }; |
| 6899 | | } |
| 6900 | 7143 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 6901 | 7144 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 6902 | 7145 | const Child = pointer_ty.child; |
| ... | ... | @@ -7326,6 +7569,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!v |
| 7326 | 7569 | .{elf.mf.io_err.?}, |
| 7327 | 7570 | ), |
| 7328 | 7571 | }; |
| 7572 | try elf.updateSegmentLoadAddresses(); |
| 7329 | 7573 | } |
| 7330 | 7574 | fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void { |
| 7331 | 7575 | const comp = elf.base.comp; |
| ... | ... | @@ -8605,6 +8849,7 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { |
| 8605 | 8849 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), |
| 8606 | 8850 | else => |e| return e, |
| 8607 | 8851 | }; |
| 8852 | try elf.updateSegmentLoadAddresses(); |
| 8608 | 8853 | } |
| 8609 | 8854 | fn prelinkInner(elf: *Elf) Error!void { |
| 8610 | 8855 | const comp = elf.base.comp; |
| ... | ... | @@ -8659,24 +8904,27 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 8659 | 8904 | if (comp.zcu) |_| self_hosted_codegen: { |
| 8660 | 8905 | if (comp.config.use_llvm) break :self_hosted_codegen; |
| 8661 | 8906 | |
| 8662 | | // We're using self-hosted codegen---add an input representing the Zig "object". |
| 8663 | | try elf.inputs.ensureUnusedCapacity(gpa, 1); |
| 8664 | | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name}); |
| 8665 | | defer gpa.free(zcu_name); |
| 8666 | | const zcu_file_symbol = try elf.addLocalSymbol(.{ |
| 8667 | | .node = .none, |
| 8668 | | .name = zcu_name, |
| 8669 | | .value = 0, |
| 8670 | | .size = 0, |
| 8671 | | .type = .FILE, |
| 8672 | | .shndx = .ABS, |
| 8673 | | }); |
| 8674 | | elf.inputs.addOneAssumeCapacity().* = .{ |
| 8675 | | .path = elf.base.emit, |
| 8676 | | .member = null, |
| 8677 | | .extra = .{ .file_symbol = zcu_file_symbol }, |
| 8678 | | }; |
| 8679 | | elf.input_pending_index += 1; |
| 8907 | if (elf.archive == null) { |
| 8908 | // Self-hosted codegen is logically emitting a relocatable which is then immediately |
| 8909 | // being linked into the output file, so add that fictional Zig object file input. |
| 8910 | try elf.inputs.ensureUnusedCapacity(gpa, 1); |
| 8911 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name}); |
| 8912 | defer gpa.free(zcu_name); |
| 8913 | const zcu_file_symbol = try elf.addLocalSymbol(.{ |
| 8914 | .node = .none, |
| 8915 | .name = zcu_name, |
| 8916 | .value = 0, |
| 8917 | .size = 0, |
| 8918 | .type = .FILE, |
| 8919 | .shndx = .ABS, |
| 8920 | }); |
| 8921 | elf.inputs.addOneAssumeCapacity().* = .{ |
| 8922 | .path = elf.base.emit, |
| 8923 | .member = null, |
| 8924 | .extra = .{ .file_symbol = zcu_file_symbol }, |
| 8925 | }; |
| 8926 | elf.input_pending_index += 1; |
| 8927 | } |
| 8680 | 8928 | |
| 8681 | 8929 | switch (elf.shndx.debug_addr) { |
| 8682 | 8930 | .UNDEF => {}, |
| ... | ... | @@ -8758,6 +9006,7 @@ pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void { |
| 8758 | 9006 | .{elf.mf.io_err.?}, |
| 8759 | 9007 | ), |
| 8760 | 9008 | }; |
| 9009 | try elf.updateSegmentLoadAddresses(); |
| 8761 | 9010 | } |
| 8762 | 9011 | fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void { |
| 8763 | 9012 | const gpa = zcu.gpa; |
| ... | ... | @@ -9126,7 +9375,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 9126 | 9375 | .size = opts.node_align.forward(opts.size), |
| 9127 | 9376 | .alignment = opts.addralign.max(opts.node_align), |
| 9128 | 9377 | .resized = opts.size > 0, |
| 9129 | | .bubbles_moved = opts.flags.ALLOC, |
| 9378 | .bubbles_moved = opts.flags.ALLOC, // see handling in `flushMoved` |
| 9130 | 9379 | }), |
| 9131 | 9380 | if (opts.manual_size) .{ .section_manual_size = shndx } else .{ .section = shndx }, |
| 9132 | 9381 | )), |
| ... | ... | @@ -9265,6 +9514,7 @@ fn addRelocAssumeCapacity( |
| 9265 | 9514 | .rela_index = rela_index.toOptional(), |
| 9266 | 9515 | .result = .ok, |
| 9267 | 9516 | }); |
| 9517 | ri.get(elf).apply(elf); |
| 9268 | 9518 | }, |
| 9269 | 9519 | .DYN, .EXEC => switch (elf.ehdrMachine()) { |
| 9270 | 9520 | .AARCH64 => switch (@"type".AARCH64) { |
| ... | ... | @@ -9622,18 +9872,21 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9622 | 9872 | ) Error!void { |
| 9623 | 9873 | assert(elf.ehdrType() != .REL); |
| 9624 | 9874 | |
| 9625 | | const rela_index: Section.RelaIndex.Optional = r: { |
| 9626 | | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| 9627 | | |
| 9628 | | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to |
| 9629 | | // determine the vaddr of `node`. |
| 9630 | | const node_vaddr = elf.getNodeVAddr(node); |
| 9631 | | |
| 9632 | | // If this is `true`, we will try to create a copy relocation for the target symbol if it is |
| 9633 | | // not locally defined. If the relocation value is always computed from the target symbol's |
| 9634 | | // value (even for an external target symbol), and if the target symbol might be of type |
| 9635 | | // STT_OBJECT, this should probably be `true`. |
| 9636 | | const try_copy_reloc: bool = switch (@"type".target) { |
| 9875 | request_static_target: { |
| 9876 | if (elf.shndx.dynamic == .UNDEF) break :request_static_target; |
| 9877 | const gsi: Symbol.Global.Index = switch (target.unwrap()) { |
| 9878 | .local => break :request_static_target, |
| 9879 | .global => |gsi| gsi, |
| 9880 | }; |
| 9881 | // The target symbol may be dynamic, in which case its value cannot be resolved statically. |
| 9882 | // However, there are strategies (such as copy relocations) the linker can employ which can |
| 9883 | // effectively convert dynamic symbols into static symbols. If statically resolving this |
| 9884 | // relocation would require knowing the symbol value, we can set a flag so that those |
| 9885 | // strategies are used when possible to make this relocation statically resolvable. |
| 9886 | // |
| 9887 | // (This `switch` should return `false` for TLS relocations, because we can't do anything |
| 9888 | // about TLS symbols being dynamic.) |
| 9889 | const want_static_value: bool = switch (@"type".target) { |
| 9637 | 9890 | .rel, .abs => true, |
| 9638 | 9891 | |
| 9639 | 9892 | .pltrel, |
| ... | ... | @@ -9662,92 +9915,13 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9662 | 9915 | => false, |
| 9663 | 9916 | }, |
| 9664 | 9917 | }; |
| 9665 | | if (try_copy_reloc) switch (target.unwrap()) { |
| 9666 | | .local => {}, |
| 9667 | | .global => |gsi| if (!gsi.ptr(elf).status.want_static_value) { |
| 9668 | | gsi.ptr(elf).status.want_static_value = true; |
| 9669 | | try elf.updateGlobalDynamic(gsi, false); |
| 9670 | | }, |
| 9671 | | }; |
| 9672 | | |
| 9673 | | classify: switch (elf.classifySymbolValue(target)) { |
| 9674 | | .static => { |
| 9675 | | switch (target.unwrap()) { |
| 9676 | | .local => {}, |
| 9677 | | .global => |gsi| gsi.ptr(elf).status.any_static_target_relocs = true, |
| 9678 | | } |
| 9679 | | break :r .none; |
| 9680 | | }, |
| 9681 | | .static_relative => { |
| 9682 | | switch (target.unwrap()) { |
| 9683 | | .local => {}, |
| 9684 | | .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true, |
| 9685 | | } |
| 9686 | | switch (@"type".target) { |
| 9687 | | // Only relocations which resolve to absolute addresses require runtime |
| 9688 | | // `R_*_RELATIVE` relocations. |
| 9689 | | .special, |
| 9690 | | .pltrel, |
| 9691 | | .rel, |
| 9692 | | .dtpoff, |
| 9693 | | .tpoff, |
| 9694 | | .size, |
| 9695 | | => break :r .none, |
| 9918 | if (!want_static_value) break :request_static_target; |
| 9696 | 9919 | |
| 9697 | | .abs, .pltabs => {}, |
| 9698 | | } |
| 9699 | | if (!@"type".action.simple.dest.isAddr(elf)) break :r .none; |
| 9700 | | switch (elf.nodeWantsDsoRelocation(node)) { |
| 9701 | | .no => break :r .none, |
| 9702 | | .yes => {}, |
| 9703 | | .yes_textrel => elf.textrel_count += 1, |
| 9704 | | } |
| 9705 | | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 9706 | | .type = .relative(elf), |
| 9707 | | .offset = node_vaddr + offset, |
| 9708 | | .raw_sym_index = 0, |
| 9709 | | .addend = 0, |
| 9710 | | }).toOptional(); |
| 9711 | | }, |
| 9712 | | .dynamic => { |
| 9713 | | target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true; |
| 9714 | | const dynamic_reloc_type: MachineRelocType = switch (@"type".target) { |
| 9715 | | // PLT relocations targeting dynamic symbols actually target that symbol's PLT |
| 9716 | | // entry, so we should emit an `R_*_RELATIVE` relocation instead. |
| 9717 | | .pltabs => continue :classify .static_relative, |
| 9718 | | // ...although PC-relative PLT relocations don't even need that! |
| 9719 | | .pltrel => break :r .none, |
| 9720 | | // Weird sizes or computations are not supported as runtime relocations. |
| 9721 | | .special => break :r .none, |
| 9722 | | // Relative addresses are not supported as runtime relocations. |
| 9723 | | .rel => break :r .none, |
| 9724 | | |
| 9725 | | // On the few targets supporting size relocations, they are valid at runtime. |
| 9726 | | .size => switch (@"type".action.simple.dest) { |
| 9727 | | .@"32" => MachineRelocType.size32(elf) orelse break :r .none, |
| 9728 | | .@"64" => MachineRelocType.size64(elf) orelse break :r .none, |
| 9729 | | else => break :r .none, |
| 9730 | | }, |
| 9731 | | // Absolute addresses and TLS offsets can be lowered at runtime provided they |
| 9732 | | // are address-sized. |
| 9733 | | .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none, |
| 9734 | | .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none, |
| 9735 | | .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none, |
| 9736 | | }; |
| 9737 | | switch (elf.nodeWantsDsoRelocation(node)) { |
| 9738 | | .no => break :r .none, |
| 9739 | | .yes => {}, |
| 9740 | | .yes_textrel => elf.textrel_count += 1, |
| 9741 | | } |
| 9742 | | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 9743 | | .type = dynamic_reloc_type, |
| 9744 | | .offset = node_vaddr + offset, |
| 9745 | | .raw_sym_index = target.unwrap().global.dynsymIndex(elf).?, |
| 9746 | | .addend = addend.normal, |
| 9747 | | }).toOptional(); |
| 9748 | | }, |
| 9920 | if (!gsi.ptr(elf).status.want_static_value) { |
| 9921 | gsi.ptr(elf).status.want_static_value = true; |
| 9922 | try elf.updateGlobalDynamic(gsi, false); |
| 9749 | 9923 | } |
| 9750 | | }; |
| 9924 | } |
| 9751 | 9925 | |
| 9752 | 9926 | const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); |
| 9753 | 9927 | const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; |
| ... | ... | @@ -9762,12 +9936,13 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9762 | 9936 | .type = @"type", |
| 9763 | 9937 | .next = next, |
| 9764 | 9938 | .prev = .none, |
| 9765 | | .rela_index = rela_index, |
| 9939 | .rela_index = .none, |
| 9766 | 9940 | .result = .ok, |
| 9767 | 9941 | }); |
| 9768 | 9942 | if (@"type".dependsOnTlsSize(elf)) { |
| 9769 | 9943 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 9770 | 9944 | } |
| 9945 | try ri.get(elf).updateDynamicReloc(elf); |
| 9771 | 9946 | |
| 9772 | 9947 | // Actually apply the new relocation! |
| 9773 | 9948 | ri.get(elf).apply(elf); |
| ... | ... | @@ -10098,18 +10273,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 10098 | 10273 | }; |
| 10099 | 10274 | } |
| 10100 | 10275 | |
| 10101 | | /// If `node` cannot contain runtime relocations, returns `.no`. |
| 10276 | /// If `node` cannot contain dynamic relocations because it is not loaded into memory at runtime, |
| 10277 | /// returns `.not_alloc`. |
| 10102 | 10278 | /// |
| 10103 | | /// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires |
| 10104 | | /// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise. |
| 10105 | | fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } { |
| 10279 | /// Otherwise, returns `.ok_textrel` if placing a dynamic relocation in this node requires the |
| 10280 | /// presence of a `DT_TEXTREL` dynamic entry, or `.ok` otherwise. |
| 10281 | fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } { |
| 10106 | 10282 | const shndx = elf.getNodeShndx(node); |
| 10107 | 10283 | const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 10108 | 10284 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| 10109 | 10285 | }; |
| 10110 | | if (!shf.ALLOC) return .no; |
| 10111 | | if (!shf.WRITE) return .yes_textrel; |
| 10112 | | return .yes; |
| 10286 | if (!shf.ALLOC) return .not_alloc; |
| 10287 | if (!shf.WRITE) return .ok_textrel; |
| 10288 | return .ok; |
| 10113 | 10289 | } |
| 10114 | 10290 | |
| 10115 | 10291 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { |
| ... | ... | @@ -10120,6 +10296,7 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) |
| 10120 | 10296 | .{elf.mf.io_err.?}, |
| 10121 | 10297 | ), |
| 10122 | 10298 | }; |
| 10299 | try elf.updateSegmentLoadAddresses(); |
| 10123 | 10300 | } |
| 10124 | 10301 | fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { |
| 10125 | 10302 | const comp = elf.base.comp; |
| ... | ... | @@ -10130,15 +10307,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) |
| 10130 | 10307 | const nav = ip.getNav(nav_index); |
| 10131 | 10308 | const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?; |
| 10132 | 10309 | switch (ip.indexToKey(nav.resolved.?.value)) { |
| 10133 | | .@"extern" => |@"extern"| { |
| 10310 | .@"extern" => { |
| 10134 | 10311 | if (mod.strip or elf.ehdrMachine() != .X86_64) return; |
| 10135 | | const si = try elf.externSymbol(.{ |
| 10136 | | .name = @"extern".name.toSlice(ip), |
| 10137 | | .lib_name = @"extern".lib_name.toSlice(ip), |
| 10138 | | .type = elf.navType(nav.resolved.?), |
| 10139 | | .linkage = @"extern".linkage, |
| 10140 | | .visibility = @"extern".visibility, |
| 10141 | | }); |
| 10142 | 10312 | const dwarf_gi = try elf.dwarf.getGlobal(nav_index); |
| 10143 | 10313 | const dwarf_global = dwarf_gi.get(&elf.dwarf); |
| 10144 | 10314 | const debug_info_ni = dwarf_global.debug_info_ni.unwrap().?; |
| ... | ... | @@ -10147,10 +10317,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) |
| 10147 | 10317 | debug_info_ni.writer(gpa, &elf.mf, &di_nw); |
| 10148 | 10318 | defer di_nw.deinit(); |
| 10149 | 10319 | elf.resetNodeRelocs(debug_info_ni); |
| 10150 | | try elf.dwarf.updateExtern(pt, &di_nw, switch (elf.ehdrMachine()) { |
| 10151 | | else => unreachable, |
| 10152 | | .X86_64 => .x86_64, |
| 10153 | | }, si, nav_index); |
| 10320 | try elf.dwarf.updateExtern(pt, &di_nw, nav_index); |
| 10154 | 10321 | }, |
| 10155 | 10322 | else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) { |
| 10156 | 10323 | const nmi = try elf.navMapIndex(zcu, nav_index); |
| ... | ... | @@ -10219,6 +10386,7 @@ pub fn updateContainerType( |
| 10219 | 10386 | .{elf.mf.io_err.?}, |
| 10220 | 10387 | ), |
| 10221 | 10388 | }; |
| 10389 | try elf.updateSegmentLoadAddresses(); |
| 10222 | 10390 | } |
| 10223 | 10391 | fn updateContainerTypeInner( |
| 10224 | 10392 | elf: *Elf, |
| ... | ... | @@ -10360,6 +10528,7 @@ pub fn updateFunc( |
| 10360 | 10528 | .{elf.mf.io_err.?}, |
| 10361 | 10529 | ), |
| 10362 | 10530 | }; |
| 10531 | try elf.updateSegmentLoadAddresses(); |
| 10363 | 10532 | } |
| 10364 | 10533 | fn updateFuncInner( |
| 10365 | 10534 | elf: *Elf, |
| ... | ... | @@ -10607,6 +10776,7 @@ pub fn lostTracking( |
| 10607 | 10776 | elf.mf.io_err.?, |
| 10608 | 10777 | }), |
| 10609 | 10778 | }; |
| 10779 | try elf.updateSegmentLoadAddresses(); |
| 10610 | 10780 | } |
| 10611 | 10781 | |
| 10612 | 10782 | pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void { |
| ... | ... | @@ -10616,6 +10786,7 @@ pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void { |
| 10616 | 10786 | }); |
| 10617 | 10787 | if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi| |
| 10618 | 10788 | try elf.updateConstInner(pt, cpi, .anyerror_type, .complete); |
| 10789 | try elf.updateSegmentLoadAddresses(); |
| 10619 | 10790 | } |
| 10620 | 10791 | |
| 10621 | 10792 | pub fn flush( |
| ... | ... | @@ -10663,6 +10834,8 @@ fn flushInner( |
| 10663 | 10834 | |
| 10664 | 10835 | try elf.prepareDynamic(); |
| 10665 | 10836 | |
| 10837 | try elf.updateSegmentLoadAddresses(); |
| 10838 | |
| 10666 | 10839 | while (try elf.idle()) {} |
| 10667 | 10840 | |
| 10668 | 10841 | assert(elf.input_pending_index == elf.inputs.items.len); |
| ... | ... | @@ -10678,10 +10851,28 @@ fn flushInner( |
| 10678 | 10851 | defer elf.mf.nodes_lock.unlock(); |
| 10679 | 10852 | |
| 10680 | 10853 | if (elf.overflowed_reloc_count > 0) { |
| 10681 | | diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count}); |
| 10854 | diags.addError( |
| 10855 | "failed to apply {d} relocations: overflow", |
| 10856 | .{elf.overflowed_reloc_count}, |
| 10857 | ); |
| 10682 | 10858 | } |
| 10683 | 10859 | if (elf.misaligned_reloc_count > 0) { |
| 10684 | | diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count}); |
| 10860 | diags.addError( |
| 10861 | "failed to apply {d} relocations: misaligned value", |
| 10862 | .{elf.misaligned_reloc_count}, |
| 10863 | ); |
| 10864 | } |
| 10865 | if (elf.illegal_dynamic_type_reloc_count > 0) { |
| 10866 | diags.addError( |
| 10867 | "refusing to emit {d} dynamic relocations with illegal types", |
| 10868 | .{elf.illegal_dynamic_type_reloc_count}, |
| 10869 | ); |
| 10870 | } |
| 10871 | if (elf.illegal_dynamic_section_reloc_count > 0) { |
| 10872 | diags.addError( |
| 10873 | "refusing to emit {d} dynamic relocations in non-allocated sections", |
| 10874 | .{elf.illegal_dynamic_section_reloc_count}, |
| 10875 | ); |
| 10685 | 10876 | } |
| 10686 | 10877 | |
| 10687 | 10878 | if (elf.archive) |*archive| { |
| ... | ... | @@ -10698,20 +10889,22 @@ fn flushInner( |
| 10698 | 10889 | elf.flushDynamic(); |
| 10699 | 10890 | |
| 10700 | 10891 | const entry_addr: u64 = entry: { |
| 10701 | | const sym_name: []const u8 = name: switch (elf.options.entry) { |
| 10702 | | .default => switch (comp.config.output_mode) { |
| 10703 | | .Exe => continue :name .enabled, |
| 10704 | | .Lib, .Obj => continue :name .disabled, |
| 10705 | | }, |
| 10706 | | .disabled => break :entry 0, |
| 10707 | | .enabled => "_start", |
| 10708 | | .named => |named| named, |
| 10709 | | }; |
| 10710 | | const gsi = elf.globalByName(.{ |
| 10711 | | .name = sym_name, |
| 10712 | | .version = null, |
| 10713 | | }) orelse break :entry 0; |
| 10714 | | break :entry Symbol.Id.global(gsi).value(elf); |
| 10892 | const entry_gsi = elf.entry_gsi.unwrap() orelse break :entry 0; |
| 10893 | |
| 10894 | // Even though we already reported undefined globals earlier, we also need to check here |
| 10895 | // whether the entry point is defined. There are two reasons for this: |
| 10896 | // |
| 10897 | // * If we are emitting something other than an executable, we skipped the undefined globals |
| 10898 | // check, but there could still be an entry point (e.g. due to `-fentry=foo` on the CLI). |
| 10899 | // |
| 10900 | // * The entry point might be defined in an input DSO, which would satisfy the undefined |
| 10901 | // symbol reference but is insufficient here: the entry point needs a local definition. |
| 10902 | |
| 10903 | if (!entry_gsi.resolveAlias(elf).defined(elf)) { |
| 10904 | return diags.fail("undefined entry point symbol '{s}'", .{entry_gsi.name(elf)}); |
| 10905 | } |
| 10906 | |
| 10907 | break :entry Symbol.Id.global(entry_gsi).value(elf); |
| 10715 | 10908 | }; |
| 10716 | 10909 | switch (elf.ehdrPtr()) { |
| 10717 | 10910 | inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)), |
| ... | ... | @@ -10719,9 +10912,12 @@ fn flushInner( |
| 10719 | 10912 | |
| 10720 | 10913 | try elf.mf.flush(); |
| 10721 | 10914 | |
| 10722 | | if (elf.options.enable_link_snapshots) |
| 10723 | | elf.dumpStderr() catch |err| |
| 10724 | | return diags.fail("dumping link snapshot failed: {t}", .{err}); |
| 10915 | if (elf.options.enable_link_snapshots) { |
| 10916 | elf.dumpStderr() catch |err| switch (err) { |
| 10917 | error.Canceled => |e| return e, |
| 10918 | else => |e| return diags.fail("dumping link snapshot failed: {t}", .{e}), |
| 10919 | }; |
| 10920 | } |
| 10725 | 10921 | } |
| 10726 | 10922 | |
| 10727 | 10923 | pub fn idle(elf: *Elf) link.Error!bool { |
| ... | ... | @@ -10729,6 +10925,9 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10729 | 10925 | elf.mf.nodes_lock.lock(); |
| 10730 | 10926 | defer elf.mf.nodes_lock.unlock(); |
| 10731 | 10927 | |
| 10928 | const trace = tracy.trace(@src()); |
| 10929 | defer trace.end(); |
| 10930 | |
| 10732 | 10931 | const comp = elf.base.comp; |
| 10733 | 10932 | const diags = &comp.link_diags; |
| 10734 | 10933 | |
| ... | ... | @@ -10857,7 +11056,7 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10857 | 11056 | break :task; |
| 10858 | 11057 | } |
| 10859 | 11058 | while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) { |
| 10860 | | if (ni.pendingDelete(&elf.mf)) continue; |
| 11059 | ni.cleanUpdated(&elf.mf); |
| 10861 | 11060 | const clean_moved = ni.cleanMoved(&elf.mf); |
| 10862 | 11061 | const clean_resized = ni.cleanResized(&elf.mf); |
| 10863 | 11062 | const clean_next_moved = ni.cleanNextMoved(&elf.mf); |
| ... | ... | @@ -10872,6 +11071,9 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10872 | 11071 | if (elf.bss_moved) { |
| 10873 | 11072 | elf.bss_moved = false; |
| 10874 | 11073 | |
| 11074 | const idle_prog_node = elf.mf.update_prog_node.start(".bss", 0); |
| 11075 | defer idle_prog_node.end(); |
| 11076 | |
| 10875 | 11077 | const data_phndx = elf.getNode(elf.ni.data).segment; |
| 10876 | 11078 | const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) { |
| 10877 | 11079 | inline else => |phdr| .{ |
| ... | ... | @@ -10902,6 +11104,30 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10902 | 11104 | |
| 10903 | 11105 | break :task; |
| 10904 | 11106 | } |
| 11107 | if (elf.dwarf_addr.pending_reloc_index < elf.dwarf_addr.symbol_relocs.items.len) { |
| 11108 | const idle_prog_node = elf.mf.update_prog_node.start(@tagName(Node.debug_addr), 0); |
| 11109 | defer idle_prog_node.end(); |
| 11110 | const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?; |
| 11111 | for (elf.dwarf_addr.symbol_relocs.items[elf.dwarf_addr.pending_reloc_index..]) |symbol_ri| { |
| 11112 | const symbol_reloc = symbol_ri.get(elf); |
| 11113 | assert(symbol_reloc.node.unwrap().? == debug_addr_ni); |
| 11114 | symbol_reloc.apply(elf); |
| 11115 | } |
| 11116 | elf.dwarf_addr.pending_reloc_index = elf.dwarf_addr.symbol_relocs.items.len; |
| 11117 | break :task; |
| 11118 | } |
| 11119 | if (elf.dwarf_str_offsets.pending_reloc_index < elf.dwarf_str_offsets.node_relocs.items.len) { |
| 11120 | const idle_prog_node = elf.mf.update_prog_node.start(@tagName(Node.debug_str_offsets), 0); |
| 11121 | defer idle_prog_node.end(); |
| 11122 | const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?; |
| 11123 | for (elf.dwarf_str_offsets.node_relocs.items[elf.dwarf_str_offsets.pending_reloc_index..]) |node_ri| { |
| 11124 | const node_reloc = node_ri.get(elf); |
| 11125 | assert(node_reloc.node.unwrap().? == debug_str_offsets_ni); |
| 11126 | node_reloc.apply(elf); |
| 11127 | } |
| 11128 | elf.dwarf_str_offsets.pending_reloc_index = elf.dwarf_str_offsets.node_relocs.items.len; |
| 11129 | break :task; |
| 11130 | } |
| 10905 | 11131 | } |
| 10906 | 11132 | if (elf.inputs.items.len - elf.input_pending_index > 0) return true; |
| 10907 | 11133 | if (elf.input_sections.items.len - elf.input_section_pending_index > 0) return true; |
| ... | ... | @@ -10909,6 +11135,8 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10909 | 11135 | if (elf.changed_symtab_index.count() > 0) return true; |
| 10910 | 11136 | if (elf.mf.updates.items.len > 0) return true; |
| 10911 | 11137 | if (elf.bss_moved) return true; |
| 11138 | if (elf.dwarf_addr.pending_reloc_index < elf.dwarf_addr.symbol_relocs.items.len) return true; |
| 11139 | if (elf.dwarf_str_offsets.pending_reloc_index < elf.dwarf_str_offsets.node_relocs.items.len) return true; |
| 10912 | 11140 | return false; |
| 10913 | 11141 | } |
| 10914 | 11142 | |
| ... | ... | @@ -11039,7 +11267,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void { |
| 11039 | 11267 | fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void { |
| 11040 | 11268 | while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) { |
| 11041 | 11269 | const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?; |
| 11042 | | try debug_addr_ni.moved(gpa, &elf.mf); |
| 11043 | 11270 | var da_nw: link.MappedFile.Node.Writer = undefined; |
| 11044 | 11271 | debug_addr_ni.writer(gpa, &elf.mf, &da_nw); |
| 11045 | 11272 | defer da_nw.deinit(); |
| ... | ... | @@ -11052,7 +11279,6 @@ fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void { |
| 11052 | 11279 | } |
| 11053 | 11280 | while (elf.dwarf.debug_str_offsets.map.count() - elf.dwarf.debug_str_offsets.pending_index > 0) { |
| 11054 | 11281 | const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?; |
| 11055 | | try debug_str_offsets_ni.moved(gpa, &elf.mf); |
| 11056 | 11282 | var dso_nw: link.MappedFile.Node.Writer = undefined; |
| 11057 | 11283 | debug_str_offsets_ni.writer(gpa, &elf.mf, &dso_nw); |
| 11058 | 11284 | defer dso_nw.deinit(); |
| ... | ... | @@ -11297,6 +11523,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 11297 | 11523 | fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 11298 | 11524 | const trace = tracy.trace(@src()); |
| 11299 | 11525 | defer trace.end(); |
| 11526 | trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)}); |
| 11300 | 11527 | |
| 11301 | 11528 | switch (elf.getNode(ni)) { |
| 11302 | 11529 | .deleted => unreachable, |
| ... | ... | @@ -11315,7 +11542,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11315 | 11542 | else => unreachable, |
| 11316 | 11543 | |
| 11317 | 11544 | .NULL, .LOAD => { |
| 11318 | | try elf.allocateSegmentLoadAddress(phndx); |
| 11545 | // Our address changes are handled by `updateSegmentLoadAddresses`, so |
| 11546 | // there's nothing we need to do here. Don't bubble moved, because our |
| 11547 | // child nodes only care if our virtual address changes, which is |
| 11548 | // handled by that other function. |
| 11549 | return; |
| 11319 | 11550 | }, |
| 11320 | 11551 | |
| 11321 | 11552 | .DYNAMIC, |
| ... | ... | @@ -11343,6 +11574,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11343 | 11574 | elf, |
| 11344 | 11575 | tbss_addr, |
| 11345 | 11576 | ); |
| 11577 | |
| 11578 | // Do not bubble moved---our child nodes don't care about our virtual |
| 11579 | // address changing, because symbols in the TLS segment have values |
| 11580 | // defined relative to the base of the segment. (This is also why we |
| 11581 | // don't update tbss symbols here.) |
| 11582 | return; |
| 11346 | 11583 | }, |
| 11347 | 11584 | } |
| 11348 | 11585 | }, |
| ... | ... | @@ -11382,7 +11619,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11382 | 11619 | reloc.apply(elf); |
| 11383 | 11620 | } |
| 11384 | 11621 | } else if (shndx == elf.shndx.plt) { |
| 11385 | | elf.flushMovedNodeRelocs(ni, addr, .{ |
| 11622 | elf.flushMovedNodeRelocs(ni, .{ |
| 11386 | 11623 | .first_symbol_reloc = elf.plt_first_symbol_reloc, |
| 11387 | 11624 | }); |
| 11388 | 11625 | elf.flushMovedPltSection(.plt, old_addr, addr); |
| ... | ... | @@ -11391,10 +11628,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11391 | 11628 | } else if (shndx == elf.shndx.plt_sec) { |
| 11392 | 11629 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 11393 | 11630 | } else if (shndx == elf.shndx.eh_frame_hdr) { |
| 11394 | | elf.flushMovedNodeRelocs(ni, addr, .{ |
| 11631 | elf.flushMovedNodeRelocs(ni, .{ |
| 11395 | 11632 | .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc, |
| 11396 | 11633 | }); |
| 11397 | 11634 | } |
| 11635 | |
| 11636 | if (!flags.ALLOC) { |
| 11637 | // Do not bubble moved---our virtual address hasn't changed (because it's always 0 |
| 11638 | // for non-ALLOC sections), so there's nothing for our children to worry about. |
| 11639 | return; |
| 11640 | } |
| 11398 | 11641 | }, |
| 11399 | 11642 | .input_section => |isi| { |
| 11400 | 11643 | const old_section_addr = isi.ptr(elf).vaddr; |
| ... | ... | @@ -11438,17 +11681,13 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11438 | 11681 | } |
| 11439 | 11682 | } |
| 11440 | 11683 | |
| 11441 | | elf.flushMovedNodeRelocs(ni, new_section_addr, .{ |
| 11684 | elf.flushMovedNodeRelocs(ni, .{ |
| 11442 | 11685 | .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc, |
| 11443 | 11686 | .first_got_reloc = isi.ptrConst(elf).first_got_reloc, |
| 11444 | 11687 | }); |
| 11445 | 11688 | }, |
| 11446 | 11689 | .copied_global => |global_name| { |
| 11447 | | const copied_global = elf.copied_globals.getPtr(global_name) orelse { |
| 11448 | | // TODO: this node is orphaned, which is possible because `MappedFile` does not yet |
| 11449 | | // support deleting nodes. See logic in `updateGlobalDynamic`. |
| 11450 | | return; |
| 11451 | | }; |
| 11690 | const copied_global = elf.copied_globals.getPtr(global_name).?; |
| 11452 | 11691 | assert(copied_global.node == ni); |
| 11453 | 11692 | |
| 11454 | 11693 | const new_addr = elf.computeNodeVAddr(ni); |
| ... | ... | @@ -11465,7 +11704,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11465 | 11704 | Symbol.Id.global(gsi).flushMoved(elf, new_addr); |
| 11466 | 11705 | } |
| 11467 | 11706 | } |
| 11468 | | elf.flushMovedNodeRelocs(ni, new_addr, .{ |
| 11707 | elf.flushMovedNodeRelocs(ni, .{ |
| 11469 | 11708 | .first_symbol_reloc = mi.firstSymbolReloc(elf), |
| 11470 | 11709 | .skip_symbol_relocs = switch (tag) { |
| 11471 | 11710 | else => comptime unreachable, |
| ... | ... | @@ -11479,12 +11718,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11479 | 11718 | }); |
| 11480 | 11719 | }, |
| 11481 | 11720 | .debug_shared => |ss| { |
| 11482 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11483 | 11721 | var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc; |
| 11484 | 11722 | while (target_ri != .none) { |
| 11485 | 11723 | const target_reloc = target_ri.get(elf); |
| 11486 | 11724 | assert(target_reloc.target == ni); |
| 11487 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11725 | target_reloc.apply(elf); |
| 11488 | 11726 | target_ri = target_reloc.next; |
| 11489 | 11727 | } |
| 11490 | 11728 | }, |
| ... | ... | @@ -11495,119 +11733,110 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11495 | 11733 | .unit_debug_line, |
| 11496 | 11734 | => {}, |
| 11497 | 11735 | .debug_addr => { |
| 11498 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11499 | 11736 | var target_ri = elf.dwarf_addr.first_target_reloc; |
| 11500 | 11737 | while (target_ri != .none) { |
| 11501 | 11738 | const target_reloc = target_ri.get(elf); |
| 11502 | 11739 | assert(target_reloc.target == ni); |
| 11503 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11740 | target_reloc.apply(elf); |
| 11504 | 11741 | target_ri = target_reloc.next; |
| 11505 | 11742 | } |
| 11506 | | const node_vaddr = elf.computeNodeVAddr(ni); |
| 11507 | 11743 | for (elf.dwarf_addr.symbol_relocs.items) |symbol_ri| { |
| 11508 | 11744 | const symbol_reloc = symbol_ri.get(elf); |
| 11509 | 11745 | assert(symbol_reloc.node.unwrap().? == ni); |
| 11510 | | symbol_reloc.flushMovedNode(elf, node_vaddr); |
| 11746 | symbol_reloc.apply(elf); |
| 11511 | 11747 | } |
| 11748 | elf.dwarf_addr.pending_reloc_index = elf.dwarf_addr.symbol_relocs.items.len; |
| 11512 | 11749 | }, |
| 11513 | 11750 | .debug_str_offsets => { |
| 11514 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11515 | 11751 | var target_ri = elf.dwarf_str_offsets.first_target_reloc; |
| 11516 | 11752 | while (target_ri != .none) { |
| 11517 | 11753 | const target_reloc = target_ri.get(elf); |
| 11518 | 11754 | assert(target_reloc.target == ni); |
| 11519 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11755 | target_reloc.apply(elf); |
| 11520 | 11756 | target_ri = target_reloc.next; |
| 11521 | 11757 | } |
| 11522 | | const node_vaddr = elf.computeNodeVAddr(ni); |
| 11523 | 11758 | for (elf.dwarf_str_offsets.node_relocs.items) |node_ri| { |
| 11524 | 11759 | const node_reloc = node_ri.get(elf); |
| 11525 | 11760 | assert(node_reloc.node.unwrap().? == ni); |
| 11526 | | node_reloc.flushMovedNode(elf, node_vaddr); |
| 11761 | node_reloc.apply(elf); |
| 11527 | 11762 | } |
| 11763 | elf.dwarf_str_offsets.pending_reloc_index = elf.dwarf_str_offsets.node_relocs.items.len; |
| 11528 | 11764 | }, |
| 11529 | 11765 | .unit_frame_cie => |ui| { |
| 11530 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11531 | 11766 | var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc; |
| 11532 | 11767 | while (target_ri != .none) { |
| 11533 | 11768 | const target_reloc = target_ri.get(elf); |
| 11534 | 11769 | assert(target_reloc.target == ni); |
| 11535 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11770 | target_reloc.apply(elf); |
| 11536 | 11771 | target_ri = target_reloc.next; |
| 11537 | 11772 | } |
| 11538 | 11773 | }, |
| 11539 | 11774 | .unit_debug_info_header => |ui| { |
| 11540 | 11775 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; |
| 11541 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11542 | 11776 | var target_ri = dwarf_unit.debug_info_header_first_target_reloc; |
| 11543 | 11777 | while (target_ri != .none) { |
| 11544 | 11778 | const target_reloc = target_ri.get(elf); |
| 11545 | 11779 | assert(target_reloc.target == ni); |
| 11546 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11780 | target_reloc.apply(elf); |
| 11547 | 11781 | target_ri = target_reloc.next; |
| 11548 | 11782 | } |
| 11549 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11783 | elf.flushMovedNodeRelocs(ni, .{ |
| 11550 | 11784 | .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc, |
| 11551 | 11785 | }); |
| 11552 | 11786 | }, |
| 11553 | 11787 | .unit_debug_info_footer => {}, |
| 11554 | 11788 | .unit_debug_line_header => |ui| { |
| 11555 | 11789 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; |
| 11556 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11557 | 11790 | var target_ri = dwarf_unit.debug_line_header_first_target_reloc; |
| 11558 | 11791 | while (target_ri != .none) { |
| 11559 | 11792 | const target_reloc = target_ri.get(elf); |
| 11560 | 11793 | assert(target_reloc.target == ni); |
| 11561 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11794 | target_reloc.apply(elf); |
| 11562 | 11795 | target_ri = target_reloc.next; |
| 11563 | 11796 | } |
| 11564 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11797 | elf.flushMovedNodeRelocs(ni, .{ |
| 11565 | 11798 | .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc, |
| 11566 | 11799 | }); |
| 11567 | 11800 | }, |
| 11568 | 11801 | .unit_debug_rnglists => |ui| { |
| 11569 | 11802 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; |
| 11570 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11571 | 11803 | var target_ri = dwarf_unit.debug_rnglists_first_target_reloc; |
| 11572 | 11804 | while (target_ri != .none) { |
| 11573 | 11805 | const target_reloc = target_ri.get(elf); |
| 11574 | 11806 | assert(target_reloc.target == ni); |
| 11575 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11807 | target_reloc.apply(elf); |
| 11576 | 11808 | target_ri = target_reloc.next; |
| 11577 | 11809 | } |
| 11578 | | const node_vaddr = elf.computeNodeVAddr(ni); |
| 11579 | 11810 | for (dwarf_unit.debug_rnglists_symbol_relocs.items) |symbol_ri| { |
| 11580 | 11811 | const symbol_reloc = symbol_ri.get(elf); |
| 11581 | 11812 | assert(symbol_reloc.node.unwrap().? == ni); |
| 11582 | | symbol_reloc.flushMovedNode(elf, node_vaddr); |
| 11813 | symbol_reloc.apply(elf); |
| 11583 | 11814 | } |
| 11584 | 11815 | }, |
| 11585 | 11816 | .const_debug_info => |cpi| { |
| 11586 | 11817 | const dwarf_const = &elf.dwarf_consts.get(cpi).?; |
| 11587 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11588 | 11818 | var target_ri = dwarf_const.debug_info_first_target_reloc; |
| 11589 | 11819 | while (target_ri != .none) { |
| 11590 | 11820 | const target_reloc = target_ri.get(elf); |
| 11591 | 11821 | assert(target_reloc.target == ni); |
| 11592 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11822 | target_reloc.apply(elf); |
| 11593 | 11823 | target_ri = target_reloc.next; |
| 11594 | 11824 | } |
| 11595 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11825 | elf.flushMovedNodeRelocs(ni, .{ |
| 11596 | 11826 | .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc, |
| 11597 | 11827 | .first_node_reloc = dwarf_const.debug_info_first_node_reloc, |
| 11598 | 11828 | }); |
| 11599 | 11829 | }, |
| 11600 | 11830 | .global_debug_info => |gi| { |
| 11601 | 11831 | const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)]; |
| 11602 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11603 | 11832 | var target_ri = dwarf_global.debug_info_first_target_reloc; |
| 11604 | 11833 | while (target_ri != .none) { |
| 11605 | 11834 | const target_reloc = target_ri.get(elf); |
| 11606 | 11835 | assert(target_reloc.target == ni); |
| 11607 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11836 | target_reloc.apply(elf); |
| 11608 | 11837 | target_ri = target_reloc.next; |
| 11609 | 11838 | } |
| 11610 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11839 | elf.flushMovedNodeRelocs(ni, .{ |
| 11611 | 11840 | .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc, |
| 11612 | 11841 | .first_node_reloc = dwarf_global.debug_info_first_node_reloc, |
| 11613 | 11842 | }); |
| ... | ... | @@ -11623,22 +11852,21 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11623 | 11852 | elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset); |
| 11624 | 11853 | }, |
| 11625 | 11854 | } |
| 11626 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11855 | elf.flushMovedNodeRelocs(ni, .{ |
| 11627 | 11856 | .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc, |
| 11628 | 11857 | .first_node_reloc = dwarf_func.frame_fde_first_node_reloc, |
| 11629 | 11858 | }); |
| 11630 | 11859 | }, |
| 11631 | 11860 | .func_debug_info => |fi| { |
| 11632 | 11861 | const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; |
| 11633 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11634 | 11862 | var target_ri = dwarf_func.debug_info_first_target_reloc; |
| 11635 | 11863 | while (target_ri != .none) { |
| 11636 | 11864 | const target_reloc = target_ri.get(elf); |
| 11637 | 11865 | assert(target_reloc.target == ni); |
| 11638 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11866 | target_reloc.apply(elf); |
| 11639 | 11867 | target_ri = target_reloc.next; |
| 11640 | 11868 | } |
| 11641 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11869 | elf.flushMovedNodeRelocs(ni, .{ |
| 11642 | 11870 | .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc, |
| 11643 | 11871 | .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav| |
| 11644 | 11872 | nav.lsi.index().ptr(elf).node |
| ... | ... | @@ -11650,7 +11878,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11650 | 11878 | }, |
| 11651 | 11879 | .func_debug_line => |fi| { |
| 11652 | 11880 | const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; |
| 11653 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11881 | elf.flushMovedNodeRelocs(ni, .{ |
| 11654 | 11882 | .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc, |
| 11655 | 11883 | .first_node_reloc = dwarf_func.debug_line_first_node_reloc, |
| 11656 | 11884 | .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni, |
| ... | ... | @@ -11658,15 +11886,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11658 | 11886 | }, |
| 11659 | 11887 | .decl_debug_info => |di| { |
| 11660 | 11888 | const dwarf_decl = &elf.dwarf_decls.get(di).?; |
| 11661 | | const target_section_offset = elf.computeNodeSectionOffset(ni); |
| 11662 | 11889 | var target_ri = dwarf_decl.debug_info_first_target_reloc; |
| 11663 | 11890 | while (target_ri != .none) { |
| 11664 | 11891 | const target_reloc = target_ri.get(elf); |
| 11665 | 11892 | assert(target_reloc.target == ni); |
| 11666 | | target_reloc.flushMovedTarget(elf, target_section_offset); |
| 11893 | target_reloc.apply(elf); |
| 11667 | 11894 | target_ri = target_reloc.next; |
| 11668 | 11895 | } |
| 11669 | | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ |
| 11896 | elf.flushMovedNodeRelocs(ni, .{ |
| 11670 | 11897 | .first_node_reloc = dwarf_decl.debug_info_first_node_reloc, |
| 11671 | 11898 | }); |
| 11672 | 11899 | }, |
| ... | ... | @@ -11737,166 +11964,156 @@ fn flushMovedNobitsSymbols( |
| 11737 | 11964 | } |
| 11738 | 11965 | } |
| 11739 | 11966 | |
| 11740 | | /// Updates `vaddr` and `paddr` of the segment header, and marks everything in the segment as moved. |
| 11741 | | fn updateSegmentLoadAddress( |
| 11742 | | elf: *Elf, |
| 11743 | | segment_ni: MappedFile.Node.Index, |
| 11744 | | new_vaddr: u64, |
| 11745 | | ) std.mem.Allocator.Error!void { |
| 11746 | | const phndx = elf.getNode(segment_ni).segment; |
| 11747 | | |
| 11748 | | switch (elf.phdrSlice()) { |
| 11749 | | inline else => |phdr| { |
| 11750 | | assert(new_vaddr != elf.targetLoad(&phdr[phndx].vaddr)); |
| 11751 | | elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr)); |
| 11752 | | elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr)); |
| 11753 | | }, |
| 11754 | | } |
| 11755 | | |
| 11756 | | try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11757 | | if (segment_ni == elf.ni.data) elf.bss_moved = true; |
| 11758 | | } |
| 11759 | | |
| 11760 | | /// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz` |
| 11761 | | /// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and |
| 11762 | | /// `memsz` fields of the segment, in order to place it at a valid virtual address. |
| 11967 | /// If any loadable segments have invalid virtual addresses, assigns them new valid addresses and |
| 11968 | /// queues the necessary idle tasks (e.g. re-applying relocations targeting those segments). |
| 11763 | 11969 | /// |
| 11764 | | /// TODO: this function is currently a source of non-determinism in the linker, because handling the |
| 11765 | | /// moving or resizing of a segment could reorder them and thereby affect how we handle *future* |
| 11766 | | /// changes to segments. |
| 11767 | | fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void { |
| 11768 | | const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?; |
| 11769 | | assert(elf.getNode(segment_ni).segment == orig_phndx); |
| 11970 | /// This function must be called at the end of every link task in case tghe task invalidated segment |
| 11971 | /// addresses (which can happen in response to any node movement/resize, or any section resize). It |
| 11972 | /// cannot be called from `idle` as this function does not have the required idempotency guarantee |
| 11973 | /// so calling it non-deterministically it would lead to unreproducible output files. |
| 11974 | fn updateSegmentLoadAddresses(elf: *Elf) std.mem.Allocator.Error!void { |
| 11975 | if (elf.ehdrType() == .REL) return; |
| 11976 | |
| 11770 | 11977 | const page_align = elf.targetPageAlign(); |
| 11771 | | const node_align = segment_ni.alignment(&elf.mf); |
| 11772 | | const ph_align = page_align.max(node_align); |
| 11773 | | |
| 11774 | | // If we determine that the segment's virtual address needs to move, then it's a good idea to |
| 11775 | | // make it less likely that it needs to move *again* in the future, because it is expensive to |
| 11776 | | // change a segment's load address (a lot of re-flushing is necessary). To do that, we reserve |
| 11777 | | // more virtual address space than we need (multiplying the actual size by this value). That |
| 11778 | | // way, there will usually be padding between segments which they can grow into. |
| 11779 | | // |
| 11780 | | // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases where |
| 11781 | | // virtual address space is constrained. For instance, 32-bit targets, or targets where short |
| 11782 | | // PC-relative relocations between segments are common. |
| 11783 | | const reserve_size_multiplier = 4; |
| 11784 | 11978 | |
| 11785 | 11979 | switch (elf.phdrSlice()) { |
| 11786 | 11980 | inline else => |phdr| { |
| 11787 | | const offset = elf.targetLoad(&phdr[orig_phndx].offset); |
| 11788 | | const filesz = elf.targetLoad(&phdr[orig_phndx].filesz); |
| 11789 | | |
| 11790 | | const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr); |
| 11791 | | assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr); |
| 11792 | | |
| 11793 | | const bss_size = if (segment_ni == elf.ni.data) |
| 11794 | | elf.shndx.bss.size(elf) |
| 11795 | | else |
| 11796 | | 0; |
| 11797 | | const memsz = filesz + bss_size; |
| 11798 | | |
| 11799 | | var vaddr: u64 = orig_vaddr; |
| 11800 | | |
| 11801 | | elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits())); |
| 11802 | | elf.targetStore(&phdr[orig_phndx].memsz, @intCast(memsz)); |
| 11803 | | elf.targetStore(&phdr[orig_phndx].type, if (memsz > 0) .LOAD else .NULL); |
| 11804 | | |
| 11805 | | // First, we will shift the virtual address as needed in order to maintain the required |
| 11806 | | // property that vaddr is congruent to offset modulo the phdr alignment. |
| 11807 | | { |
| 11808 | | // Compute the candidate address by undoing the current offset and then re-offsetting |
| 11809 | | vaddr = std.mem.alignBackward(u64, vaddr, ph_align.toByteUnits()) + offset % ph_align.toByteUnits(); |
| 11810 | | // If `node_align` is greater than `page_align`, the address we just set might be in |
| 11811 | | // the previous segment. The first page we "own" is the one in which the old vaddr |
| 11812 | | // resides, so check against that. |
| 11813 | | const first_good_vaddr = std.mem.alignBackward(u64, orig_vaddr, page_align.toByteUnits()); |
| 11814 | | if (vaddr < first_good_vaddr) { |
| 11815 | | // Yep, we crossed into the previous segment's pages, so correct for that by |
| 11816 | | // offsetting our address by another `ph_align`. |
| 11817 | | vaddr += ph_align.toByteUnits(); |
| 11818 | | assert(vaddr >= first_good_vaddr); |
| 11981 | // Somewhere in the phdrs is a contiguous sequence of all "loadable" segments (segments |
| 11982 | // with a `type` of `PT_NULL` or `PT_LOAD`); find the start of that sequence. In theory |
| 11983 | // we could memoize this, but there's no real need since the number of phdrs is tiny. |
| 11984 | const load_phdrs_start = for (phdr, 0..) |*ph, phndx| { |
| 11985 | switch (elf.targetLoad(&ph.type)) { |
| 11986 | .NULL, .LOAD => break phndx, |
| 11987 | else => continue, |
| 11819 | 11988 | } |
| 11820 | | } |
| 11821 | | |
| 11822 | | // If our size has changed, or if the address shift above caused our "end" address to |
| 11823 | | // cross a page boundary, then we might be overlapping with the next segment's pages. In |
| 11824 | | // that case, we will jump past that segment and give ourselves a new address after it. |
| 11825 | | // We'll need to repeat this for every loadable phdr after us, until we're no longer |
| 11826 | | // overlapping anything. |
| 11827 | | var phndx = orig_phndx; |
| 11828 | | for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| { |
| 11829 | | switch (elf.targetLoad(&next_ph.type)) { |
| 11989 | } else unreachable; // we always create loadable (`PT_NULL`/`PT_LOAD`) segments |
| 11990 | |
| 11991 | // Update these fields for each loadable segment: |
| 11992 | // |
| 11993 | // * `memsz` |
| 11994 | // * `type` (`PT_NULL` vs `PT_LOAD`) |
| 11995 | // * `align` |
| 11996 | // * `offset` (in theory this should happen in `updateElfOffset`, but this logic needs |
| 11997 | // to track that value, so it's easier to just populate it here instead) |
| 11998 | // |
| 11999 | // ...and check if the current `vaddr` values for the loadable segments are valid (i.e. |
| 12000 | // compatible with the segments' file offsets and address ranges do not overlap). |
| 12001 | var vaddrs_valid: bool = true; |
| 12002 | var first_free_vaddr: u64 = elf.baseLoadableSegmentVAddr(); |
| 12003 | const load_phdrs_end: usize = for ( |
| 12004 | phdr[load_phdrs_start..], |
| 12005 | elf.phdrs.items[load_phdrs_start..], |
| 12006 | load_phdrs_start.., |
| 12007 | ) |*ph, segment_oni, phndx| { |
| 12008 | switch (elf.targetLoad(&ph.type)) { |
| 11830 | 12009 | .NULL, .LOAD => {}, |
| 11831 | | else => { |
| 11832 | | // All loadable segments have contiguous indices, so this indicates we have |
| 11833 | | // become the last loadable segment, meaning we definitely don't overlap any |
| 11834 | | // other loadable segment. |
| 11835 | | break; |
| 11836 | | }, |
| 12010 | else => break phndx, // reached end of loadable segments |
| 11837 | 12011 | } |
| 11838 | | |
| 11839 | | const next_vaddr = elf.targetLoad(&next_ph.vaddr); |
| 11840 | | // Find the first virtual address which the next phdr "owns" by aligning its vaddr |
| 11841 | | // backwards to the start of the page. |
| 11842 | | const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits()); |
| 11843 | | |
| 11844 | | // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if |
| 11845 | | // the segment is already known to be moving---making it easier to grow in-place is |
| 11846 | | // the whole point of the multiplier! |
| 11847 | | { |
| 11848 | | const target_size = if (vaddr == orig_vaddr) memsz else memsz * reserve_size_multiplier; |
| 11849 | | if (vaddr + target_size <= next_page_vaddr) { |
| 11850 | | break; // hooray, we fit here! |
| 12012 | const segment_ni = segment_oni.unwrap().?; // all loadable segments have nodes |
| 12013 | |
| 12014 | // We need to know the segment's file offset here anyway, so we may as well just |
| 12015 | // populate the phdr `offset` field now instead of in `flushElfOffset`. |
| 12016 | const ph_offset = elf.computeNodeElfOffset(segment_ni); |
| 12017 | elf.targetStore(&ph.offset, @intCast(ph_offset)); |
| 12018 | |
| 12019 | // The alignment of the segment affects which virtual addresses are valid. |
| 12020 | const ph_align = segment_ni.alignment(&elf.mf).max(page_align); |
| 12021 | elf.targetStore(&ph.@"align", @intCast(ph_align.toByteUnits())); |
| 12022 | |
| 12023 | // The size of the segment in memory is equal to the size of the segment in the file |
| 12024 | // plus the size of any `SHT_NOBITS` section(s) at the end of the segment. |
| 12025 | const ph_memsz: u64 = memsz: { |
| 12026 | _, const file_size: u64 = segment_ni.location(&elf.mf).resolve(&elf.mf); |
| 12027 | if (segment_ni == elf.ni.data) { |
| 12028 | break :memsz file_size + elf.shndx.bss.size(elf); |
| 12029 | } else { |
| 12030 | break :memsz file_size; |
| 11851 | 12031 | } |
| 12032 | }; |
| 12033 | elf.targetStore(&ph.memsz, @intCast(ph_memsz)); |
| 12034 | elf.targetStore(&ph.type, if (ph_memsz == 0) .NULL else .LOAD); |
| 12035 | |
| 12036 | // Check if the existing virtual address of the segment is valid. |
| 12037 | const ph_vaddr = elf.targetLoad(&ph.vaddr); |
| 12038 | assert(elf.targetLoad(&ph.paddr) == ph_vaddr); |
| 12039 | if (ph_vaddr < first_free_vaddr or |
| 12040 | ph_offset % ph_align.toByteUnits() != ph_vaddr % ph_align.toByteUnits()) |
| 12041 | { |
| 12042 | @branchHint(.unlikely); |
| 12043 | vaddrs_valid = false; |
| 11852 | 12044 | } |
| 11853 | 12045 | |
| 11854 | | const next_ni = elf.phdrs.items[next_phndx].unwrap().?; |
| 12046 | // The next segment's pages must not overlap our own. |
| 12047 | first_free_vaddr = page_align.forward(ph_vaddr + ph_memsz); |
| 12048 | } else phdr.len; |
| 11855 | 12049 | |
| 11856 | | // This segment don't fit here, but before deciding how to proceed, we need to |
| 11857 | | // consider any target-specific restrictions we are subject to. |
| 11858 | | switch (elf.targetSegmentLoadAddressRestrictions()) { |
| 11859 | | .none => {}, |
| 11860 | | .data_last => if (next_ni == elf.ni.data) { |
| 11861 | | // We can't leapfrog over the data segment. Instead, that segment just needs |
| 11862 | | // to be shifted forwards to make space for us, and we'll then `break` with |
| 11863 | | // our current vaddr. |
| 11864 | | |
| 11865 | | if (next_phndx + 1 < phdr.len) switch (elf.targetLoad(&phdr[next_phndx + 1].type)) { |
| 11866 | | .NULL, .LOAD => unreachable, // data segment should be the last loadable segment |
| 11867 | | else => {}, |
| 11868 | | }; |
| 12050 | // There should be no more loadable segments after the contiguous range. |
| 12051 | for (phdr[load_phdrs_end..]) |*ph| switch (elf.targetLoad(&ph.type)) { |
| 12052 | .NULL, .LOAD => unreachable, // non-contiguous loadable segment |
| 12053 | else => {}, |
| 12054 | }; |
| 11869 | 12055 | |
| 11870 | | const free_vaddr = vaddr + memsz * reserve_size_multiplier; |
| 12056 | if (vaddrs_valid) { |
| 12057 | @branchHint(.likely); |
| 12058 | return; |
| 12059 | } |
| 11871 | 12060 | |
| 11872 | | const next_align = page_align.max(next_ni.alignment(&elf.mf)); |
| 11873 | | const next_offset = elf.targetLoad(&next_ph.offset); |
| 11874 | | const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits(); |
| 12061 | // It's time to actually allocate virtual addresses for all segments. Our current |
| 12062 | // strategy for this preserves the order of segments in memory, but this isn't a hard |
| 12063 | // requirement in general. |
| 12064 | // |
| 12065 | // However, a warning! If this logic *is* ever changed to re-order segments, it may need |
| 12066 | // to abide by target-specific restrictions. For instance, on SPARC, because the GOT is |
| 12067 | // usually accessed with the unsigned relocations `R_SPARC_PC{10,22}`, it is effectively |
| 12068 | // a requirement for all code to have a lower virtual address than the GOT, which means |
| 12069 | // that the "text" segment must be ordered before the "data" segment on that target. |
| 11875 | 12070 | |
| 11876 | | try elf.updateSegmentLoadAddress(next_ni, next_new_vaddr); |
| 12071 | const gpa = elf.base.comp.gpa; |
| 11877 | 12072 | |
| 11878 | | break; |
| 11879 | | }, |
| 12073 | // If we determine that a segment's virtual address needs to move, then it's a good idea |
| 12074 | // to space it out a bit from the surrounding segments, making it less likely that more |
| 12075 | // segment moves are necessary in the future (because every segment movement invalidates |
| 12076 | // a lot of idle work). We do that by multiplying each segment's size by this multiplier |
| 12077 | // and treating that amount of space as "reserved" for the segment to grow into. |
| 12078 | // |
| 12079 | // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases |
| 12080 | // where the virtual address space is constrained; for instance, 32-bit targets, or |
| 12081 | // targets where short PC-relative relocations between segments are common. |
| 12082 | const reserve_size_multiplier = 4; |
| 12083 | |
| 12084 | first_free_vaddr = elf.baseLoadableSegmentVAddr(); |
| 12085 | var preferred_vaddr = first_free_vaddr; |
| 12086 | |
| 12087 | for (load_phdrs_start..load_phdrs_end) |phndx| { |
| 12088 | const ph_align: Alignment = .fromByteUnits(elf.targetLoad(&phdr[phndx].@"align")); |
| 12089 | assert(ph_align.compare(.gte, page_align)); |
| 12090 | const ph_offset = elf.targetLoad(&phdr[phndx].offset); |
| 12091 | const ph_memsz = elf.targetLoad(&phdr[phndx].memsz); |
| 12092 | |
| 12093 | // If the existing address is valid, then don't do anything. |
| 12094 | const ph_vaddr: u64 = elf.targetLoad(&phdr[phndx].vaddr); |
| 12095 | if (ph_vaddr >= first_free_vaddr and |
| 12096 | ph_vaddr % ph_align.toByteUnits() == ph_offset % ph_align.toByteUnits()) |
| 12097 | { |
| 12098 | // The current virtual address is already valid, so just stay put. Perhaps the |
| 12099 | // next segment will have to move, but the only alternative was *us* moving! |
| 12100 | first_free_vaddr = page_align.forward(ph_vaddr + ph_memsz); |
| 12101 | preferred_vaddr = page_align.forward(ph_vaddr + ph_memsz * reserve_size_multiplier); |
| 12102 | continue; |
| 11880 | 12103 | } |
| 11881 | 12104 | |
| 11882 | | // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But |
| 11883 | | // first we need to adjust `vaddr` to come after it. |
| 11884 | | const next_size = elf.targetLoad(&next_ph.memsz); |
| 11885 | | // Instead of putting ourselves right after `next_ph`, we'll go a bit later in the |
| 11886 | | // address space so that `next_ph` has address space to grow into (like above). |
| 11887 | | vaddr = ph_align.forward(@intCast(next_vaddr + next_size * 4)) + offset % ph_align.toByteUnits(); |
| 12105 | // Otherwise, put ourselves at a new address just after the previous segment. |
| 12106 | const new_vaddr = ph_align.forward(preferred_vaddr) + ph_offset % ph_align.toByteUnits(); |
| 12107 | assert(new_vaddr != ph_vaddr); // `ph_vaddr` was not valid |
| 11888 | 12108 | |
| 11889 | | // Now just swap the phdrs and update our `phndx`. |
| 11890 | | std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph); |
| 11891 | | elf.phdrs.items[phndx] = .wrap(next_ni); |
| 11892 | | elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx }; |
| 11893 | | elf.phdrs.items[next_phndx] = .wrap(segment_ni); |
| 11894 | | elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) }; |
| 11895 | | phndx = @intCast(next_phndx); |
| 11896 | | } |
| 12109 | elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr)); |
| 12110 | elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr)); |
| 12111 | const segment_ni = elf.phdrs.items[phndx].unwrap().?; |
| 12112 | try segment_ni.childrenMoved(gpa, &elf.mf); |
| 12113 | if (segment_ni == elf.ni.data) elf.bss_moved = true; |
| 11897 | 12114 | |
| 11898 | | if (vaddr != orig_vaddr) { |
| 11899 | | try elf.updateSegmentLoadAddress(segment_ni, vaddr); |
| 12115 | first_free_vaddr = page_align.forward(new_vaddr + ph_memsz); |
| 12116 | preferred_vaddr = page_align.forward(new_vaddr + ph_memsz * reserve_size_multiplier); |
| 11900 | 12117 | } |
| 11901 | 12118 | }, |
| 11902 | 12119 | } |
| ... | ... | @@ -11905,6 +12122,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11905 | 12122 | fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 11906 | 12123 | const trace = tracy.trace(@src()); |
| 11907 | 12124 | defer trace.end(); |
| 12125 | trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)}); |
| 11908 | 12126 | |
| 11909 | 12127 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); |
| 11910 | 12128 | switch (elf.getNode(ni)) { |
| ... | ... | @@ -11933,7 +12151,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11933 | 12151 | else => unreachable, |
| 11934 | 12152 | .NULL, .LOAD => { |
| 11935 | 12153 | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11936 | | try elf.allocateSegmentLoadAddress(phndx); |
| 11937 | 12154 | if (ni == elf.ni.data) elf.bss_moved = true; |
| 11938 | 12155 | }, |
| 11939 | 12156 | .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => { |
| ... | ... | @@ -11946,7 +12163,11 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11946 | 12163 | |
| 11947 | 12164 | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11948 | 12165 | |
| 11949 | | // Our memsz must include the `.tbss` section. |
| 12166 | // Our memsz must include the `.tbss` section. After adding that in, we will |
| 12167 | // realign to the TLS segment alignment. This is not strictly required, but |
| 12168 | // the TLS runtime will pad the module's TLS block to the segment alignment |
| 12169 | // anyway, so it's simpler for us to just make the memsz aligned so that our |
| 12170 | // memsz matches the size of the module's TLS block. |
| 11950 | 12171 | const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align")); |
| 11951 | 12172 | elf.targetStore(&ph.memsz, @intCast( |
| 11952 | 12173 | tls_align.forward(size + elf.shndx.tbss.size(elf)), |
| ... | ... | @@ -12040,6 +12261,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 12040 | 12261 | fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 12041 | 12262 | const trace = tracy.trace(@src()); |
| 12042 | 12263 | defer trace.end(); |
| 12264 | trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)}); |
| 12043 | 12265 | |
| 12044 | 12266 | const node = elf.getNode(ni); |
| 12045 | 12267 | switch (node) { |
| ... | ... | @@ -12480,7 +12702,6 @@ fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool { |
| 12480 | 12702 | |
| 12481 | 12703 | // relocate the PLT entry to point to the .GOT.PLT entry |
| 12482 | 12704 | const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset; |
| 12483 | | // TODO: handle overflow gracefully |
| 12484 | 12705 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]); |
| 12485 | 12706 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]); |
| 12486 | 12707 | elf.targetStore(inst0, .{ |
| ... | ... | @@ -12646,7 +12867,6 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad |
| 12646 | 12867 | const target_slice = plt_slice[plt_offset..]; |
| 12647 | 12868 | |
| 12648 | 12869 | const got_plt_abs: u64 = got_plt_addr + got_plt_offset; |
| 12649 | | // TODO: handle overflow gracefully |
| 12650 | 12870 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]); |
| 12651 | 12871 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]); |
| 12652 | 12872 | |
| ... | ... | @@ -12698,6 +12918,7 @@ pub fn updateExports( |
| 12698 | 12918 | }; |
| 12699 | 12919 | } |
| 12700 | 12920 | try elf.genPending(pt); |
| 12921 | try elf.updateSegmentLoadAddresses(); |
| 12701 | 12922 | } |
| 12702 | 12923 | fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void { |
| 12703 | 12924 | const zcu = elf.base.comp.zcu.?; |
| ... | ... | @@ -12801,111 +13022,8 @@ pub fn printNode( |
| 12801 | 13022 | ni: MappedFile.Node.Index, |
| 12802 | 13023 | indent: usize, |
| 12803 | 13024 | ) Io.Writer.Error!void { |
| 12804 | | const node = elf.getNode(ni); |
| 12805 | 13025 | try w.splatByteAll(' ', indent); |
| 12806 | | try w.writeAll(@tagName(node)); |
| 12807 | | switch (node) { |
| 12808 | | else => {}, |
| 12809 | | .segment => |phndx| switch (elf.phdrSlice()) { |
| 12810 | | inline else => |phdr| { |
| 12811 | | const ph = &phdr[phndx]; |
| 12812 | | try w.writeByte('('); |
| 12813 | | const pt = elf.targetLoad(&ph.type); |
| 12814 | | if (std.enums.tagName(std.elf.PT, pt)) |pt_name| |
| 12815 | | try w.writeAll(pt_name) |
| 12816 | | else inline for (@typeInfo(std.elf.PT).@"enum".decl_names) |decl_name| { |
| 12817 | | const decl_val = @field(std.elf.PT, decl_name); |
| 12818 | | if (@TypeOf(decl_val) != std.elf.PT) continue; |
| 12819 | | if (pt == @field(std.elf.PT, decl_name)) break try w.writeAll(decl_name); |
| 12820 | | } else try w.print("0x{x}", .{pt}); |
| 12821 | | try w.writeAll(", "); |
| 12822 | | const pf = elf.targetLoad(&ph.flags); |
| 12823 | | if (pf.R) try w.writeByte('R'); |
| 12824 | | if (pf.W) try w.writeByte('W'); |
| 12825 | | if (pf.X) try w.writeByte('X'); |
| 12826 | | try w.writeByte(')'); |
| 12827 | | }, |
| 12828 | | }, |
| 12829 | | .section, .section_manual_size => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}), |
| 12830 | | .input_section => |isi| { |
| 12831 | | const ii = isi.input(elf); |
| 12832 | | try w.print("({f}{f}, {s})", .{ |
| 12833 | | ii.path(elf).fmtEscapeString(), |
| 12834 | | fmtMemberString(ii.member(elf)), |
| 12835 | | elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), |
| 12836 | | }); |
| 12837 | | }, |
| 12838 | | .copied_global => |gsi| try w.print("(copy:{s})", .{gsi.rawName(elf).slice(elf)}), |
| 12839 | | .nav => |nmi| { |
| 12840 | | const zcu = elf.base.comp.zcu.?; |
| 12841 | | const ip = &zcu.intern_pool; |
| 12842 | | const nav = ip.getNav(nmi.nav(elf)); |
| 12843 | | try w.print("({f}, {f})", .{ |
| 12844 | | Type.fromInterned(nav.resolved.?.type).fmt(zcu), |
| 12845 | | nav.fqn.fmt(ip), |
| 12846 | | }); |
| 12847 | | }, |
| 12848 | | .uav => |umi| { |
| 12849 | | const zcu = elf.base.comp.zcu.?; |
| 12850 | | const val: Value = .fromInterned(umi.uavValue(elf)); |
| 12851 | | try w.print("({f}, {f})", .{ |
| 12852 | | val.typeOf(zcu).fmt(zcu), |
| 12853 | | val.fmtValue(zcu), |
| 12854 | | }); |
| 12855 | | }, |
| 12856 | | inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{ |
| 12857 | | Type.fromInterned(lmi.lazySymbol(elf).ty).fmt(elf.base.comp.zcu.?), |
| 12858 | | }), |
| 12859 | | .debug_shared => |ss| try w.print("({})", .{ss}), |
| 12860 | | .unit_frame, |
| 12861 | | .unit_frame_cie, |
| 12862 | | .unit_debug_info, |
| 12863 | | .unit_debug_info_header, |
| 12864 | | .unit_debug_info_footer, |
| 12865 | | .unit_debug_line, |
| 12866 | | .unit_debug_line_header, |
| 12867 | | .unit_debug_rnglists, |
| 12868 | | => |ui| try w.print("({s})", .{ui.mod(&elf.dwarf).fully_qualified_name}), |
| 12869 | | .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) { |
| 12870 | | .generic_poison_type => try w.writeAll("(anytype)"), |
| 12871 | | else => |val| try w.print("({f})", .{ |
| 12872 | | Value.fromInterned(val).fmtValue(elf.base.comp.zcu.?), |
| 12873 | | }), |
| 12874 | | }, |
| 12875 | | .global_debug_info => |gi| { |
| 12876 | | const zcu = elf.base.comp.zcu.?; |
| 12877 | | const ip = &zcu.intern_pool; |
| 12878 | | const nav = ip.getNav(gi.nav(&elf.dwarf)); |
| 12879 | | try w.writeByte('('); |
| 12880 | | if (nav.resolved) |resolved| try w.print("{f}, ", .{ |
| 12881 | | Type.fromInterned(resolved.type).fmt(zcu), |
| 12882 | | }); |
| 12883 | | try w.print("{f}", .{nav.fqn.fmt(ip)}); |
| 12884 | | if (nav.resolved) |resolved| try w.print(", {f}", .{ |
| 12885 | | Value.fromInterned(resolved.value).fmtValue(zcu), |
| 12886 | | }); |
| 12887 | | try w.writeByte(')'); |
| 12888 | | }, |
| 12889 | | .func_frame_fde, .func_debug_info, .func_debug_line => |fi| { |
| 12890 | | const zcu = elf.base.comp.zcu.?; |
| 12891 | | const ip = &zcu.intern_pool; |
| 12892 | | const nav = ip.getNav(fi.nav(&elf.dwarf)); |
| 12893 | | try w.writeByte('('); |
| 12894 | | if (nav.resolved) |resolved| try w.print("{f}, ", .{ |
| 12895 | | Type.fromInterned(resolved.type).fmt(zcu), |
| 12896 | | }); |
| 12897 | | try w.print("{f})", .{nav.fqn.fmt(ip)}); |
| 12898 | | }, |
| 12899 | | .decl_debug_info => |di| { |
| 12900 | | const comp = elf.base.comp; |
| 12901 | | const zcu = comp.zcu.?; |
| 12902 | | const ip = &zcu.intern_pool; |
| 12903 | | const src_inst = di.srcInst(&elf.dwarf); |
| 12904 | | try w.print("({f}, ", .{zcu.fileByIndex(src_inst.resolveFile(ip)).path.fmt(comp)}); |
| 12905 | | if (src_inst.resolve(ip)) |inst| try w.print("%{d}", .{inst}) else try w.writeAll("lost"); |
| 12906 | | try w.writeByte(')'); |
| 12907 | | }, |
| 12908 | | } |
| 13026 | try w.print("{f}", .{Node.fmtDebug(ni, elf)}); |
| 12909 | 13027 | { |
| 12910 | 13028 | const mf_node = &elf.mf.nodes.items[@backingInt(ni)]; |
| 12911 | 13029 | const off, const size = mf_node.location().resolve(&elf.mf); |
| ... | ... | @@ -12975,15 +13093,8 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error |
| 12975 | 13093 | switch (elf.phdrSlice()) { |
| 12976 | 13094 | inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) { |
| 12977 | 13095 | .NULL, .LOAD => { |
| 12978 | | // The `@"align"` field is managed by `allocateSegmentLoadAddress`. |
| 12979 | | // |
| 12980 | | // It's very likely that the node was moved and/or resized when we realigned it |
| 12981 | | // just above, but it is possible that it was not moved *but* still has an |
| 12982 | | // unaligned virtual address. In that case, we need to ensure the segment's |
| 12983 | | // virtual address range will be recomputed. |
| 12984 | | if (!min_align.check(@intCast(elf.targetLoad(&phdr[phndx].vaddr)))) { |
| 12985 | | try seg_ni.moved(gpa, &elf.mf); |
| 12986 | | } |
| 13096 | // The `@"align"` field is managed by `updateSegmentLoadAddresses`, which will |
| 13097 | // be called at the end of the current link task. |
| 12987 | 13098 | }, |
| 12988 | 13099 | else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max( |
| 12989 | 13100 | elf.targetLoad(&phdr[phndx].@"align"), |
| ... | ... | @@ -13008,11 +13119,9 @@ pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) M |
| 13008 | 13119 | return ni; |
| 13009 | 13120 | } |
| 13010 | 13121 | |
| 13011 | | fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocator.Error!void { |
| 13012 | | const ni = node.unwrap().?; |
| 13013 | | try ni.delete(elf.base.comp.gpa, &elf.mf); |
| 13014 | | elf.nodes.set(@backingInt(ni), .deleted); |
| 13015 | | node.* = .none; |
| 13122 | fn deleteNode(elf: *Elf, node: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 13123 | try node.delete(elf.base.comp.gpa, &elf.mf); |
| 13124 | elf.nodes.set(@backingInt(node), .deleted); |
| 13016 | 13125 | } |
| 13017 | 13126 | |
| 13018 | 13127 | /// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a |