| ... | @@ -244,8 +244,9 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), | ... | @@ -244,8 +244,9 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), |
| 244 | changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void), | 244 | changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void), |
| 245 | /// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL` | 245 | /// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL` |
| 246 | /// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic` | 246 | /// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic` |
| 247 | /// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`. | 247 | /// section in `flush` only when it is actually necessary. See also `nodeAllowsDynamicReloc`. |
| 248 | textrel_count: u32, | 248 | textrel_count: u32, |
| | 249 | entry_gsi: Symbol.Global.Index.Optional, |
| 249 | | 250 | |
| 250 | dwarf: Dwarf, | 251 | dwarf: Dwarf, |
| 251 | dwarf_shared: std.enums.EnumArray(Dwarf.SharedSection, dwarf_relocs.Shared), | 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,6 +260,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl), |
| 259 | | 260 | |
| 260 | overflowed_reloc_count: u32, | 261 | overflowed_reloc_count: u32, |
| 261 | misaligned_reloc_count: u32, | 262 | misaligned_reloc_count: u32, |
| | 263 | illegal_dynamic_type_reloc_count: u32, |
| | 264 | illegal_dynamic_section_reloc_count: u32, |
| 262 | | 265 | |
| 263 | const_prog_node: std.Progress.Node, | 266 | const_prog_node: std.Progress.Node, |
| 264 | input_prog_node: std.Progress.Node, | 267 | input_prog_node: std.Progress.Node, |
| ... | @@ -308,9 +311,6 @@ const Node = union(enum) { | ... | @@ -308,9 +311,6 @@ const Node = union(enum) { |
| 308 | /// emitted a copy relocation. | 311 | /// emitted a copy relocation. |
| 309 | /// | 312 | /// |
| 310 | /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those. | 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 | copied_global: Symbol.Global.Index, | 314 | copied_global: Symbol.Global.Index, |
| 315 | /// May contain relocations. | 315 | /// May contain relocations. |
| 316 | nav: NavMapIndex, | 316 | nav: NavMapIndex, |
| ... | @@ -464,6 +464,135 @@ const Node = union(enum) { | ... | @@ -464,6 +464,135 @@ const Node = union(enum) { |
| 464 | fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index { | 464 | fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index { |
| 465 | return @fromBackingInt(@backingInt(atom)); | 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 | const InputSection = struct { | 598 | const InputSection = struct { |
| ... | @@ -947,10 +1076,14 @@ const dwarf_relocs = struct { | ... | @@ -947,10 +1076,14 @@ const dwarf_relocs = struct { |
| 947 | const Addr = struct { | 1076 | const Addr = struct { |
| 948 | first_target_reloc: NodeReloc.Index, | 1077 | first_target_reloc: NodeReloc.Index, |
| 949 | symbol_relocs: std.ArrayList(SymbolReloc.Index), | 1078 | symbol_relocs: std.ArrayList(SymbolReloc.Index), |
| | 1079 | /// Index into `symbol_relocs`. |
| | 1080 | pending_reloc_index: usize, |
| 950 | }; | 1081 | }; |
| 951 | const StrOffsets = struct { | 1082 | const StrOffsets = struct { |
| 952 | first_target_reloc: NodeReloc.Index, | 1083 | first_target_reloc: NodeReloc.Index, |
| 953 | node_relocs: std.ArrayList(NodeReloc.Index), | 1084 | node_relocs: std.ArrayList(NodeReloc.Index), |
| | 1085 | /// Index into `node_relocs`. |
| | 1086 | pending_reloc_index: usize, |
| 954 | }; | 1087 | }; |
| 955 | const Unit = struct { | 1088 | const Unit = struct { |
| 956 | frame_cie_first_target_reloc: NodeReloc.Index, | 1089 | frame_cie_first_target_reloc: NodeReloc.Index, |
| ... | @@ -1279,7 +1412,13 @@ const SymbolReloc = struct { | ... | @@ -1279,7 +1412,13 @@ const SymbolReloc = struct { |
| 1279 | /// relocation entry. The entry will be removed if we discover a definition which allows us to | 1412 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 1280 | /// statically resolve the relocation. | 1413 | /// statically resolve the relocation. |
| 1281 | rela_index: Section.RelaIndex.Optional, | 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 | /// Determines the section in which this relocation will be placed if it is outstanding. | 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,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 | fn apply(reloc: *SymbolReloc, elf: *Elf) void { | 1955 | fn apply(reloc: *SymbolReloc, elf: *Elf) void { |
| 1831 | assert(elf.ehdrType() != .REL); | | |
| 1832 | const node = reloc.node.unwrap() orelse return; // deleted | 1956 | const node = reloc.node.unwrap() orelse return; // deleted |
| 1833 | if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { | 1957 | if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1834 | // There's no point applying the relocation now, because it will be re-applied by | 1958 | // There's no point applying the relocation now, because it will be re-applied by |
| 1835 | // `flushMoved` at some point anyway. | 1959 | // `flushMoved` at some point anyway. |
| 1836 | return; | 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 | switch (reloc.result) { | 1969 | switch (reloc.result) { |
| 1839 | .ok => {}, | 1970 | .ok => {}, |
| 1840 | .overflowed => elf.overflowed_reloc_count -= 1, | 1971 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1841 | .misaligned => elf.misaligned_reloc_count -= 1, | 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 | if (reloc.applyInner(elf)) { | 1978 | if (reloc.applyInner(elf)) { |
| 1844 | @branchHint(.likely); | 1979 | @branchHint(.likely); |
| ... | @@ -1900,7 +2035,11 @@ const SymbolReloc = struct { | ... | @@ -1900,7 +2035,11 @@ const SymbolReloc = struct { |
| 1900 | // is required, meaning we can handle it now and return early. | 2035 | // is required, meaning we can handle it now and return early. |
| 1901 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { | 2036 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { |
| 1902 | .static => unreachable, | 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 | .static_relative => { | 2043 | .static_relative => { |
| 1905 | // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address | 2044 | // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address |
| 1906 | // relocation. The value computed above is valid, but instead of writing it to the | 2045 | // relocation. The value computed above is valid, but instead of writing it to the |
| ... | @@ -1912,6 +2051,7 @@ const SymbolReloc = struct { | ... | @@ -1912,6 +2051,7 @@ const SymbolReloc = struct { |
| 1912 | } | 2051 | } |
| 1913 | assert(reloc.type.action.simple.cast == .unsigned); | 2052 | assert(reloc.type.action.simple.cast == .unsigned); |
| 1914 | assert(reloc.type.action.simple.shift == .@"0"); | 2053 | assert(reloc.type.action.simple.shift == .@"0"); |
| | 2054 | elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr); |
| 1915 | elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val); | 2055 | elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val); |
| 1916 | return; | 2056 | return; |
| 1917 | }, | 2057 | }, |
| ... | @@ -1944,6 +2084,8 @@ const SymbolReloc = struct { | ... | @@ -1944,6 +2084,8 @@ const SymbolReloc = struct { |
| 1944 | .ok => {}, | 2084 | .ok => {}, |
| 1945 | .overflowed => elf.overflowed_reloc_count -= 1, | 2085 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1946 | .misaligned => elf.misaligned_reloc_count -= 1, | 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 | reloc.* = undefined; | 2091 | reloc.* = undefined; |
| ... | @@ -1957,14 +2099,211 @@ const SymbolReloc = struct { | ... | @@ -1957,14 +2099,211 @@ const SymbolReloc = struct { |
| 1957 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); | 2099 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 1958 | switch (elf.ehdrType()) { | 2100 | switch (elf.ehdrType()) { |
| 1959 | .REL => {}, | 2101 | .REL => {}, |
| 1960 | .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) { | 2102 | .EXEC, .DYN => switch (elf.nodeAllowsDynamicReloc(reloc.node.unwrap().?)) { |
| 1961 | .no => unreachable, // there *was* a dynamic relocation! | 2103 | .not_alloc => unreachable, // there *was* a dynamic relocation! |
| 1962 | .yes => {}, | 2104 | .ok => {}, |
| 1963 | .yes_textrel => elf.textrel_count -= 1, | 2105 | .ok_textrel => elf.textrel_count -= 1, |
| 1964 | }, | 2106 | }, |
| 1965 | } | 2107 | } |
| 1966 | reloc.rela_index = .none; | 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 | /// A relocation targeting an arbitrary node (within a section) with a fixed addend. | 2309 | /// A relocation targeting an arbitrary node (within a section) with a fixed addend. |
| ... | @@ -1992,34 +2331,14 @@ const NodeReloc = struct { | ... | @@ -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 | fn apply(reloc: *NodeReloc, elf: *Elf) void { | 2334 | fn apply(reloc: *NodeReloc, elf: *Elf) void { |
| 2019 | const node = reloc.node.unwrap() orelse return; // deleted | 2335 | const node = reloc.node.unwrap() orelse return; // deleted |
| 2020 | if (reloc.rela_index.unwrap()) |rela_index| { | 2336 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 2021 | assert(elf.ehdrType() == .REL); | 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 | } else { | 2342 | } else { |
| 2024 | assert(elf.ehdrType() != .REL); | 2343 | assert(elf.ehdrType() != .REL); |
| 2025 | if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) { | 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,6 +3074,8 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 2755 | } || Error)!Symbol.Global.Index { | 3074 | } || Error)!Symbol.Global.Index { |
| 2756 | _ = opts.lib_name; // TODO | 3075 | _ = opts.lib_name; // TODO |
| 2757 | | 3076 | |
| | 3077 | log.debug("addGlobalSymbol({q})", .{opts.name}); |
| | 3078 | |
| 2758 | const gpa = elf.base.comp.gpa; | 3079 | const gpa = elf.base.comp.gpa; |
| 2759 | | 3080 | |
| 2760 | try elf.symtab.ensureUnusedCapacity(gpa, 1); | 3081 | try elf.symtab.ensureUnusedCapacity(gpa, 1); |
| ... | @@ -2802,9 +3123,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ | ... | @@ -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)) { | 3126 | elf.mergeGlobalSymbolVisibility(gsi, opts.visibility); |
| 2806 | force_apply_relocs = true; | | |
| 2807 | } | | |
| 2808 | | 3127 | |
| 2809 | // There's already a symbol with this name, we just need to potentially set its value, and | 3128 | // There's already a symbol with this name, we just need to potentially set its value, and |
| 2810 | // to update its bind and visibility. | 3129 | // to update its bind and visibility. |
| ... | @@ -2988,10 +3307,15 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ | ... | @@ -2988,10 +3307,15 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 2988 | /// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as | 3307 | /// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as |
| 2989 | /// `true` to guarantee this function to do so. | 3308 | /// `true` to guarantee this function to do so. |
| 2990 | fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void { | 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 | const gpa = elf.base.comp.gpa; | 3313 | const gpa = elf.base.comp.gpa; |
| 2992 | | 3314 | |
| 2993 | const gsi = orig_gsi.resolveAlias(elf); | 3315 | const gsi = orig_gsi.resolveAlias(elf); |
| 2994 | | 3316 | |
| | 3317 | trace.addText(gsi.rawName(elf).slice(elf)); |
| | 3318 | |
| 2995 | const maybe_alias_gsi = gsi.findAliaser(elf); | 3319 | const maybe_alias_gsi = gsi.findAliaser(elf); |
| 2996 | | 3320 | |
| 2997 | var apply_relocs: bool = force_apply_relocs; | 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,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: { | 3382 | const have_dynsym: bool = switch (elf.shndx.dynsym) { |
| 3059 | if (elf.shndx.dynsym == .UNDEF) break :omit true; | 3383 | .UNDEF => false, |
| 3060 | break :omit switch (visibility) { | 3384 | else => switch (visibility) { |
| 3061 | .HIDDEN, .INTERNAL => true, | 3385 | .HIDDEN, .INTERNAL => false, |
| 3062 | .DEFAULT, .PROTECTED => false, | 3386 | // An undefined protected dynamic symbol would be pointless, because protected symbols |
| 3063 | }; | 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 | if (try elf.deleteOwnedDynsym(gsi)) { | 3399 | if (try elf.deleteOwnedDynsym(gsi)) { |
| 3067 | apply_relocs = true; | 3400 | apply_relocs = true; |
| 3068 | } | 3401 | } |
| ... | @@ -3089,9 +3422,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel | ... | @@ -3089,9 +3422,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3089 | if (elf.ehdrType() != .REL) { | 3422 | if (elf.ehdrType() != .REL) { |
| 3090 | assert(elf.classifySymbolValue(.global(gsi)) != .dynamic); | 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 | if (maybe_alias_gsi) |alias_gsi| { | 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 | if (apply_relocs) { | 3435 | if (apply_relocs) { |
| ... | @@ -3335,10 +3673,8 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel | ... | @@ -3335,10 +3673,8 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3335 | } else { | 3673 | } else { |
| 3336 | if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| { | 3674 | if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| { |
| 3337 | // We previously made a copy relocation, but now have deemed it unnecessary. | 3675 | // We previously made a copy relocation, but now have deemed it unnecessary. |
| | 3676 | try elf.deleteNode(copied_global_kv.value.node); |
| 3338 | elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index); | 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 | apply_relocs = true; | 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,10 +3688,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel |
| 3352 | _ = elf.unknown_globals.swapRemove(gsi); | 3688 | _ = elf.unknown_globals.swapRemove(gsi); |
| 3353 | } | 3689 | } |
| 3354 | | 3690 | |
| 3355 | if (elf.classifySymbolValue(.global(gsi)) != .dynamic) { | 3691 | if (try gsi.updateDynamicTargetRelocs(elf)) { |
| 3356 | gsi.deleteDynamicTargetRelocs(elf); | 3692 | apply_relocs = true; |
| 3357 | if (maybe_alias_gsi) |alias_gsi| { | 3693 | } |
| 3358 | alias_gsi.deleteDynamicTargetRelocs(elf); | 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,15 +3816,12 @@ fn setGlobalSymbolBind( |
| 3478 | /// undefined---their visibility values are combined to determine the resulting visibility. | 3816 | /// undefined---their visibility values are combined to determine the resulting visibility. |
| 3479 | /// | 3817 | /// |
| 3480 | /// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes | 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`, | 3819 | /// to symbol visibility can affect the dynamic symbol table entry. |
| 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. | | |
| 3485 | fn mergeGlobalSymbolVisibility( | 3820 | fn mergeGlobalSymbolVisibility( |
| 3486 | elf: *Elf, | 3821 | elf: *Elf, |
| 3487 | gsi: Symbol.Global.Index, | 3822 | gsi: Symbol.Global.Index, |
| 3488 | other_visibility: std.elf.STV, | 3823 | other_visibility: std.elf.STV, |
| 3489 | ) bool { | 3824 | ) void { |
| 3490 | const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) { | 3825 | const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) { |
| 3491 | inline else => |sym| elf.targetLoad(&sym.other).visibility, | 3826 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 3492 | }; | 3827 | }; |
| ... | @@ -3518,13 +3853,13 @@ fn mergeGlobalSymbolVisibility( | ... | @@ -3518,13 +3853,13 @@ fn mergeGlobalSymbolVisibility( |
| 3518 | ), | 3853 | ), |
| 3519 | } | 3854 | } |
| 3520 | | 3855 | |
| 3521 | if (elf.ehdrType() == .REL) { | 3856 | if (elf.ehdrType() == .REL) return; |
| 3522 | return false; | | |
| 3523 | } | | |
| 3524 | | 3857 | |
| 3525 | // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or | 3858 | // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or |
| 3526 | // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to | 3859 | // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to |
| 3527 | // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output. | 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 | const old_demoted: bool = switch (old_visibility) { | 3863 | const old_demoted: bool = switch (old_visibility) { |
| 3529 | .INTERNAL, .HIDDEN => true, | 3864 | .INTERNAL, .HIDDEN => true, |
| 3530 | .DEFAULT, .PROTECTED => false, | 3865 | .DEFAULT, .PROTECTED => false, |
| ... | @@ -3542,16 +3877,6 @@ fn mergeGlobalSymbolVisibility( | ... | @@ -3542,16 +3877,6 @@ fn mergeGlobalSymbolVisibility( |
| 3542 | } | 3877 | } |
| 3543 | elf.moveDemotedGlobal(gsi); | 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 | /// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`), | 3881 | /// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`), |
| 3557 | /// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF | 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,10 +3938,8 @@ fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Erro |
| 3613 | | 3938 | |
| 3614 | var apply_relocs: bool = false; | 3939 | var apply_relocs: bool = false; |
| 3615 | if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| { | 3940 | if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| { |
| | 3941 | try elf.deleteNode(copied_global_kv.value.node); |
| 3616 | elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index); | 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 | apply_relocs = true; | 3943 | apply_relocs = true; |
| 3621 | } | 3944 | } |
| 3622 | if (elf.plt.getIndex(gsi)) |plt_index| { | 3945 | if (elf.plt.getIndex(gsi)) |plt_index| { |
| ... | @@ -3930,85 +4253,52 @@ const Symbol = struct { | ... | @@ -3930,85 +4253,52 @@ const Symbol = struct { |
| 3930 | return null; | 4253 | return null; |
| 3931 | } | 4254 | } |
| 3932 | | 4255 | |
| 3933 | /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation | 4256 | /// Updates the dynamic relocations associated with relocations targeting `gsi`, adding |
| 3934 | /// entries, and adds `R_*_RELATIVE` relocation entries as needed. | 4257 | /// or removing them as needed based on the current classification of `gsi`. |
| 3935 | /// | 4258 | /// |
| 3936 | /// Asserts we are creating a DSO. | 4259 | /// Returns `true` if any relocations were updated, in which case relocations targeting |
| 3937 | fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void { | 4260 | /// `gsi` must be re-applied by the caller. |
| 3938 | if (elf.shndx.dynamic == .UNDEF) return; | 4261 | fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool { |
| | 4262 | if (elf.shndx.dynamic == .UNDEF) return false; |
| 3939 | const sym_id: Symbol.Id = .global(gsi); | 4263 | const sym_id: Symbol.Id = .global(gsi); |
| | 4264 | |
| 3940 | switch (elf.classifySymbolValue(sym_id)) { | 4265 | switch (elf.classifySymbolValue(sym_id)) { |
| 3941 | .static => { | 4266 | .static => { |
| 3942 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and | 4267 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and |
| 3943 | !gsi.ptr(elf).status.any_static_relative_target_relocs) | 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 | .static_relative => { | 4273 | .static_relative => { |
| 3952 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and | 4274 | if (!gsi.ptr(elf).status.any_dynamic_target_relocs and |
| 3953 | !gsi.ptr(elf).status.any_static_target_relocs) | 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 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; | 4293 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 3967 | while (ri != .none) { | 4294 | while (ri != .none) { |
| 3968 | const reloc = ri.get(elf); | 4295 | const reloc = ri.get(elf); |
| 3969 | assert(reloc.target == sym_id); | 4296 | assert(reloc.target == sym_id); |
| 3970 | reloc.deleteOutputRel(elf); | 4297 | try reloc.updateDynamicReloc(elf); |
| 3971 | ri = reloc.next; | 4298 | ri = reloc.next; |
| 3972 | } | 4299 | } |
| 3973 | switch (elf.classifySymbolValue(sym_id)) { | 4300 | |
| 3974 | .static => return, | 4301 | return true; |
| 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 | } | | |
| 4012 | } | 4302 | } |
| 4013 | | 4303 | |
| 4014 | const Optional = enum(u32) { | 4304 | const Optional = enum(u32) { |
| ... | @@ -4139,7 +4429,20 @@ const Symbol = struct { | ... | @@ -4139,7 +4429,20 @@ const Symbol = struct { |
| 4139 | } | 4429 | } |
| 4140 | | 4430 | |
| 4141 | fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void { | 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 | const sym_index = sym_id.index(elf); | 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 | switch (elf.symPtr(sym_index)) { | 4446 | switch (elf.symPtr(sym_index)) { |
| 4144 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), | 4447 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 4145 | } | 4448 | } |
| ... | @@ -4164,14 +4467,12 @@ const Symbol = struct { | ... | @@ -4164,14 +4467,12 @@ const Symbol = struct { |
| 4164 | } | 4467 | } |
| 4165 | | 4468 | |
| 4166 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { | 4469 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 4167 | if (elf.ehdrType() != .REL) { | 4470 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4168 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; | 4471 | while (ri != .none) { |
| 4169 | while (ri != .none) { | 4472 | const reloc = ri.get(elf); |
| 4170 | const reloc = ri.get(elf); | 4473 | assert(reloc.target == sym_id); |
| 4171 | assert(reloc.target == sym_id); | 4474 | reloc.apply(elf); |
| 4172 | reloc.apply(elf); | 4475 | ri = reloc.next; |
| 4173 | ri = reloc.next; | | |
| 4174 | } | | |
| 4175 | } | 4476 | } |
| 4176 | | 4477 | |
| 4177 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { | 4478 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { |
| ... | @@ -4251,61 +4552,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { | ... | @@ -4251,61 +4552,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 4251 | .EXEC => false, | 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 | const resolved_sym = sym.resolveAlias(elf); | 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 | inline else => |sym_ptr| .{ | 4558 | inline else => |sym_ptr| .{ |
| 4263 | .fromSection(elf.targetLoad(&sym_ptr.shndx)), | 4559 | .fromSection(elf.targetLoad(&sym_ptr.shndx)), |
| 4264 | elf.targetLoad(&sym_ptr.other).visibility, | 4560 | elf.targetLoad(&sym_ptr.other).visibility, |
| 4265 | }, | 4561 | }, |
| 4266 | }; | 4562 | }; |
| 4267 | | 4563 | |
| 4268 | switch (resolved_sym.unwrap()) { | 4564 | const shndx: Section.Index = switch (resolved_sym.unwrap()) { |
| 4269 | .local => { | 4565 | .local => shndx: { |
| 4270 | assert(shndx != .UNDEF); | 4566 | assert(symtab_shndx != .UNDEF); |
| 4271 | assert(visibility == .DEFAULT); | 4567 | assert(visibility == .DEFAULT); |
| | 4568 | break :shndx symtab_shndx; |
| 4272 | }, | 4569 | }, |
| 4273 | .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) { | 4570 | .global => |gsi| shndx: { |
| 4274 | // An unprotected symbol in a DSO which is not an executable is subject to runtime | 4571 | const shndx: Section.Index = switch (symtab_shndx) { |
| 4275 | // preemption, so a dynamic relocation is required for it even if we have a definition. | 4572 | .UNDEF => if (elf.copied_globals.contains(gsi)) .data else .UNDEF, |
| 4276 | return .dynamic; | 4573 | else => |shndx| shndx, |
| 4277 | } else if (elf.copied_globals.contains(gsi)) { | 4574 | }; |
| 4278 | // This becomes a locally-defined symbol in `.data`. | | |
| 4279 | return if (runtime_load_addr) .static_relative else .static; | | |
| 4280 | }, | | |
| 4281 | } | | |
| 4282 | | 4575 | |
| 4283 | return switch (shndx) { | 4576 | // Check for dynamic symbols before falling into the static path. |
| 4284 | .UNDEF => switch (visibility) { | 4577 | if (gsi.dynsymIndex(elf) != null and visibility == .DEFAULT) { |
| 4285 | .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) { | 4578 | if (shndx == .UNDEF) { |
| 4286 | assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section | 4579 | // There is no local definition of this symbol. |
| 4287 | // This is a static PIE---we'll see a definition at some point, so there'd be no | 4580 | return .dynamic; |
| 4288 | // point in adding an invalid dynamic relocation (the only valid dynamic relation | 4581 | } else if (comp.config.output_mode != .Exe) { |
| 4289 | // type in a static PIE is `R_*_RELATIVE`). | 4582 | // There is a local definition, but it is preemptible at runtime. |
| 4290 | return .static; | 4583 | return .dynamic; |
| 4291 | } else .dynamic, // external symbol | 4584 | } |
| | 4585 | } |
| 4292 | | 4586 | |
| 4293 | // If the symbol *cannot* be external, then there's no point making a dynamic relocation | 4587 | break :shndx shndx; |
| 4294 | // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`. | | |
| 4295 | .INTERNAL, .HIDDEN, .PROTECTED => .static, | | |
| 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 | 4601 | return .static; |
| 4301 | shndx.flags(elf).ALLOC and | | |
| 4302 | !shndx.flags(elf).TLS) | | |
| 4303 | { | | |
| 4304 | return .static_relative; | | |
| 4305 | } else { | | |
| 4306 | return .static; | | |
| 4307 | }, | | |
| 4308 | }; | | |
| 4309 | } | 4602 | } |
| 4310 | | 4603 | |
| 4311 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { | 4604 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| ... | @@ -4848,6 +5141,7 @@ fn create( | ... | @@ -4848,6 +5141,7 @@ fn create( |
| 4848 | .section_by_name = .empty, | 5141 | .section_by_name = .empty, |
| 4849 | .changed_symtab_index = .empty, | 5142 | .changed_symtab_index = .empty, |
| 4850 | .textrel_count = 0, | 5143 | .textrel_count = 0, |
| | 5144 | .entry_gsi = undefined, |
| 4851 | | 5145 | |
| 4852 | .dwarf = .init(&elf.base, switch (comp.config.debug_format) { | 5146 | .dwarf = .init(&elf.base, switch (comp.config.debug_format) { |
| 4853 | .strip => .@"32", // for .eh_frame | 5147 | .strip => .@"32", // for .eh_frame |
| ... | @@ -4860,10 +5154,12 @@ fn create( | ... | @@ -4860,10 +5154,12 @@ fn create( |
| 4860 | .dwarf_addr = .{ | 5154 | .dwarf_addr = .{ |
| 4861 | .first_target_reloc = .none, | 5155 | .first_target_reloc = .none, |
| 4862 | .symbol_relocs = .empty, | 5156 | .symbol_relocs = .empty, |
| | 5157 | .pending_reloc_index = 0, |
| 4863 | }, | 5158 | }, |
| 4864 | .dwarf_str_offsets = .{ | 5159 | .dwarf_str_offsets = .{ |
| 4865 | .first_target_reloc = .none, | 5160 | .first_target_reloc = .none, |
| 4866 | .node_relocs = .empty, | 5161 | .node_relocs = .empty, |
| | 5162 | .pending_reloc_index = 0, |
| 4867 | }, | 5163 | }, |
| 4868 | .dwarf_units = &.{}, | 5164 | .dwarf_units = &.{}, |
| 4869 | .dwarf_consts = .empty, | 5165 | .dwarf_consts = .empty, |
| ... | @@ -4873,6 +5169,8 @@ fn create( | ... | @@ -4873,6 +5169,8 @@ fn create( |
| 4873 | | 5169 | |
| 4874 | .overflowed_reloc_count = 0, | 5170 | .overflowed_reloc_count = 0, |
| 4875 | .misaligned_reloc_count = 0, | 5171 | .misaligned_reloc_count = 0, |
| | 5172 | .illegal_dynamic_type_reloc_count = 0, |
| | 5173 | .illegal_dynamic_section_reloc_count = 0, |
| 4876 | | 5174 | |
| 4877 | .const_prog_node = .none, | 5175 | .const_prog_node = .none, |
| 4878 | .input_prog_node = .none, | 5176 | .input_prog_node = .none, |
| ... | @@ -5078,9 +5376,10 @@ fn initHeaders( | ... | @@ -5078,9 +5376,10 @@ fn initHeaders( |
| 5078 | defer phnum += 1; | 5376 | defer phnum += 1; |
| 5079 | break :phndx phnum; | 5377 | break :phndx phnum; |
| 5080 | } else undefined, | 5378 | } else undefined, |
| 5081 | // `data` must be assigned after all other loadable segments so that it has the greatest | 5379 | // `data` must be assigned after all other loadable segments so that it has the last |
| 5082 | // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions` | 5380 | // phndx of the loadable segments. This causes `updateSegmentLoadAddresses` to put |
| 5083 | // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC). | 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 | .data = phndx: { | 5383 | .data = phndx: { |
| 5085 | defer phnum += 1; | 5384 | defer phnum += 1; |
| 5086 | break :phndx phnum; | 5385 | break :phndx phnum; |
| ... | @@ -5214,7 +5513,7 @@ fn initHeaders( | ... | @@ -5214,7 +5513,7 @@ fn initHeaders( |
| 5214 | // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node | 5513 | // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node |
| 5215 | .alignment = node_block_align.max(addr_align), | 5514 | .alignment = node_block_align.max(addr_align), |
| 5216 | .moved = true, | 5515 | .moved = true, |
| 5217 | .bubbles_moved = false, | 5516 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5218 | }), .{ .segment = phndx.rodata }); | 5517 | }), .{ .segment = phndx.rodata }); |
| 5219 | elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata); | 5518 | elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata); |
| 5220 | | 5519 | |
| ... | @@ -5223,14 +5522,13 @@ fn initHeaders( | ... | @@ -5223,14 +5522,13 @@ fn initHeaders( |
| 5223 | .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above | 5522 | .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above |
| 5224 | .moved = true, | 5523 | .moved = true, |
| 5225 | .resized = true, | 5524 | .resized = true, |
| 5226 | .bubbles_moved = false, | | |
| 5227 | }), .{ .segment = phndx.phdr }); | 5525 | }), .{ .segment = phndx.phdr }); |
| 5228 | elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr); | 5526 | elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr); |
| 5229 | | 5527 | |
| 5230 | elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ | 5528 | elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ |
| 5231 | .alignment = node_block_align, | 5529 | .alignment = node_block_align, |
| 5232 | .moved = true, | 5530 | .moved = true, |
| 5233 | .bubbles_moved = false, | 5531 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5234 | }), .{ .segment = phndx.text }); | 5532 | }), .{ .segment = phndx.text }); |
| 5235 | elf.phdrs.items[phndx.text] = .wrap(elf.ni.text); | 5533 | elf.phdrs.items[phndx.text] = .wrap(elf.ni.text); |
| 5236 | | 5534 | |
| ... | @@ -5238,7 +5536,7 @@ fn initHeaders( | ... | @@ -5238,7 +5536,7 @@ fn initHeaders( |
| 5238 | // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node | 5536 | // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node |
| 5239 | .alignment = node_block_align.max(addr_align), | 5537 | .alignment = node_block_align.max(addr_align), |
| 5240 | .moved = true, | 5538 | .moved = true, |
| 5241 | .bubbles_moved = false, | 5539 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5242 | }), .{ .segment = phndx.data }); | 5540 | }), .{ .segment = phndx.data }); |
| 5243 | elf.phdrs.items[phndx.data] = .wrap(elf.ni.data); | 5541 | elf.phdrs.items[phndx.data] = .wrap(elf.ni.data); |
| 5244 | | 5542 | |
| ... | @@ -5246,7 +5544,7 @@ fn initHeaders( | ... | @@ -5246,7 +5544,7 @@ fn initHeaders( |
| 5246 | try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ | 5544 | try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ |
| 5247 | .alignment = node_block_align, | 5545 | .alignment = node_block_align, |
| 5248 | .moved = true, | 5546 | .moved = true, |
| 5249 | .bubbles_moved = false, | 5547 | .bubbles_moved = false, // see loadable segment handling in `flushMoved` |
| 5250 | }), | 5548 | }), |
| 5251 | .{ .segment = phndx.plt }, | 5549 | .{ .segment = phndx.plt }, |
| 5252 | )); | 5550 | )); |
| ... | @@ -5256,7 +5554,6 @@ fn initHeaders( | ... | @@ -5256,7 +5554,6 @@ fn initHeaders( |
| 5256 | // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above. | 5554 | // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above. |
| 5257 | .alignment = node_block_align.max(addr_align), | 5555 | .alignment = node_block_align.max(addr_align), |
| 5258 | .moved = true, | 5556 | .moved = true, |
| 5259 | .bubbles_moved = false, | | |
| 5260 | }), .{ .segment = phndx.relro }); | 5557 | }), .{ .segment = phndx.relro }); |
| 5261 | elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro); | 5558 | elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro); |
| 5262 | | 5559 | |
| ... | @@ -5265,7 +5562,7 @@ fn initHeaders( | ... | @@ -5265,7 +5562,7 @@ fn initHeaders( |
| 5265 | try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ | 5562 | try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ |
| 5266 | .alignment = node_block_align, | 5563 | .alignment = node_block_align, |
| 5267 | .moved = true, | 5564 | .moved = true, |
| 5268 | .bubbles_moved = false, | 5565 | .bubbles_moved = false, // see TLS segment handling in `flushMoved` |
| 5269 | }), | 5566 | }), |
| 5270 | .{ .segment = phndx.tls }, | 5567 | .{ .segment = phndx.tls }, |
| 5271 | )); | 5568 | )); |
| ... | @@ -5358,25 +5655,10 @@ fn initHeaders( | ... | @@ -5358,25 +5655,10 @@ fn initHeaders( |
| 5358 | const ElfN = ct_class.ElfN(); | 5655 | const ElfN = ct_class.ElfN(); |
| 5359 | const target_endian = elf.targetEndian(); | 5656 | const target_endian = elf.targetEndian(); |
| 5360 | | 5657 | |
| 5361 | populate_phdrs: { | 5658 | if (@"type" != .REL) { |
| 5362 | // Initially we will give every `PT_LOAD` segment this address. When we re-allocate | 5659 | // Initially we will give every `PT_LOAD` segment the base address. They will be |
| 5363 | // segments in the virtual address space in `flushMoved` and `flushResized`, we will | 5660 | // moved to non-overlapping addresses by `updateSegmentLoadAddresses`. |
| 5364 | // move some segments to higher addresses to prevent overlap. This address therefore | 5661 | const base_vaddr: u64 = elf.baseLoadableSegmentVAddr(); |
| 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 | }; | | |
| 5380 | | 5662 | |
| 5381 | // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the | 5663 | // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the |
| 5382 | // binary, their *nodes* are not aligned to this boundary---ELF only requires that | 5664 | // binary, their *nodes* are not aligned to this boundary---ELF only requires that |
| ... | @@ -5683,7 +5965,6 @@ fn initHeaders( | ... | @@ -5683,7 +5965,6 @@ fn initHeaders( |
| 5683 | .size = interp.len + 1, | 5965 | .size = interp.len + 1, |
| 5684 | .moved = true, | 5966 | .moved = true, |
| 5685 | .resized = true, | 5967 | .resized = true, |
| 5686 | .bubbles_moved = false, | | |
| 5687 | }), | 5968 | }), |
| 5688 | .{ .segment = phndx.interp }, | 5969 | .{ .segment = phndx.interp }, |
| 5689 | ); | 5970 | ); |
| ... | @@ -5705,7 +5986,6 @@ fn initHeaders( | ... | @@ -5705,7 +5986,6 @@ fn initHeaders( |
| 5705 | try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{ | 5986 | try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{ |
| 5706 | .alignment = addr_align, | 5987 | .alignment = addr_align, |
| 5707 | .moved = true, | 5988 | .moved = true, |
| 5708 | .bubbles_moved = false, | | |
| 5709 | }), | 5989 | }), |
| 5710 | .{ .segment = phndx.dynamic }, | 5990 | .{ .segment = phndx.dynamic }, |
| 5711 | ); | 5991 | ); |
| ... | @@ -5959,7 +6239,6 @@ fn initHeaders( | ... | @@ -5959,7 +6239,6 @@ fn initHeaders( |
| 5959 | .size = @sizeOf(Dwarf.EhFrameHdr), | 6239 | .size = @sizeOf(Dwarf.EhFrameHdr), |
| 5960 | .alignment = .@"4", | 6240 | .alignment = .@"4", |
| 5961 | .moved = true, | 6241 | .moved = true, |
| 5962 | .bubbles_moved = false, | | |
| 5963 | }), | 6242 | }), |
| 5964 | .{ .segment = phndx.gnu_eh_frame }, | 6243 | .{ .segment = phndx.gnu_eh_frame }, |
| 5965 | ); | 6244 | ); |
| ... | @@ -6272,6 +6551,31 @@ fn initHeaders( | ... | @@ -6272,6 +6551,31 @@ fn initHeaders( |
| 6272 | .code_view => unreachable, | 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 | assert(elf.nodes.len == expected_nodes_len); | 6579 | assert(elf.nodes.len == expected_nodes_len); |
| 6276 | assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF | 6580 | assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF |
| 6277 | | 6581 | |
| ... | @@ -6303,21 +6607,7 @@ fn initHeaders( | ... | @@ -6303,21 +6607,7 @@ fn initHeaders( |
| 6303 | }, | 6607 | }, |
| 6304 | }; | 6608 | }; |
| 6305 | | 6609 | |
| 6306 | if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) { | 6610 | try elf.updateSegmentLoadAddresses(); |
| 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 | }; | | |
| 6321 | } | 6611 | } |
| 6322 | | 6612 | |
| 6323 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { | 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,43 +6772,6 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6482 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); | 6772 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| 6483 | return parent_vaddr + offset; | 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 | fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { | 6775 | fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6523 | return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset; | 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,7 +6913,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 6660 | fn flushMovedNodeRelocs( | 6913 | fn flushMovedNodeRelocs( |
| 6661 | elf: *Elf, | 6914 | elf: *Elf, |
| 6662 | node: MappedFile.Node.Index, | 6915 | node: MappedFile.Node.Index, |
| 6663 | node_vaddr: u64, | | |
| 6664 | opts: struct { | 6916 | opts: struct { |
| 6665 | first_symbol_reloc: SymbolReloc.Index = .none, | 6917 | first_symbol_reloc: SymbolReloc.Index = .none, |
| 6666 | skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, | 6918 | skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, |
| ... | @@ -6676,7 +6928,7 @@ fn flushMovedNodeRelocs( | ... | @@ -6676,7 +6928,7 @@ fn flushMovedNodeRelocs( |
| 6676 | if (reloc.node == opts.skip_symbol_relocs) continue; | 6928 | if (reloc.node == opts.skip_symbol_relocs) continue; |
| 6677 | break; | 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,7 +6939,7 @@ fn flushMovedNodeRelocs( |
| 6687 | if (reloc.node == opts.skip_node_relocs) continue; | 6939 | if (reloc.node == opts.skip_node_relocs) continue; |
| 6688 | break; | 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,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 | fn targetPtrSize(elf: *const Elf) u8 { | 7025 | fn targetPtrSize(elf: *const Elf) u8 { |
| 6759 | return elf.identClass().size(); | 7026 | return elf.identClass().size(); |
| 6760 | } | 7027 | } |
| ... | @@ -6873,30 +7140,6 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { | ... | @@ -6873,30 +7140,6 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { |
| 6873 | // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`. | 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 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { | 7143 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 6901 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; | 7144 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 6902 | const Child = pointer_ty.child; | 7145 | const Child = pointer_ty.child; |
| ... | @@ -7326,6 +7569,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!v | ... | @@ -7326,6 +7569,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!v |
| 7326 | .{elf.mf.io_err.?}, | 7569 | .{elf.mf.io_err.?}, |
| 7327 | ), | 7570 | ), |
| 7328 | }; | 7571 | }; |
| | 7572 | try elf.updateSegmentLoadAddresses(); |
| 7329 | } | 7573 | } |
| 7330 | fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void { | 7574 | fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void { |
| 7331 | const comp = elf.base.comp; | 7575 | const comp = elf.base.comp; |
| ... | @@ -8605,6 +8849,7 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { | ... | @@ -8605,6 +8849,7 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { |
| 8605 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), | 8849 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), |
| 8606 | else => |e| return e, | 8850 | else => |e| return e, |
| 8607 | }; | 8851 | }; |
| | 8852 | try elf.updateSegmentLoadAddresses(); |
| 8608 | } | 8853 | } |
| 8609 | fn prelinkInner(elf: *Elf) Error!void { | 8854 | fn prelinkInner(elf: *Elf) Error!void { |
| 8610 | const comp = elf.base.comp; | 8855 | const comp = elf.base.comp; |
| ... | @@ -8659,24 +8904,27 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -8659,24 +8904,27 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 8659 | if (comp.zcu) |_| self_hosted_codegen: { | 8904 | if (comp.zcu) |_| self_hosted_codegen: { |
| 8660 | if (comp.config.use_llvm) break :self_hosted_codegen; | 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". | 8907 | if (elf.archive == null) { |
| 8663 | try elf.inputs.ensureUnusedCapacity(gpa, 1); | 8908 | // Self-hosted codegen is logically emitting a relocatable which is then immediately |
| 8664 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name}); | 8909 | // being linked into the output file, so add that fictional Zig object file input. |
| 8665 | defer gpa.free(zcu_name); | 8910 | try elf.inputs.ensureUnusedCapacity(gpa, 1); |
| 8666 | const zcu_file_symbol = try elf.addLocalSymbol(.{ | 8911 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name}); |
| 8667 | .node = .none, | 8912 | defer gpa.free(zcu_name); |
| 8668 | .name = zcu_name, | 8913 | const zcu_file_symbol = try elf.addLocalSymbol(.{ |
| 8669 | .value = 0, | 8914 | .node = .none, |
| 8670 | .size = 0, | 8915 | .name = zcu_name, |
| 8671 | .type = .FILE, | 8916 | .value = 0, |
| 8672 | .shndx = .ABS, | 8917 | .size = 0, |
| 8673 | }); | 8918 | .type = .FILE, |
| 8674 | elf.inputs.addOneAssumeCapacity().* = .{ | 8919 | .shndx = .ABS, |
| 8675 | .path = elf.base.emit, | 8920 | }); |
| 8676 | .member = null, | 8921 | elf.inputs.addOneAssumeCapacity().* = .{ |
| 8677 | .extra = .{ .file_symbol = zcu_file_symbol }, | 8922 | .path = elf.base.emit, |
| 8678 | }; | 8923 | .member = null, |
| 8679 | elf.input_pending_index += 1; | 8924 | .extra = .{ .file_symbol = zcu_file_symbol }, |
| | 8925 | }; |
| | 8926 | elf.input_pending_index += 1; |
| | 8927 | } |
| 8680 | | 8928 | |
| 8681 | switch (elf.shndx.debug_addr) { | 8929 | switch (elf.shndx.debug_addr) { |
| 8682 | .UNDEF => {}, | 8930 | .UNDEF => {}, |
| ... | @@ -8758,6 +9006,7 @@ pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void { | ... | @@ -8758,6 +9006,7 @@ pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void { |
| 8758 | .{elf.mf.io_err.?}, | 9006 | .{elf.mf.io_err.?}, |
| 8759 | ), | 9007 | ), |
| 8760 | }; | 9008 | }; |
| | 9009 | try elf.updateSegmentLoadAddresses(); |
| 8761 | } | 9010 | } |
| 8762 | fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void { | 9011 | fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void { |
| 8763 | const gpa = zcu.gpa; | 9012 | const gpa = zcu.gpa; |
| ... | @@ -9126,7 +9375,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | ... | @@ -9126,7 +9375,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 9126 | .size = opts.node_align.forward(opts.size), | 9375 | .size = opts.node_align.forward(opts.size), |
| 9127 | .alignment = opts.addralign.max(opts.node_align), | 9376 | .alignment = opts.addralign.max(opts.node_align), |
| 9128 | .resized = opts.size > 0, | 9377 | .resized = opts.size > 0, |
| 9129 | .bubbles_moved = opts.flags.ALLOC, | 9378 | .bubbles_moved = opts.flags.ALLOC, // see handling in `flushMoved` |
| 9130 | }), | 9379 | }), |
| 9131 | if (opts.manual_size) .{ .section_manual_size = shndx } else .{ .section = shndx }, | 9380 | if (opts.manual_size) .{ .section_manual_size = shndx } else .{ .section = shndx }, |
| 9132 | )), | 9381 | )), |
| ... | @@ -9265,6 +9514,7 @@ fn addRelocAssumeCapacity( | ... | @@ -9265,6 +9514,7 @@ fn addRelocAssumeCapacity( |
| 9265 | .rela_index = rela_index.toOptional(), | 9514 | .rela_index = rela_index.toOptional(), |
| 9266 | .result = .ok, | 9515 | .result = .ok, |
| 9267 | }); | 9516 | }); |
| | 9517 | ri.get(elf).apply(elf); |
| 9268 | }, | 9518 | }, |
| 9269 | .DYN, .EXEC => switch (elf.ehdrMachine()) { | 9519 | .DYN, .EXEC => switch (elf.ehdrMachine()) { |
| 9270 | .AARCH64 => switch (@"type".AARCH64) { | 9520 | .AARCH64 => switch (@"type".AARCH64) { |
| ... | @@ -9622,18 +9872,21 @@ fn addSymbolRelocAssumeCapacityInner( | ... | @@ -9622,18 +9872,21 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9622 | ) Error!void { | 9872 | ) Error!void { |
| 9623 | assert(elf.ehdrType() != .REL); | 9873 | assert(elf.ehdrType() != .REL); |
| 9624 | | 9874 | |
| 9625 | const rela_index: Section.RelaIndex.Optional = r: { | 9875 | request_static_target: { |
| 9626 | if (elf.shndx.dynamic == .UNDEF) break :r .none; | 9876 | if (elf.shndx.dynamic == .UNDEF) break :request_static_target; |
| 9627 | | 9877 | const gsi: Symbol.Global.Index = switch (target.unwrap()) { |
| 9628 | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to | 9878 | .local => break :request_static_target, |
| 9629 | // determine the vaddr of `node`. | 9879 | .global => |gsi| gsi, |
| 9630 | const node_vaddr = elf.getNodeVAddr(node); | 9880 | }; |
| 9631 | | 9881 | // The target symbol may be dynamic, in which case its value cannot be resolved statically. |
| 9632 | // If this is `true`, we will try to create a copy relocation for the target symbol if it is | 9882 | // However, there are strategies (such as copy relocations) the linker can employ which can |
| 9633 | // not locally defined. If the relocation value is always computed from the target symbol's | 9883 | // effectively convert dynamic symbols into static symbols. If statically resolving this |
| 9634 | // value (even for an external target symbol), and if the target symbol might be of type | 9884 | // relocation would require knowing the symbol value, we can set a flag so that those |
| 9635 | // STT_OBJECT, this should probably be `true`. | 9885 | // strategies are used when possible to make this relocation statically resolvable. |
| 9636 | const try_copy_reloc: bool = switch (@"type".target) { | 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 | .rel, .abs => true, | 9890 | .rel, .abs => true, |
| 9638 | | 9891 | |
| 9639 | .pltrel, | 9892 | .pltrel, |
| ... | @@ -9662,92 +9915,13 @@ fn addSymbolRelocAssumeCapacityInner( | ... | @@ -9662,92 +9915,13 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9662 | => false, | 9915 | => false, |
| 9663 | }, | 9916 | }, |
| 9664 | }; | 9917 | }; |
| 9665 | if (try_copy_reloc) switch (target.unwrap()) { | 9918 | if (!want_static_value) break :request_static_target; |
| 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, | | |
| 9696 | | 9919 | |
| 9697 | .abs, .pltabs => {}, | 9920 | if (!gsi.ptr(elf).status.want_static_value) { |
| 9698 | } | 9921 | gsi.ptr(elf).status.want_static_value = true; |
| 9699 | if (!@"type".action.simple.dest.isAddr(elf)) break :r .none; | 9922 | try elf.updateGlobalDynamic(gsi, false); |
| 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 | }, | | |
| 9749 | } | 9923 | } |
| 9750 | }; | 9924 | } |
| 9751 | | 9925 | |
| 9752 | const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); | 9926 | const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); |
| 9753 | const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; | 9927 | const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; |
| ... | @@ -9762,12 +9936,13 @@ fn addSymbolRelocAssumeCapacityInner( | ... | @@ -9762,12 +9936,13 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9762 | .type = @"type", | 9936 | .type = @"type", |
| 9763 | .next = next, | 9937 | .next = next, |
| 9764 | .prev = .none, | 9938 | .prev = .none, |
| 9765 | .rela_index = rela_index, | 9939 | .rela_index = .none, |
| 9766 | .result = .ok, | 9940 | .result = .ok, |
| 9767 | }); | 9941 | }); |
| 9768 | if (@"type".dependsOnTlsSize(elf)) { | 9942 | if (@"type".dependsOnTlsSize(elf)) { |
| 9769 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); | 9943 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 9770 | } | 9944 | } |
| | 9945 | try ri.get(elf).updateDynamicReloc(elf); |
| 9771 | | 9946 | |
| 9772 | // Actually apply the new relocation! | 9947 | // Actually apply the new relocation! |
| 9773 | ri.get(elf).apply(elf); | 9948 | ri.get(elf).apply(elf); |
| ... | @@ -10098,18 +10273,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -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 | 10279 | /// Otherwise, returns `.ok_textrel` if placing a dynamic relocation in this node requires the |
| 10104 | /// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise. | 10280 | /// presence of a `DT_TEXTREL` dynamic entry, or `.ok` otherwise. |
| 10105 | fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } { | 10281 | fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } { |
| 10106 | const shndx = elf.getNodeShndx(node); | 10282 | const shndx = elf.getNodeShndx(node); |
| 10107 | const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) { | 10283 | const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 10108 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, | 10284 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| 10109 | }; | 10285 | }; |
| 10110 | if (!shf.ALLOC) return .no; | 10286 | if (!shf.ALLOC) return .not_alloc; |
| 10111 | if (!shf.WRITE) return .yes_textrel; | 10287 | if (!shf.WRITE) return .ok_textrel; |
| 10112 | return .yes; | 10288 | return .ok; |
| 10113 | } | 10289 | } |
| 10114 | | 10290 | |
| 10115 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { | 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,6 +10296,7 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) |
| 10120 | .{elf.mf.io_err.?}, | 10296 | .{elf.mf.io_err.?}, |
| 10121 | ), | 10297 | ), |
| 10122 | }; | 10298 | }; |
| | 10299 | try elf.updateSegmentLoadAddresses(); |
| 10123 | } | 10300 | } |
| 10124 | fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { | 10301 | fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { |
| 10125 | const comp = elf.base.comp; | 10302 | const comp = elf.base.comp; |
| ... | @@ -10130,15 +10307,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) | ... | @@ -10130,15 +10307,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) |
| 10130 | const nav = ip.getNav(nav_index); | 10307 | const nav = ip.getNav(nav_index); |
| 10131 | const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?; | 10308 | const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?; |
| 10132 | switch (ip.indexToKey(nav.resolved.?.value)) { | 10309 | switch (ip.indexToKey(nav.resolved.?.value)) { |
| 10133 | .@"extern" => |@"extern"| { | 10310 | .@"extern" => { |
| 10134 | if (mod.strip or elf.ehdrMachine() != .X86_64) return; | 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 | const dwarf_gi = try elf.dwarf.getGlobal(nav_index); | 10312 | const dwarf_gi = try elf.dwarf.getGlobal(nav_index); |
| 10143 | const dwarf_global = dwarf_gi.get(&elf.dwarf); | 10313 | const dwarf_global = dwarf_gi.get(&elf.dwarf); |
| 10144 | const debug_info_ni = dwarf_global.debug_info_ni.unwrap().?; | 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,10 +10317,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) |
| 10147 | debug_info_ni.writer(gpa, &elf.mf, &di_nw); | 10317 | debug_info_ni.writer(gpa, &elf.mf, &di_nw); |
| 10148 | defer di_nw.deinit(); | 10318 | defer di_nw.deinit(); |
| 10149 | elf.resetNodeRelocs(debug_info_ni); | 10319 | elf.resetNodeRelocs(debug_info_ni); |
| 10150 | try elf.dwarf.updateExtern(pt, &di_nw, switch (elf.ehdrMachine()) { | 10320 | try elf.dwarf.updateExtern(pt, &di_nw, nav_index); |
| 10151 | else => unreachable, | | |
| 10152 | .X86_64 => .x86_64, | | |
| 10153 | }, si, nav_index); | | |
| 10154 | }, | 10321 | }, |
| 10155 | else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) { | 10322 | else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) { |
| 10156 | const nmi = try elf.navMapIndex(zcu, nav_index); | 10323 | const nmi = try elf.navMapIndex(zcu, nav_index); |
| ... | @@ -10219,6 +10386,7 @@ pub fn updateContainerType( | ... | @@ -10219,6 +10386,7 @@ pub fn updateContainerType( |
| 10219 | .{elf.mf.io_err.?}, | 10386 | .{elf.mf.io_err.?}, |
| 10220 | ), | 10387 | ), |
| 10221 | }; | 10388 | }; |
| | 10389 | try elf.updateSegmentLoadAddresses(); |
| 10222 | } | 10390 | } |
| 10223 | fn updateContainerTypeInner( | 10391 | fn updateContainerTypeInner( |
| 10224 | elf: *Elf, | 10392 | elf: *Elf, |
| ... | @@ -10360,6 +10528,7 @@ pub fn updateFunc( | ... | @@ -10360,6 +10528,7 @@ pub fn updateFunc( |
| 10360 | .{elf.mf.io_err.?}, | 10528 | .{elf.mf.io_err.?}, |
| 10361 | ), | 10529 | ), |
| 10362 | }; | 10530 | }; |
| | 10531 | try elf.updateSegmentLoadAddresses(); |
| 10363 | } | 10532 | } |
| 10364 | fn updateFuncInner( | 10533 | fn updateFuncInner( |
| 10365 | elf: *Elf, | 10534 | elf: *Elf, |
| ... | @@ -10607,6 +10776,7 @@ pub fn lostTracking( | ... | @@ -10607,6 +10776,7 @@ pub fn lostTracking( |
| 10607 | elf.mf.io_err.?, | 10776 | elf.mf.io_err.?, |
| 10608 | }), | 10777 | }), |
| 10609 | }; | 10778 | }; |
| | 10779 | try elf.updateSegmentLoadAddresses(); |
| 10610 | } | 10780 | } |
| 10611 | | 10781 | |
| 10612 | pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void { | 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,6 +10786,7 @@ pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void { |
| 10616 | }); | 10786 | }); |
| 10617 | if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi| | 10787 | if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi| |
| 10618 | try elf.updateConstInner(pt, cpi, .anyerror_type, .complete); | 10788 | try elf.updateConstInner(pt, cpi, .anyerror_type, .complete); |
| | 10789 | try elf.updateSegmentLoadAddresses(); |
| 10619 | } | 10790 | } |
| 10620 | | 10791 | |
| 10621 | pub fn flush( | 10792 | pub fn flush( |
| ... | @@ -10663,6 +10834,8 @@ fn flushInner( | ... | @@ -10663,6 +10834,8 @@ fn flushInner( |
| 10663 | | 10834 | |
| 10664 | try elf.prepareDynamic(); | 10835 | try elf.prepareDynamic(); |
| 10665 | | 10836 | |
| | 10837 | try elf.updateSegmentLoadAddresses(); |
| | 10838 | |
| 10666 | while (try elf.idle()) {} | 10839 | while (try elf.idle()) {} |
| 10667 | | 10840 | |
| 10668 | assert(elf.input_pending_index == elf.inputs.items.len); | 10841 | assert(elf.input_pending_index == elf.inputs.items.len); |
| ... | @@ -10678,10 +10851,28 @@ fn flushInner( | ... | @@ -10678,10 +10851,28 @@ fn flushInner( |
| 10678 | defer elf.mf.nodes_lock.unlock(); | 10851 | defer elf.mf.nodes_lock.unlock(); |
| 10679 | | 10852 | |
| 10680 | if (elf.overflowed_reloc_count > 0) { | 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 | if (elf.misaligned_reloc_count > 0) { | 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 | if (elf.archive) |*archive| { | 10878 | if (elf.archive) |*archive| { |
| ... | @@ -10698,20 +10889,22 @@ fn flushInner( | ... | @@ -10698,20 +10889,22 @@ fn flushInner( |
| 10698 | elf.flushDynamic(); | 10889 | elf.flushDynamic(); |
| 10699 | | 10890 | |
| 10700 | const entry_addr: u64 = entry: { | 10891 | const entry_addr: u64 = entry: { |
| 10701 | const sym_name: []const u8 = name: switch (elf.options.entry) { | 10892 | const entry_gsi = elf.entry_gsi.unwrap() orelse break :entry 0; |
| 10702 | .default => switch (comp.config.output_mode) { | 10893 | |
| 10703 | .Exe => continue :name .enabled, | 10894 | // Even though we already reported undefined globals earlier, we also need to check here |
| 10704 | .Lib, .Obj => continue :name .disabled, | 10895 | // whether the entry point is defined. There are two reasons for this: |
| 10705 | }, | 10896 | // |
| 10706 | .disabled => break :entry 0, | 10897 | // * If we are emitting something other than an executable, we skipped the undefined globals |
| 10707 | .enabled => "_start", | 10898 | // check, but there could still be an entry point (e.g. due to `-fentry=foo` on the CLI). |
| 10708 | .named => |named| named, | 10899 | // |
| 10709 | }; | 10900 | // * The entry point might be defined in an input DSO, which would satisfy the undefined |
| 10710 | const gsi = elf.globalByName(.{ | 10901 | // symbol reference but is insufficient here: the entry point needs a local definition. |
| 10711 | .name = sym_name, | 10902 | |
| 10712 | .version = null, | 10903 | if (!entry_gsi.resolveAlias(elf).defined(elf)) { |
| 10713 | }) orelse break :entry 0; | 10904 | return diags.fail("undefined entry point symbol '{s}'", .{entry_gsi.name(elf)}); |
| 10714 | break :entry Symbol.Id.global(gsi).value(elf); | 10905 | } |
| | 10906 | |
| | 10907 | break :entry Symbol.Id.global(entry_gsi).value(elf); |
| 10715 | }; | 10908 | }; |
| 10716 | switch (elf.ehdrPtr()) { | 10909 | switch (elf.ehdrPtr()) { |
| 10717 | inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)), | 10910 | inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)), |
| ... | @@ -10719,9 +10912,12 @@ fn flushInner( | ... | @@ -10719,9 +10912,12 @@ fn flushInner( |
| 10719 | | 10912 | |
| 10720 | try elf.mf.flush(); | 10913 | try elf.mf.flush(); |
| 10721 | | 10914 | |
| 10722 | if (elf.options.enable_link_snapshots) | 10915 | if (elf.options.enable_link_snapshots) { |
| 10723 | elf.dumpStderr() catch |err| | 10916 | elf.dumpStderr() catch |err| switch (err) { |
| 10724 | return diags.fail("dumping link snapshot failed: {t}", .{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 | pub fn idle(elf: *Elf) link.Error!bool { | 10923 | pub fn idle(elf: *Elf) link.Error!bool { |
| ... | @@ -10729,6 +10925,9 @@ pub fn idle(elf: *Elf) link.Error!bool { | ... | @@ -10729,6 +10925,9 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10729 | elf.mf.nodes_lock.lock(); | 10925 | elf.mf.nodes_lock.lock(); |
| 10730 | defer elf.mf.nodes_lock.unlock(); | 10926 | defer elf.mf.nodes_lock.unlock(); |
| 10731 | | 10927 | |
| | 10928 | const trace = tracy.trace(@src()); |
| | 10929 | defer trace.end(); |
| | 10930 | |
| 10732 | const comp = elf.base.comp; | 10931 | const comp = elf.base.comp; |
| 10733 | const diags = &comp.link_diags; | 10932 | const diags = &comp.link_diags; |
| 10734 | | 10933 | |
| ... | @@ -10857,7 +11056,7 @@ pub fn idle(elf: *Elf) link.Error!bool { | ... | @@ -10857,7 +11056,7 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10857 | break :task; | 11056 | break :task; |
| 10858 | } | 11057 | } |
| 10859 | while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) { | 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 | const clean_moved = ni.cleanMoved(&elf.mf); | 11060 | const clean_moved = ni.cleanMoved(&elf.mf); |
| 10862 | const clean_resized = ni.cleanResized(&elf.mf); | 11061 | const clean_resized = ni.cleanResized(&elf.mf); |
| 10863 | const clean_next_moved = ni.cleanNextMoved(&elf.mf); | 11062 | const clean_next_moved = ni.cleanNextMoved(&elf.mf); |
| ... | @@ -10872,6 +11071,9 @@ pub fn idle(elf: *Elf) link.Error!bool { | ... | @@ -10872,6 +11071,9 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10872 | if (elf.bss_moved) { | 11071 | if (elf.bss_moved) { |
| 10873 | elf.bss_moved = false; | 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 | const data_phndx = elf.getNode(elf.ni.data).segment; | 11077 | const data_phndx = elf.getNode(elf.ni.data).segment; |
| 10876 | const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) { | 11078 | const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) { |
| 10877 | inline else => |phdr| .{ | 11079 | inline else => |phdr| .{ |
| ... | @@ -10902,6 +11104,30 @@ pub fn idle(elf: *Elf) link.Error!bool { | ... | @@ -10902,6 +11104,30 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10902 | | 11104 | |
| 10903 | break :task; | 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 | if (elf.inputs.items.len - elf.input_pending_index > 0) return true; | 11132 | if (elf.inputs.items.len - elf.input_pending_index > 0) return true; |
| 10907 | if (elf.input_sections.items.len - elf.input_section_pending_index > 0) return true; | 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,6 +11135,8 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10909 | if (elf.changed_symtab_index.count() > 0) return true; | 11135 | if (elf.changed_symtab_index.count() > 0) return true; |
| 10910 | if (elf.mf.updates.items.len > 0) return true; | 11136 | if (elf.mf.updates.items.len > 0) return true; |
| 10911 | if (elf.bss_moved) return true; | 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 | return false; | 11140 | return false; |
| 10913 | } | 11141 | } |
| 10914 | | 11142 | |
| ... | @@ -11039,7 +11267,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void { | ... | @@ -11039,7 +11267,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void { |
| 11039 | fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void { | 11267 | fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void { |
| 11040 | while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) { | 11268 | while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) { |
| 11041 | const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?; | 11269 | const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?; |
| 11042 | try debug_addr_ni.moved(gpa, &elf.mf); | | |
| 11043 | var da_nw: link.MappedFile.Node.Writer = undefined; | 11270 | var da_nw: link.MappedFile.Node.Writer = undefined; |
| 11044 | debug_addr_ni.writer(gpa, &elf.mf, &da_nw); | 11271 | debug_addr_ni.writer(gpa, &elf.mf, &da_nw); |
| 11045 | defer da_nw.deinit(); | 11272 | defer da_nw.deinit(); |
| ... | @@ -11052,7 +11279,6 @@ fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void { | ... | @@ -11052,7 +11279,6 @@ fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void { |
| 11052 | } | 11279 | } |
| 11053 | while (elf.dwarf.debug_str_offsets.map.count() - elf.dwarf.debug_str_offsets.pending_index > 0) { | 11280 | while (elf.dwarf.debug_str_offsets.map.count() - elf.dwarf.debug_str_offsets.pending_index > 0) { |
| 11054 | const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?; | 11281 | const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?; |
| 11055 | try debug_str_offsets_ni.moved(gpa, &elf.mf); | | |
| 11056 | var dso_nw: link.MappedFile.Node.Writer = undefined; | 11282 | var dso_nw: link.MappedFile.Node.Writer = undefined; |
| 11057 | debug_str_offsets_ni.writer(gpa, &elf.mf, &dso_nw); | 11283 | debug_str_offsets_ni.writer(gpa, &elf.mf, &dso_nw); |
| 11058 | defer dso_nw.deinit(); | 11284 | defer dso_nw.deinit(); |
| ... | @@ -11297,6 +11523,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { | ... | @@ -11297,6 +11523,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 11297 | fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { | 11523 | fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 11298 | const trace = tracy.trace(@src()); | 11524 | const trace = tracy.trace(@src()); |
| 11299 | defer trace.end(); | 11525 | defer trace.end(); |
| | 11526 | trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)}); |
| 11300 | | 11527 | |
| 11301 | switch (elf.getNode(ni)) { | 11528 | switch (elf.getNode(ni)) { |
| 11302 | .deleted => unreachable, | 11529 | .deleted => unreachable, |
| ... | @@ -11315,7 +11542,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void | ... | @@ -11315,7 +11542,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11315 | else => unreachable, | 11542 | else => unreachable, |
| 11316 | | 11543 | |
| 11317 | .NULL, .LOAD => { | 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 | .DYNAMIC, | 11552 | .DYNAMIC, |
| ... | @@ -11343,6 +11574,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void | ... | @@ -11343,6 +11574,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11343 | elf, | 11574 | elf, |
| 11344 | tbss_addr, | 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,7 +11619,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11382 | reloc.apply(elf); | 11619 | reloc.apply(elf); |
| 11383 | } | 11620 | } |
| 11384 | } else if (shndx == elf.shndx.plt) { | 11621 | } else if (shndx == elf.shndx.plt) { |
| 11385 | elf.flushMovedNodeRelocs(ni, addr, .{ | 11622 | elf.flushMovedNodeRelocs(ni, .{ |
| 11386 | .first_symbol_reloc = elf.plt_first_symbol_reloc, | 11623 | .first_symbol_reloc = elf.plt_first_symbol_reloc, |
| 11387 | }); | 11624 | }); |
| 11388 | elf.flushMovedPltSection(.plt, old_addr, addr); | 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,10 +11628,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11391 | } else if (shndx == elf.shndx.plt_sec) { | 11628 | } else if (shndx == elf.shndx.plt_sec) { |
| 11392 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); | 11629 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 11393 | } else if (shndx == elf.shndx.eh_frame_hdr) { | 11630 | } else if (shndx == elf.shndx.eh_frame_hdr) { |
| 11394 | elf.flushMovedNodeRelocs(ni, addr, .{ | 11631 | elf.flushMovedNodeRelocs(ni, .{ |
| 11395 | .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc, | 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 | .input_section => |isi| { | 11642 | .input_section => |isi| { |
| 11400 | const old_section_addr = isi.ptr(elf).vaddr; | 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,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 | .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc, | 11685 | .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc, |
| 11443 | .first_got_reloc = isi.ptrConst(elf).first_got_reloc, | 11686 | .first_got_reloc = isi.ptrConst(elf).first_got_reloc, |
| 11444 | }); | 11687 | }); |
| 11445 | }, | 11688 | }, |
| 11446 | .copied_global => |global_name| { | 11689 | .copied_global => |global_name| { |
| 11447 | const copied_global = elf.copied_globals.getPtr(global_name) orelse { | 11690 | const copied_global = elf.copied_globals.getPtr(global_name).?; |
| 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 | }; | | |
| 11452 | assert(copied_global.node == ni); | 11691 | assert(copied_global.node == ni); |
| 11453 | | 11692 | |
| 11454 | const new_addr = elf.computeNodeVAddr(ni); | 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,7 +11704,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11465 | Symbol.Id.global(gsi).flushMoved(elf, new_addr); | 11704 | Symbol.Id.global(gsi).flushMoved(elf, new_addr); |
| 11466 | } | 11705 | } |
| 11467 | } | 11706 | } |
| 11468 | elf.flushMovedNodeRelocs(ni, new_addr, .{ | 11707 | elf.flushMovedNodeRelocs(ni, .{ |
| 11469 | .first_symbol_reloc = mi.firstSymbolReloc(elf), | 11708 | .first_symbol_reloc = mi.firstSymbolReloc(elf), |
| 11470 | .skip_symbol_relocs = switch (tag) { | 11709 | .skip_symbol_relocs = switch (tag) { |
| 11471 | else => comptime unreachable, | 11710 | else => comptime unreachable, |
| ... | @@ -11479,12 +11718,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void | ... | @@ -11479,12 +11718,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11479 | }); | 11718 | }); |
| 11480 | }, | 11719 | }, |
| 11481 | .debug_shared => |ss| { | 11720 | .debug_shared => |ss| { |
| 11482 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11483 | var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc; | 11721 | var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc; |
| 11484 | while (target_ri != .none) { | 11722 | while (target_ri != .none) { |
| 11485 | const target_reloc = target_ri.get(elf); | 11723 | const target_reloc = target_ri.get(elf); |
| 11486 | assert(target_reloc.target == ni); | 11724 | assert(target_reloc.target == ni); |
| 11487 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11725 | target_reloc.apply(elf); |
| 11488 | target_ri = target_reloc.next; | 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,119 +11733,110 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11495 | .unit_debug_line, | 11733 | .unit_debug_line, |
| 11496 | => {}, | 11734 | => {}, |
| 11497 | .debug_addr => { | 11735 | .debug_addr => { |
| 11498 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11499 | var target_ri = elf.dwarf_addr.first_target_reloc; | 11736 | var target_ri = elf.dwarf_addr.first_target_reloc; |
| 11500 | while (target_ri != .none) { | 11737 | while (target_ri != .none) { |
| 11501 | const target_reloc = target_ri.get(elf); | 11738 | const target_reloc = target_ri.get(elf); |
| 11502 | assert(target_reloc.target == ni); | 11739 | assert(target_reloc.target == ni); |
| 11503 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11740 | target_reloc.apply(elf); |
| 11504 | target_ri = target_reloc.next; | 11741 | target_ri = target_reloc.next; |
| 11505 | } | 11742 | } |
| 11506 | const node_vaddr = elf.computeNodeVAddr(ni); | | |
| 11507 | for (elf.dwarf_addr.symbol_relocs.items) |symbol_ri| { | 11743 | for (elf.dwarf_addr.symbol_relocs.items) |symbol_ri| { |
| 11508 | const symbol_reloc = symbol_ri.get(elf); | 11744 | const symbol_reloc = symbol_ri.get(elf); |
| 11509 | assert(symbol_reloc.node.unwrap().? == ni); | 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 | .debug_str_offsets => { | 11750 | .debug_str_offsets => { |
| 11514 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11515 | var target_ri = elf.dwarf_str_offsets.first_target_reloc; | 11751 | var target_ri = elf.dwarf_str_offsets.first_target_reloc; |
| 11516 | while (target_ri != .none) { | 11752 | while (target_ri != .none) { |
| 11517 | const target_reloc = target_ri.get(elf); | 11753 | const target_reloc = target_ri.get(elf); |
| 11518 | assert(target_reloc.target == ni); | 11754 | assert(target_reloc.target == ni); |
| 11519 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11755 | target_reloc.apply(elf); |
| 11520 | target_ri = target_reloc.next; | 11756 | target_ri = target_reloc.next; |
| 11521 | } | 11757 | } |
| 11522 | const node_vaddr = elf.computeNodeVAddr(ni); | | |
| 11523 | for (elf.dwarf_str_offsets.node_relocs.items) |node_ri| { | 11758 | for (elf.dwarf_str_offsets.node_relocs.items) |node_ri| { |
| 11524 | const node_reloc = node_ri.get(elf); | 11759 | const node_reloc = node_ri.get(elf); |
| 11525 | assert(node_reloc.node.unwrap().? == ni); | 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 | .unit_frame_cie => |ui| { | 11765 | .unit_frame_cie => |ui| { |
| 11530 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11531 | var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc; | 11766 | var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc; |
| 11532 | while (target_ri != .none) { | 11767 | while (target_ri != .none) { |
| 11533 | const target_reloc = target_ri.get(elf); | 11768 | const target_reloc = target_ri.get(elf); |
| 11534 | assert(target_reloc.target == ni); | 11769 | assert(target_reloc.target == ni); |
| 11535 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11770 | target_reloc.apply(elf); |
| 11536 | target_ri = target_reloc.next; | 11771 | target_ri = target_reloc.next; |
| 11537 | } | 11772 | } |
| 11538 | }, | 11773 | }, |
| 11539 | .unit_debug_info_header => |ui| { | 11774 | .unit_debug_info_header => |ui| { |
| 11540 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; | 11775 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; |
| 11541 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11542 | var target_ri = dwarf_unit.debug_info_header_first_target_reloc; | 11776 | var target_ri = dwarf_unit.debug_info_header_first_target_reloc; |
| 11543 | while (target_ri != .none) { | 11777 | while (target_ri != .none) { |
| 11544 | const target_reloc = target_ri.get(elf); | 11778 | const target_reloc = target_ri.get(elf); |
| 11545 | assert(target_reloc.target == ni); | 11779 | assert(target_reloc.target == ni); |
| 11546 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11780 | target_reloc.apply(elf); |
| 11547 | target_ri = target_reloc.next; | 11781 | target_ri = target_reloc.next; |
| 11548 | } | 11782 | } |
| 11549 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11783 | elf.flushMovedNodeRelocs(ni, .{ |
| 11550 | .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc, | 11784 | .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc, |
| 11551 | }); | 11785 | }); |
| 11552 | }, | 11786 | }, |
| 11553 | .unit_debug_info_footer => {}, | 11787 | .unit_debug_info_footer => {}, |
| 11554 | .unit_debug_line_header => |ui| { | 11788 | .unit_debug_line_header => |ui| { |
| 11555 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; | 11789 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; |
| 11556 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11557 | var target_ri = dwarf_unit.debug_line_header_first_target_reloc; | 11790 | var target_ri = dwarf_unit.debug_line_header_first_target_reloc; |
| 11558 | while (target_ri != .none) { | 11791 | while (target_ri != .none) { |
| 11559 | const target_reloc = target_ri.get(elf); | 11792 | const target_reloc = target_ri.get(elf); |
| 11560 | assert(target_reloc.target == ni); | 11793 | assert(target_reloc.target == ni); |
| 11561 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11794 | target_reloc.apply(elf); |
| 11562 | target_ri = target_reloc.next; | 11795 | target_ri = target_reloc.next; |
| 11563 | } | 11796 | } |
| 11564 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11797 | elf.flushMovedNodeRelocs(ni, .{ |
| 11565 | .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc, | 11798 | .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc, |
| 11566 | }); | 11799 | }); |
| 11567 | }, | 11800 | }, |
| 11568 | .unit_debug_rnglists => |ui| { | 11801 | .unit_debug_rnglists => |ui| { |
| 11569 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; | 11802 | const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; |
| 11570 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11571 | var target_ri = dwarf_unit.debug_rnglists_first_target_reloc; | 11803 | var target_ri = dwarf_unit.debug_rnglists_first_target_reloc; |
| 11572 | while (target_ri != .none) { | 11804 | while (target_ri != .none) { |
| 11573 | const target_reloc = target_ri.get(elf); | 11805 | const target_reloc = target_ri.get(elf); |
| 11574 | assert(target_reloc.target == ni); | 11806 | assert(target_reloc.target == ni); |
| 11575 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11807 | target_reloc.apply(elf); |
| 11576 | target_ri = target_reloc.next; | 11808 | target_ri = target_reloc.next; |
| 11577 | } | 11809 | } |
| 11578 | const node_vaddr = elf.computeNodeVAddr(ni); | | |
| 11579 | for (dwarf_unit.debug_rnglists_symbol_relocs.items) |symbol_ri| { | 11810 | for (dwarf_unit.debug_rnglists_symbol_relocs.items) |symbol_ri| { |
| 11580 | const symbol_reloc = symbol_ri.get(elf); | 11811 | const symbol_reloc = symbol_ri.get(elf); |
| 11581 | assert(symbol_reloc.node.unwrap().? == ni); | 11812 | assert(symbol_reloc.node.unwrap().? == ni); |
| 11582 | symbol_reloc.flushMovedNode(elf, node_vaddr); | 11813 | symbol_reloc.apply(elf); |
| 11583 | } | 11814 | } |
| 11584 | }, | 11815 | }, |
| 11585 | .const_debug_info => |cpi| { | 11816 | .const_debug_info => |cpi| { |
| 11586 | const dwarf_const = &elf.dwarf_consts.get(cpi).?; | 11817 | const dwarf_const = &elf.dwarf_consts.get(cpi).?; |
| 11587 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11588 | var target_ri = dwarf_const.debug_info_first_target_reloc; | 11818 | var target_ri = dwarf_const.debug_info_first_target_reloc; |
| 11589 | while (target_ri != .none) { | 11819 | while (target_ri != .none) { |
| 11590 | const target_reloc = target_ri.get(elf); | 11820 | const target_reloc = target_ri.get(elf); |
| 11591 | assert(target_reloc.target == ni); | 11821 | assert(target_reloc.target == ni); |
| 11592 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11822 | target_reloc.apply(elf); |
| 11593 | target_ri = target_reloc.next; | 11823 | target_ri = target_reloc.next; |
| 11594 | } | 11824 | } |
| 11595 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11825 | elf.flushMovedNodeRelocs(ni, .{ |
| 11596 | .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc, | 11826 | .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc, |
| 11597 | .first_node_reloc = dwarf_const.debug_info_first_node_reloc, | 11827 | .first_node_reloc = dwarf_const.debug_info_first_node_reloc, |
| 11598 | }); | 11828 | }); |
| 11599 | }, | 11829 | }, |
| 11600 | .global_debug_info => |gi| { | 11830 | .global_debug_info => |gi| { |
| 11601 | const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)]; | 11831 | const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)]; |
| 11602 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11603 | var target_ri = dwarf_global.debug_info_first_target_reloc; | 11832 | var target_ri = dwarf_global.debug_info_first_target_reloc; |
| 11604 | while (target_ri != .none) { | 11833 | while (target_ri != .none) { |
| 11605 | const target_reloc = target_ri.get(elf); | 11834 | const target_reloc = target_ri.get(elf); |
| 11606 | assert(target_reloc.target == ni); | 11835 | assert(target_reloc.target == ni); |
| 11607 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11836 | target_reloc.apply(elf); |
| 11608 | target_ri = target_reloc.next; | 11837 | target_ri = target_reloc.next; |
| 11609 | } | 11838 | } |
| 11610 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11839 | elf.flushMovedNodeRelocs(ni, .{ |
| 11611 | .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc, | 11840 | .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc, |
| 11612 | .first_node_reloc = dwarf_global.debug_info_first_node_reloc, | 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,22 +11852,21 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11623 | elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset); | 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 | .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc, | 11856 | .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc, |
| 11628 | .first_node_reloc = dwarf_func.frame_fde_first_node_reloc, | 11857 | .first_node_reloc = dwarf_func.frame_fde_first_node_reloc, |
| 11629 | }); | 11858 | }); |
| 11630 | }, | 11859 | }, |
| 11631 | .func_debug_info => |fi| { | 11860 | .func_debug_info => |fi| { |
| 11632 | const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; | 11861 | const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; |
| 11633 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11634 | var target_ri = dwarf_func.debug_info_first_target_reloc; | 11862 | var target_ri = dwarf_func.debug_info_first_target_reloc; |
| 11635 | while (target_ri != .none) { | 11863 | while (target_ri != .none) { |
| 11636 | const target_reloc = target_ri.get(elf); | 11864 | const target_reloc = target_ri.get(elf); |
| 11637 | assert(target_reloc.target == ni); | 11865 | assert(target_reloc.target == ni); |
| 11638 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11866 | target_reloc.apply(elf); |
| 11639 | target_ri = target_reloc.next; | 11867 | target_ri = target_reloc.next; |
| 11640 | } | 11868 | } |
| 11641 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11869 | elf.flushMovedNodeRelocs(ni, .{ |
| 11642 | .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc, | 11870 | .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc, |
| 11643 | .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav| | 11871 | .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav| |
| 11644 | nav.lsi.index().ptr(elf).node | 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,7 +11878,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11650 | }, | 11878 | }, |
| 11651 | .func_debug_line => |fi| { | 11879 | .func_debug_line => |fi| { |
| 11652 | const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; | 11880 | const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; |
| 11653 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11881 | elf.flushMovedNodeRelocs(ni, .{ |
| 11654 | .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc, | 11882 | .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc, |
| 11655 | .first_node_reloc = dwarf_func.debug_line_first_node_reloc, | 11883 | .first_node_reloc = dwarf_func.debug_line_first_node_reloc, |
| 11656 | .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni, | 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,15 +11886,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11658 | }, | 11886 | }, |
| 11659 | .decl_debug_info => |di| { | 11887 | .decl_debug_info => |di| { |
| 11660 | const dwarf_decl = &elf.dwarf_decls.get(di).?; | 11888 | const dwarf_decl = &elf.dwarf_decls.get(di).?; |
| 11661 | const target_section_offset = elf.computeNodeSectionOffset(ni); | | |
| 11662 | var target_ri = dwarf_decl.debug_info_first_target_reloc; | 11889 | var target_ri = dwarf_decl.debug_info_first_target_reloc; |
| 11663 | while (target_ri != .none) { | 11890 | while (target_ri != .none) { |
| 11664 | const target_reloc = target_ri.get(elf); | 11891 | const target_reloc = target_ri.get(elf); |
| 11665 | assert(target_reloc.target == ni); | 11892 | assert(target_reloc.target == ni); |
| 11666 | target_reloc.flushMovedTarget(elf, target_section_offset); | 11893 | target_reloc.apply(elf); |
| 11667 | target_ri = target_reloc.next; | 11894 | target_ri = target_reloc.next; |
| 11668 | } | 11895 | } |
| 11669 | elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ | 11896 | elf.flushMovedNodeRelocs(ni, .{ |
| 11670 | .first_node_reloc = dwarf_decl.debug_info_first_node_reloc, | 11897 | .first_node_reloc = dwarf_decl.debug_info_first_node_reloc, |
| 11671 | }); | 11898 | }); |
| 11672 | }, | 11899 | }, |
| ... | @@ -11737,166 +11964,156 @@ fn flushMovedNobitsSymbols( | ... | @@ -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. | 11967 | /// If any loadable segments have invalid virtual addresses, assigns them new valid addresses and |
| 11741 | fn updateSegmentLoadAddress( | 11968 | /// queues the necessary idle tasks (e.g. re-applying relocations targeting those segments). |
| 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. | | |
| 11763 | /// | 11969 | /// |
| 11764 | /// TODO: this function is currently a source of non-determinism in the linker, because handling the | 11970 | /// This function must be called at the end of every link task in case tghe task invalidated segment |
| 11765 | /// moving or resizing of a segment could reorder them and thereby affect how we handle *future* | 11971 | /// addresses (which can happen in response to any node movement/resize, or any section resize). It |
| 11766 | /// changes to segments. | 11972 | /// cannot be called from `idle` as this function does not have the required idempotency guarantee |
| 11767 | fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void { | 11973 | /// so calling it non-deterministically it would lead to unreproducible output files. |
| 11768 | const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?; | 11974 | fn updateSegmentLoadAddresses(elf: *Elf) std.mem.Allocator.Error!void { |
| 11769 | assert(elf.getNode(segment_ni).segment == orig_phndx); | 11975 | if (elf.ehdrType() == .REL) return; |
| | 11976 | |
| 11770 | const page_align = elf.targetPageAlign(); | 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 | switch (elf.phdrSlice()) { | 11979 | switch (elf.phdrSlice()) { |
| 11786 | inline else => |phdr| { | 11980 | inline else => |phdr| { |
| 11787 | const offset = elf.targetLoad(&phdr[orig_phndx].offset); | 11981 | // Somewhere in the phdrs is a contiguous sequence of all "loadable" segments (segments |
| 11788 | const filesz = elf.targetLoad(&phdr[orig_phndx].filesz); | 11982 | // with a `type` of `PT_NULL` or `PT_LOAD`); find the start of that sequence. In theory |
| 11789 | | 11983 | // we could memoize this, but there's no real need since the number of phdrs is tiny. |
| 11790 | const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr); | 11984 | const load_phdrs_start = for (phdr, 0..) |*ph, phndx| { |
| 11791 | assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr); | 11985 | switch (elf.targetLoad(&ph.type)) { |
| 11792 | | 11986 | .NULL, .LOAD => break phndx, |
| 11793 | const bss_size = if (segment_ni == elf.ni.data) | 11987 | else => continue, |
| 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); | | |
| 11819 | } | 11988 | } |
| 11820 | } | 11989 | } else unreachable; // we always create loadable (`PT_NULL`/`PT_LOAD`) segments |
| 11821 | | 11990 | |
| 11822 | // If our size has changed, or if the address shift above caused our "end" address to | 11991 | // Update these fields for each loadable segment: |
| 11823 | // cross a page boundary, then we might be overlapping with the next segment's pages. In | 11992 | // |
| 11824 | // that case, we will jump past that segment and give ourselves a new address after it. | 11993 | // * `memsz` |
| 11825 | // We'll need to repeat this for every loadable phdr after us, until we're no longer | 11994 | // * `type` (`PT_NULL` vs `PT_LOAD`) |
| 11826 | // overlapping anything. | 11995 | // * `align` |
| 11827 | var phndx = orig_phndx; | 11996 | // * `offset` (in theory this should happen in `updateElfOffset`, but this logic needs |
| 11828 | for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| { | 11997 | // to track that value, so it's easier to just populate it here instead) |
| 11829 | switch (elf.targetLoad(&next_ph.type)) { | 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 | .NULL, .LOAD => {}, | 12009 | .NULL, .LOAD => {}, |
| 11831 | else => { | 12010 | else => break phndx, // reached end of loadable segments |
| 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 | }, | | |
| 11837 | } | 12011 | } |
| 11838 | | 12012 | const segment_ni = segment_oni.unwrap().?; // all loadable segments have nodes |
| 11839 | const next_vaddr = elf.targetLoad(&next_ph.vaddr); | 12013 | |
| 11840 | // Find the first virtual address which the next phdr "owns" by aligning its vaddr | 12014 | // We need to know the segment's file offset here anyway, so we may as well just |
| 11841 | // backwards to the start of the page. | 12015 | // populate the phdr `offset` field now instead of in `flushElfOffset`. |
| 11842 | const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits()); | 12016 | const ph_offset = elf.computeNodeElfOffset(segment_ni); |
| 11843 | | 12017 | elf.targetStore(&ph.offset, @intCast(ph_offset)); |
| 11844 | // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if | 12018 | |
| 11845 | // the segment is already known to be moving---making it easier to grow in-place is | 12019 | // The alignment of the segment affects which virtual addresses are valid. |
| 11846 | // the whole point of the multiplier! | 12020 | const ph_align = segment_ni.alignment(&elf.mf).max(page_align); |
| 11847 | { | 12021 | elf.targetStore(&ph.@"align", @intCast(ph_align.toByteUnits())); |
| 11848 | const target_size = if (vaddr == orig_vaddr) memsz else memsz * reserve_size_multiplier; | 12022 | |
| 11849 | if (vaddr + target_size <= next_page_vaddr) { | 12023 | // The size of the segment in memory is equal to the size of the segment in the file |
| 11850 | break; // hooray, we fit here! | 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 | 12050 | // There should be no more loadable segments after the contiguous range. |
| 11857 | // consider any target-specific restrictions we are subject to. | 12051 | for (phdr[load_phdrs_end..]) |*ph| switch (elf.targetLoad(&ph.type)) { |
| 11858 | switch (elf.targetSegmentLoadAddressRestrictions()) { | 12052 | .NULL, .LOAD => unreachable, // non-contiguous loadable segment |
| 11859 | .none => {}, | 12053 | else => {}, |
| 11860 | .data_last => if (next_ni == elf.ni.data) { | 12054 | }; |
| 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 | }; | | |
| 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)); | 12061 | // It's time to actually allocate virtual addresses for all segments. Our current |
| 11873 | const next_offset = elf.targetLoad(&next_ph.offset); | 12062 | // strategy for this preserves the order of segments in memory, but this isn't a hard |
| 11874 | const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits(); | 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; | 12073 | // If we determine that a segment's virtual address needs to move, then it's a good idea |
| 11879 | }, | 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 | 12105 | // Otherwise, put ourselves at a new address just after the previous segment. |
| 11883 | // first we need to adjust `vaddr` to come after it. | 12106 | const new_vaddr = ph_align.forward(preferred_vaddr) + ph_offset % ph_align.toByteUnits(); |
| 11884 | const next_size = elf.targetLoad(&next_ph.memsz); | 12107 | assert(new_vaddr != ph_vaddr); // `ph_vaddr` was not valid |
| 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(); | | |
| 11888 | | 12108 | |
| 11889 | // Now just swap the phdrs and update our `phndx`. | 12109 | elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr)); |
| 11890 | std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph); | 12110 | elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr)); |
| 11891 | elf.phdrs.items[phndx] = .wrap(next_ni); | 12111 | const segment_ni = elf.phdrs.items[phndx].unwrap().?; |
| 11892 | elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx }; | 12112 | try segment_ni.childrenMoved(gpa, &elf.mf); |
| 11893 | elf.phdrs.items[next_phndx] = .wrap(segment_ni); | 12113 | if (segment_ni == elf.ni.data) elf.bss_moved = true; |
| 11894 | elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) }; | | |
| 11895 | phndx = @intCast(next_phndx); | | |
| 11896 | } | | |
| 11897 | | 12114 | |
| 11898 | if (vaddr != orig_vaddr) { | 12115 | first_free_vaddr = page_align.forward(new_vaddr + ph_memsz); |
| 11899 | try elf.updateSegmentLoadAddress(segment_ni, vaddr); | 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,6 +12122,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11905 | fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { | 12122 | fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 11906 | const trace = tracy.trace(@src()); | 12123 | const trace = tracy.trace(@src()); |
| 11907 | defer trace.end(); | 12124 | defer trace.end(); |
| | 12125 | trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)}); |
| 11908 | | 12126 | |
| 11909 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); | 12127 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); |
| 11910 | switch (elf.getNode(ni)) { | 12128 | switch (elf.getNode(ni)) { |
| ... | @@ -11933,7 +12151,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo | ... | @@ -11933,7 +12151,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11933 | else => unreachable, | 12151 | else => unreachable, |
| 11934 | .NULL, .LOAD => { | 12152 | .NULL, .LOAD => { |
| 11935 | elf.targetStore(&ph.filesz, @intCast(size)); | 12153 | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11936 | try elf.allocateSegmentLoadAddress(phndx); | | |
| 11937 | if (ni == elf.ni.data) elf.bss_moved = true; | 12154 | if (ni == elf.ni.data) elf.bss_moved = true; |
| 11938 | }, | 12155 | }, |
| 11939 | .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => { | 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,7 +12163,11 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11946 | | 12163 | |
| 11947 | elf.targetStore(&ph.filesz, @intCast(size)); | 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 | const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align")); | 12171 | const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align")); |
| 11951 | elf.targetStore(&ph.memsz, @intCast( | 12172 | elf.targetStore(&ph.memsz, @intCast( |
| 11952 | tls_align.forward(size + elf.shndx.tbss.size(elf)), | 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,6 +12261,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 12040 | fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { | 12261 | fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 12041 | const trace = tracy.trace(@src()); | 12262 | const trace = tracy.trace(@src()); |
| 12042 | defer trace.end(); | 12263 | defer trace.end(); |
| | 12264 | trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)}); |
| 12043 | | 12265 | |
| 12044 | const node = elf.getNode(ni); | 12266 | const node = elf.getNode(ni); |
| 12045 | switch (node) { | 12267 | switch (node) { |
| ... | @@ -12480,7 +12702,6 @@ fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool { | ... | @@ -12480,7 +12702,6 @@ fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool { |
| 12480 | | 12702 | |
| 12481 | // relocate the PLT entry to point to the .GOT.PLT entry | 12703 | // relocate the PLT entry to point to the .GOT.PLT entry |
| 12482 | const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset; | 12704 | const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset; |
| 12483 | // TODO: handle overflow gracefully | | |
| 12484 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]); | 12705 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]); |
| 12485 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]); | 12706 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]); |
| 12486 | elf.targetStore(inst0, .{ | 12707 | elf.targetStore(inst0, .{ |
| ... | @@ -12646,7 +12867,6 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad | ... | @@ -12646,7 +12867,6 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad |
| 12646 | const target_slice = plt_slice[plt_offset..]; | 12867 | const target_slice = plt_slice[plt_offset..]; |
| 12647 | | 12868 | |
| 12648 | const got_plt_abs: u64 = got_plt_addr + got_plt_offset; | 12869 | const got_plt_abs: u64 = got_plt_addr + got_plt_offset; |
| 12649 | // TODO: handle overflow gracefully | | |
| 12650 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]); | 12870 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]); |
| 12651 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]); | 12871 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]); |
| 12652 | | 12872 | |
| ... | @@ -12698,6 +12918,7 @@ pub fn updateExports( | ... | @@ -12698,6 +12918,7 @@ pub fn updateExports( |
| 12698 | }; | 12918 | }; |
| 12699 | } | 12919 | } |
| 12700 | try elf.genPending(pt); | 12920 | try elf.genPending(pt); |
| | 12921 | try elf.updateSegmentLoadAddresses(); |
| 12701 | } | 12922 | } |
| 12702 | fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void { | 12923 | fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void { |
| 12703 | const zcu = elf.base.comp.zcu.?; | 12924 | const zcu = elf.base.comp.zcu.?; |
| ... | @@ -12801,111 +13022,8 @@ pub fn printNode( | ... | @@ -12801,111 +13022,8 @@ pub fn printNode( |
| 12801 | ni: MappedFile.Node.Index, | 13022 | ni: MappedFile.Node.Index, |
| 12802 | indent: usize, | 13023 | indent: usize, |
| 12803 | ) Io.Writer.Error!void { | 13024 | ) Io.Writer.Error!void { |
| 12804 | const node = elf.getNode(ni); | | |
| 12805 | try w.splatByteAll(' ', indent); | 13025 | try w.splatByteAll(' ', indent); |
| 12806 | try w.writeAll(@tagName(node)); | 13026 | try w.print("{f}", .{Node.fmtDebug(ni, elf)}); |
| 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 | } | | |
| 12909 | { | 13027 | { |
| 12910 | const mf_node = &elf.mf.nodes.items[@backingInt(ni)]; | 13028 | const mf_node = &elf.mf.nodes.items[@backingInt(ni)]; |
| 12911 | const off, const size = mf_node.location().resolve(&elf.mf); | 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,15 +13093,8 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error |
| 12975 | switch (elf.phdrSlice()) { | 13093 | switch (elf.phdrSlice()) { |
| 12976 | inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) { | 13094 | inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) { |
| 12977 | .NULL, .LOAD => { | 13095 | .NULL, .LOAD => { |
| 12978 | // The `@"align"` field is managed by `allocateSegmentLoadAddress`. | 13096 | // The `@"align"` field is managed by `updateSegmentLoadAddresses`, which will |
| 12979 | // | 13097 | // be called at the end of the current link task. |
| 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 | } | | |
| 12987 | }, | 13098 | }, |
| 12988 | else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max( | 13099 | else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max( |
| 12989 | elf.targetLoad(&phdr[phndx].@"align"), | 13100 | elf.targetLoad(&phdr[phndx].@"align"), |
| ... | @@ -13008,11 +13119,9 @@ pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) M | ... | @@ -13008,11 +13119,9 @@ pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) M |
| 13008 | return ni; | 13119 | return ni; |
| 13009 | } | 13120 | } |
| 13010 | | 13121 | |
| 13011 | fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocator.Error!void { | 13122 | fn deleteNode(elf: *Elf, node: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 13012 | const ni = node.unwrap().?; | 13123 | try node.delete(elf.base.comp.gpa, &elf.mf); |
| 13013 | try ni.delete(elf.base.comp.gpa, &elf.mf); | 13124 | elf.nodes.set(@backingInt(node), .deleted); |
| 13014 | elf.nodes.set(@backingInt(ni), .deleted); | | |
| 13015 | node.* = .none; | | |
| 13016 | } | 13125 | } |
| 13017 | | 13126 | |
| 13018 | /// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a | 13127 | /// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a |