| ... | @@ -49,6 +49,20 @@ globals: struct { | ... | @@ -49,6 +49,20 @@ globals: struct { |
| 49 | strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global), | 49 | strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global), |
| 50 | weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global), | 50 | weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global), |
| 51 | }, | 51 | }, |
| | 52 | /// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`). |
| | 53 | copied_globals: std.array_hash_map.Auto(String(.strtab), struct { |
| | 54 | node: MappedFile.Node.Index, |
| | 55 | /// The index of this global's runtime relocation in `.rela.dyn`. |
| | 56 | rela_index: Section.RelaIndex, |
| | 57 | }), |
| | 58 | /// Key is the name of an undef global for which we would *like* to create a copy relocation |
| | 59 | /// (`R_*_COPY`), but cannot because we have not seen an appropriate definition in a linked DSO yet. |
| | 60 | /// |
| | 61 | /// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we |
| | 62 | /// will remove it from this map and call ` |
| | 63 | /// |
| | 64 | /// which have not created |
| | 65 | want_copied_globals: std.array_hash_map.Auto(String(.strtab), void), |
| 52 | /// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol | 66 | /// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol |
| 53 | /// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`. | 67 | /// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`. |
| 54 | /// | 68 | /// |
| ... | @@ -57,14 +71,29 @@ globals: struct { | ... | @@ -57,14 +71,29 @@ globals: struct { |
| 57 | /// We use a separate hash map for this data rather than storing it in `navs` etc to save memory, | 71 | /// We use a separate hash map for this data rather than storing it in `navs` etc to save memory, |
| 58 | /// because the vast majority of nodes which can export global symbols actually will not. | 72 | /// because the vast majority of nodes which can export global symbols actually will not. |
| 59 | node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)), | 73 | node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)), |
| 60 | /// Contains all globals symbols defined in any needed DSO. This map serves two purposes: | 74 | /// Contains all globals symbols defined in any needed DSO. This map serves three purposes: |
| | 75 | /// |
| | 76 | /// * If we discover an undefined reference to one of these symbols, we know whether the symbol has |
| | 77 | /// type `STT_FUNC`, in which case we will create a PLT entry. |
| 61 | /// | 78 | /// |
| 62 | /// * If we discover an undefined reference to one of these symbols, we will know the associated | 79 | /// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these |
| 63 | /// symbol type, which is important because it may cause us to create a PLT entry. | 80 | /// symbols, we know whether the symbol has type `STT_OBJECT` and we know its size and alignment, |
| | 81 | /// so we can emit a copy relocation for that symbol instead of using a text relocation. |
| 64 | /// | 82 | /// |
| 65 | /// * When emitting a dynamic executable, we can detect which undefined references are resolved by a | 83 | /// * When emitting a dynamic executable, we can detect which undefined references are resolved by a |
| 66 | /// linked DSO, so can emit "undefined global symbol" errors for any other undefined references. | 84 | /// linked DSO, so can emit "undefined global symbol" errors for any other undefined references. |
| 67 | dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT), | 85 | dso_globals: std.array_hash_map.Auto(String(.strtab), struct { |
| | 86 | type: std.elf.STT, |
| | 87 | size: u64, |
| | 88 | /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and so |
| | 89 | /// becomes a part of the executable's address space despite being defined by a different DSO, |
| | 90 | /// we need to know its alignment requirement so that we don't break other code. This isn't |
| | 91 | /// actually stored on the symbol---instead we compute a maximum alignment from the alignment of |
| | 92 | /// the section containing the symbol, and the symbol's offset within the section. I know this |
| | 93 | /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy |
| | 94 | /// relocations suck. |
| | 95 | alignment: std.mem.Alignment, |
| | 96 | }), |
| 68 | shstrtab: StringTable, | 97 | shstrtab: StringTable, |
| 69 | strtab: StringTable, | 98 | strtab: StringTable, |
| 70 | dynstr: StringTable, | 99 | dynstr: StringTable, |
| ... | @@ -136,22 +165,24 @@ synth_prog_node: std.Progress.Node, | ... | @@ -136,22 +165,24 @@ synth_prog_node: std.Progress.Node, |
| 136 | input_prog_node: std.Progress.Node, | 165 | input_prog_node: std.Progress.Node, |
| 137 | | 166 | |
| 138 | const Node = union(enum) { | 167 | const Node = union(enum) { |
| 139 | /// Cannot contain relocations. | | |
| 140 | file, | 168 | file, |
| 141 | /// Cannot contain relocations. | | |
| 142 | ehdr, | 169 | ehdr, |
| 143 | /// Cannot contain relocations. | | |
| 144 | shdr, | 170 | shdr, |
| 145 | /// Cannot contain relocations. | | |
| 146 | segment: u32, | 171 | segment: u32, |
| 147 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. | 172 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. |
| 148 | /// | 173 | /// |
| 149 | /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`. | 174 | /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`. |
| 150 | /// | | |
| 151 | /// Otherwise, cannot contain relocations. | | |
| 152 | section: Section.Index, | 175 | section: Section.Index, |
| 153 | /// May contain relocations. | 176 | /// May contain relocations. |
| 154 | input_section: InputSection.Index, | 177 | input_section: InputSection.Index, |
| | 178 | /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for |
| | 179 | /// which we have emitted a copy relocation. |
| | 180 | /// |
| | 181 | /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those. |
| | 182 | /// |
| | 183 | /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because |
| | 184 | /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`. |
| | 185 | copied_global: String(.strtab), |
| 155 | /// May contain relocations. | 186 | /// May contain relocations. |
| 156 | nav: NavMapIndex, | 187 | nav: NavMapIndex, |
| 157 | /// May contain relocations. | 188 | /// May contain relocations. |
| ... | @@ -746,6 +777,7 @@ const GotReloc = struct { | ... | @@ -746,6 +777,7 @@ const GotReloc = struct { |
| 746 | .ehdr => unreachable, | 777 | .ehdr => unreachable, |
| 747 | .shdr => unreachable, | 778 | .shdr => unreachable, |
| 748 | .segment => unreachable, | 779 | .segment => unreachable, |
| | 780 | .copied_global => unreachable, |
| 749 | .section => |shndx| shndx.vaddr(elf), | 781 | .section => |shndx| shndx.vaddr(elf), |
| 750 | .input_section => |isi| isi.ptrConst(elf).vaddr, | 782 | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| 751 | inline .nav, | 783 | inline .nav, |
| ... | @@ -808,6 +840,15 @@ pub const MachineRelocType = union { | ... | @@ -808,6 +840,15 @@ pub const MachineRelocType = union { |
| 808 | .X86_64 => .{ .X86_64 = .NONE }, | 840 | .X86_64 => .{ .X86_64 = .NONE }, |
| 809 | }; | 841 | }; |
| 810 | } | 842 | } |
| | 843 | pub fn copy(elf: *Elf) MachineRelocType { |
| | 844 | return switch (elf.ehdrField(.machine)) { |
| | 845 | else => unreachable, |
| | 846 | .AARCH64 => .{ .AARCH64 = .COPY }, |
| | 847 | .PPC64 => .{ .PPC64 = .COPY }, |
| | 848 | .RISCV => .{ .RISCV = .COPY }, |
| | 849 | .X86_64 => .{ .X86_64 = .COPY }, |
| | 850 | }; |
| | 851 | } |
| 811 | pub fn jumpSlot(elf: *Elf) MachineRelocType { | 852 | pub fn jumpSlot(elf: *Elf) MachineRelocType { |
| 812 | return switch (elf.ehdrField(.machine)) { | 853 | return switch (elf.ehdrField(.machine)) { |
| 813 | else => unreachable, | 854 | else => unreachable, |
| ... | @@ -973,6 +1014,7 @@ const SymbolReloc = struct { | ... | @@ -973,6 +1014,7 @@ const SymbolReloc = struct { |
| 973 | .ehdr => unreachable, | 1014 | .ehdr => unreachable, |
| 974 | .shdr => unreachable, | 1015 | .shdr => unreachable, |
| 975 | .segment => unreachable, | 1016 | .segment => unreachable, |
| | 1017 | .copied_global => unreachable, |
| 976 | .section => |shndx| shndx.vaddr(elf), | 1018 | .section => |shndx| shndx.vaddr(elf), |
| 977 | .input_section => |isi| isi.ptrConst(elf).vaddr, | 1019 | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| 978 | inline .nav, | 1020 | inline .nav, |
| ... | @@ -984,11 +1026,9 @@ const SymbolReloc = struct { | ... | @@ -984,11 +1026,9 @@ const SymbolReloc = struct { |
| 984 | const dest_vaddr = node_vaddr + reloc.offset; | 1026 | const dest_vaddr = node_vaddr + reloc.offset; |
| 985 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; | 1027 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 986 | const target_endian = elf.targetEndian(); | 1028 | const target_endian = elf.targetEndian(); |
| 987 | const sym_value: u64, const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) { | 1029 | const sym_value: u64 = reloc.target.value(elf); |
| 988 | inline else => |target_sym| .{ | 1030 | const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) { |
| 989 | elf.targetLoad(&target_sym.value), | 1031 | inline else => |target_sym| elf.targetLoad(&target_sym.size), |
| 990 | elf.targetLoad(&target_sym.size), | | |
| 991 | }, | | |
| 992 | }; | 1032 | }; |
| 993 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); | 1033 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); |
| 994 | type: switch (reloc.type) { | 1034 | type: switch (reloc.type) { |
| ... | @@ -1114,6 +1154,12 @@ const SymbolReloc = struct { | ... | @@ -1114,6 +1154,12 @@ const SymbolReloc = struct { |
| 1114 | | 1154 | |
| 1115 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { | 1155 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { |
| 1116 | assert(index.get(elf) == reloc); | 1156 | assert(index.get(elf) == reloc); |
| | 1157 | |
| | 1158 | reloc.deleteOutputRel(elf); |
| | 1159 | if (reloc.type.dependsOnTlsSize()) { |
| | 1160 | assert(elf.tls_size_symbol_relocs.swapRemove(index)); |
| | 1161 | } |
| | 1162 | |
| 1117 | switch (reloc.prev) { | 1163 | switch (reloc.prev) { |
| 1118 | .none => { | 1164 | .none => { |
| 1119 | const target_ptr = reloc.target.index(elf).ptr(elf); | 1165 | const target_ptr = reloc.target.index(elf).ptr(elf); |
| ... | @@ -1126,18 +1172,25 @@ const SymbolReloc = struct { | ... | @@ -1126,18 +1172,25 @@ const SymbolReloc = struct { |
| 1126 | .none => {}, | 1172 | .none => {}, |
| 1127 | else => |next| next.get(elf).prev = reloc.prev, | 1173 | else => |next| next.get(elf).prev = reloc.prev, |
| 1128 | } | 1174 | } |
| 1129 | if (reloc.rela_index.unwrap()) |rela_index| { | 1175 | |
| 1130 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); | 1176 | reloc.* = undefined; |
| 1131 | switch (elf.nodeWantsDsoRelocation(reloc.node)) { | 1177 | } |
| | 1178 | |
| | 1179 | /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation, updating |
| | 1180 | /// `elf.textrel_count` if necessary. |
| | 1181 | fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void { |
| | 1182 | const rela_index = reloc.rela_index.unwrap() orelse return; |
| | 1183 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| | 1184 | switch (elf.ehdrField(.type)) { |
| | 1185 | .NONE, .CORE, _ => unreachable, |
| | 1186 | .REL => {}, |
| | 1187 | .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) { |
| 1132 | .no => unreachable, // there *was* a dynamic relocation! | 1188 | .no => unreachable, // there *was* a dynamic relocation! |
| 1133 | .yes => {}, | 1189 | .yes => {}, |
| 1134 | .yes_textrel => elf.textrel_count -= 1, | 1190 | .yes_textrel => elf.textrel_count -= 1, |
| 1135 | } | 1191 | }, |
| 1136 | } | | |
| 1137 | if (reloc.type.dependsOnTlsSize()) { | | |
| 1138 | assert(elf.tls_size_symbol_relocs.swapRemove(index)); | | |
| 1139 | } | 1192 | } |
| 1140 | reloc.* = undefined; | 1193 | reloc.rela_index = .none; |
| 1141 | } | 1194 | } |
| 1142 | }; | 1195 | }; |
| 1143 | | 1196 | |
| ... | @@ -1468,7 +1521,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -1468,7 +1521,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 1468 | }; | 1521 | }; |
| 1469 | | 1522 | |
| 1470 | const @"type": std.elf.STT = switch (opts.type) { | 1523 | const @"type": std.elf.STT = switch (opts.type) { |
| 1471 | .NOTYPE => elf.dso_globals.get(opts.name.strtab) orelse .NOTYPE, | 1524 | .NOTYPE => if (elf.dso_globals.get(opts.name.strtab)) |dso_global| t: { |
| | 1525 | break :t dso_global.type; |
| | 1526 | } else .NOTYPE, |
| 1472 | else => |t| t, | 1527 | else => |t| t, |
| 1473 | }; | 1528 | }; |
| 1474 | | 1529 | |
| ... | @@ -1605,6 +1660,23 @@ fn setGlobalSymbolValue( | ... | @@ -1605,6 +1660,23 @@ fn setGlobalSymbolValue( |
| 1605 | assert(global_ptr.prev_in_node == .empty); | 1660 | assert(global_ptr.prev_in_node == .empty); |
| 1606 | } | 1661 | } |
| 1607 | | 1662 | |
| | 1663 | if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| { |
| | 1664 | // This is a quite rare case: there was a definition for this symbol in a shared library |
| | 1665 | // input, and we ended up emitting a copy relocation for it, but we've now got our *own* |
| | 1666 | // definition which replaces it. We know that our definition cannot be preempted because we |
| | 1667 | // are the executable (only executables can have copy relocations!), so we definitely do not |
| | 1668 | // need the copy relocation. |
| | 1669 | |
| | 1670 | // All we actually need to do is remove the entry from `copied_globals` (already done), and |
| | 1671 | // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations |
| | 1672 | // targeting this symbol, but we were going to do that at the end of this function anyway. |
| | 1673 | elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index); |
| | 1674 | // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we |
| | 1675 | // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node. |
| | 1676 | } else { |
| | 1677 | _ = elf.want_copied_globals.swapRemove(global_name); |
| | 1678 | } |
| | 1679 | |
| 1608 | global_ptr.symtab_index.ptr(elf).node = new.node; | 1680 | global_ptr.symtab_index.ptr(elf).node = new.node; |
| 1609 | | 1681 | |
| 1610 | const old_head: String(.strtab) = old_head: { | 1682 | const old_head: String(.strtab) = old_head: { |
| ... | @@ -1668,21 +1740,7 @@ fn setGlobalSymbolValue( | ... | @@ -1668,21 +1740,7 @@ fn setGlobalSymbolValue( |
| 1668 | // If this symbol was previously undefined, relocations targeting it may have been lowered to | 1740 | // If this symbol was previously undefined, relocations targeting it may have been lowered to |
| 1669 | // runtime relocations which we have now discovered we do not need, so delete those. | 1741 | // runtime relocations which we have now discovered we do not need, so delete those. |
| 1670 | if (elf.shndx.dynamic != .UNDEF) { | 1742 | if (elf.shndx.dynamic != .UNDEF) { |
| 1671 | var ri = global_ptr.symtab_index.ptr(elf).first_target_reloc; | 1743 | Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf); |
| 1672 | while (ri != .none) { | | |
| 1673 | const reloc = ri.get(elf); | | |
| 1674 | assert(reloc.target == Symbol.Id.global(global_name)); | | |
| 1675 | if (reloc.rela_index.unwrap()) |rela_index| { | | |
| 1676 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); | | |
| 1677 | switch (elf.nodeWantsDsoRelocation(reloc.node)) { | | |
| 1678 | .no => unreachable, // there *was* a dynamic relocation! | | |
| 1679 | .yes => {}, | | |
| 1680 | .yes_textrel => elf.textrel_count -= 1, | | |
| 1681 | } | | |
| 1682 | reloc.rela_index = .none; | | |
| 1683 | } | | |
| 1684 | ri = reloc.next; | | |
| 1685 | } | | |
| 1686 | } | 1744 | } |
| 1687 | | 1745 | |
| 1688 | // Finally, update the symbol value, re-applying target relocations. Also note that because we | 1746 | // Finally, update the symbol value, re-applying target relocations. Also note that because we |
| ... | @@ -2029,6 +2087,9 @@ const Symbol = struct { | ... | @@ -2029,6 +2087,9 @@ const Symbol = struct { |
| 2029 | }; | 2087 | }; |
| 2030 | } | 2088 | } |
| 2031 | | 2089 | |
| | 2090 | /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined |
| | 2091 | /// global for which we have emitted a copy relocation, returns the virtual address of that |
| | 2092 | /// copy relocation, which the symbol is guaranteed to resolve to at runtime. |
| 2032 | fn value(s: Symbol.Id, elf: *Elf) u64 { | 2093 | fn value(s: Symbol.Id, elf: *Elf) u64 { |
| 2033 | return switch (elf.symPtr(s.index(elf))) { | 2094 | return switch (elf.symPtr(s.index(elf))) { |
| 2034 | inline else => |sym| elf.targetLoad(&sym.value), | 2095 | inline else => |sym| elf.targetLoad(&sym.value), |
| ... | @@ -2084,12 +2145,36 @@ const Symbol = struct { | ... | @@ -2084,12 +2145,36 @@ const Symbol = struct { |
| 2084 | } | 2145 | } |
| 2085 | } | 2146 | } |
| 2086 | | 2147 | |
| | 2148 | /// Scans through all relocations targeting `sym_id` and deletes each one's dynamic |
| | 2149 | /// relocation entry, if it has one. |
| | 2150 | /// |
| | 2151 | /// Asserts we are creating a DSO. |
| | 2152 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| | 2153 | assert(elf.ehdrField(.type) != .REL); |
| | 2154 | assert(elf.shndx.dynamic != .UNDEF); |
| | 2155 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| | 2156 | while (ri != .none) { |
| | 2157 | const reloc = ri.get(elf); |
| | 2158 | assert(reloc.target == sym_id); |
| | 2159 | reloc.deleteOutputRel(elf); |
| | 2160 | ri = reloc.next; |
| | 2161 | } |
| | 2162 | } |
| | 2163 | |
| 2087 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at | 2164 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at |
| 2088 | /// some point due to a call to `flushMoved`. | 2165 | /// some point due to a call to `flushMoved`. |
| 2089 | fn hasMoved(s: Symbol.Id, elf: *Elf) bool { | 2166 | fn hasMoved(s: Symbol.Id, elf: *Elf) bool { |
| 2090 | const node = s.index(elf).ptr(elf).node; | 2167 | const node = s.index(elf).ptr(elf).node; |
| 2091 | if (node == .none) return false; | 2168 | if (node != .none) { |
| 2092 | return node.hasMoved(&elf.mf); | 2169 | return node.hasMoved(&elf.mf); |
| | 2170 | } |
| | 2171 | switch (s.unwrap()) { |
| | 2172 | .local => {}, |
| | 2173 | .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| { |
| | 2174 | return copied_global.node.hasMoved(&elf.mf); |
| | 2175 | }, |
| | 2176 | } |
| | 2177 | return false; |
| 2093 | } | 2178 | } |
| 2094 | }; | 2179 | }; |
| 2095 | }; | 2180 | }; |
| ... | @@ -2110,6 +2195,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { | ... | @@ -2110,6 +2195,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| 2110 | .segment, | 2195 | .segment, |
| 2111 | .section, | 2196 | .section, |
| 2112 | .input_section, | 2197 | .input_section, |
| | 2198 | .copied_global, |
| 2113 | => unreachable, | 2199 | => unreachable, |
| 2114 | | 2200 | |
| 2115 | inline .nav, | 2201 | inline .nav, |
| ... | @@ -2204,7 +2290,7 @@ pub fn addReloc( | ... | @@ -2204,7 +2290,7 @@ pub fn addReloc( |
| 2204 | ) !void { | 2290 | ) !void { |
| 2205 | const node: MappedFile.Node.Index = Node.fromAtom(atom); | 2291 | const node: MappedFile.Node.Index = Node.fromAtom(atom); |
| 2206 | try elf.ensureUnusedRelocCapacity(node, 1); | 2292 | try elf.ensureUnusedRelocCapacity(node, 1); |
| 2207 | elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type"); | 2293 | try elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type"); |
| 2208 | } | 2294 | } |
| 2209 | pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId { | 2295 | pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId { |
| 2210 | const zcu = elf.base.comp.zcu.?; | 2296 | const zcu = elf.base.comp.zcu.?; |
| ... | @@ -2252,7 +2338,7 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy | ... | @@ -2252,7 +2338,7 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy |
| 2252 | const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index); | 2338 | const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index); |
| 2253 | const target_sym: Symbol.Id = .fromTypeErased(target); | 2339 | const target_sym: Symbol.Id = .fromTypeErased(target); |
| 2254 | try elf.ensureUnusedRelocCapacity(node, 1); | 2340 | try elf.ensureUnusedRelocCapacity(node, 1); |
| 2255 | elf.addRelocAssumeCapacity( | 2341 | try elf.addRelocAssumeCapacity( |
| 2256 | node, | 2342 | node, |
| 2257 | reloc_info.offset, | 2343 | reloc_info.offset, |
| 2258 | target_sym, | 2344 | target_sym, |
| ... | @@ -2511,6 +2597,8 @@ fn create( | ... | @@ -2511,6 +2597,8 @@ fn create( |
| 2511 | .strong_undef = .empty, | 2597 | .strong_undef = .empty, |
| 2512 | .weak_undef = .empty, | 2598 | .weak_undef = .empty, |
| 2513 | }, | 2599 | }, |
| | 2600 | .copied_globals = .empty, |
| | 2601 | .want_copied_globals = .empty, |
| 2514 | .node_global_symbols = .empty, | 2602 | .node_global_symbols = .empty, |
| 2515 | .dso_globals = .empty, | 2603 | .dso_globals = .empty, |
| 2516 | .shstrtab = .{ .map = .empty }, | 2604 | .shstrtab = .{ .map = .empty }, |
| ... | @@ -2558,6 +2646,8 @@ pub fn deinit(elf: *Elf) void { | ... | @@ -2558,6 +2646,8 @@ pub fn deinit(elf: *Elf) void { |
| 2558 | elf.globals.weak_def.deinit(gpa); | 2646 | elf.globals.weak_def.deinit(gpa); |
| 2559 | elf.globals.strong_undef.deinit(gpa); | 2647 | elf.globals.strong_undef.deinit(gpa); |
| 2560 | elf.globals.weak_undef.deinit(gpa); | 2648 | elf.globals.weak_undef.deinit(gpa); |
| | 2649 | elf.copied_globals.deinit(gpa); |
| | 2650 | elf.want_copied_globals.deinit(gpa); |
| 2561 | elf.node_global_symbols.deinit(gpa); | 2651 | elf.node_global_symbols.deinit(gpa); |
| 2562 | elf.dso_globals.deinit(gpa); | 2652 | elf.dso_globals.deinit(gpa); |
| 2563 | elf.shstrtab.map.deinit(gpa); | 2653 | elf.shstrtab.map.deinit(gpa); |
| ... | @@ -3117,14 +3207,14 @@ fn initHeaders( | ... | @@ -3117,14 +3207,14 @@ fn initHeaders( |
| 3117 | }); | 3207 | }); |
| 3118 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 3208 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 3119 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); | 3209 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 3120 | elf.addRelocAssumeCapacity( | 3210 | try elf.addRelocAssumeCapacity( |
| 3121 | plt_ni, | 3211 | plt_ni, |
| 3122 | 2, | 3212 | 2, |
| 3123 | got_plt_sym, | 3213 | got_plt_sym, |
| 3124 | 8 * 1 - 4, | 3214 | 8 * 1 - 4, |
| 3125 | .{ .X86_64 = .PC32 }, | 3215 | .{ .X86_64 = .PC32 }, |
| 3126 | ); | 3216 | ); |
| 3127 | elf.addRelocAssumeCapacity( | 3217 | try elf.addRelocAssumeCapacity( |
| 3128 | plt_ni, | 3218 | plt_ni, |
| 3129 | 8, | 3219 | 8, |
| 3130 | got_plt_sym, | 3220 | got_plt_sym, |
| ... | @@ -3267,7 +3357,7 @@ pub fn endProgress(elf: *Elf) void { | ... | @@ -3267,7 +3357,7 @@ pub fn endProgress(elf: *Elf) void { |
| 3267 | fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { | 3357 | fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { |
| 3268 | return elf.nodes.get(@intFromEnum(ni)); | 3358 | return elf.nodes.get(@intFromEnum(ni)); |
| 3269 | } | 3359 | } |
| 3270 | /// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data. | 3360 | /// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data. |
| 3271 | fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { | 3361 | fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { |
| 3272 | return switch (elf.getNode(ni)) { | 3362 | return switch (elf.getNode(ni)) { |
| 3273 | .file => unreachable, | 3363 | .file => unreachable, |
| ... | @@ -3278,6 +3368,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { | ... | @@ -3278,6 +3368,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { |
| 3278 | .section => |shndx| shndx, | 3368 | .section => |shndx| shndx, |
| 3279 | | 3369 | |
| 3280 | .input_section, | 3370 | .input_section, |
| | 3371 | .copied_global, |
| 3281 | .nav, | 3372 | .nav, |
| 3282 | .uav, | 3373 | .uav, |
| 3283 | .lazy_code, | 3374 | .lazy_code, |
| ... | @@ -3294,6 +3385,7 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { | ... | @@ -3294,6 +3385,7 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 3294 | }, | 3385 | }, |
| 3295 | .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), | 3386 | .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), |
| 3296 | .input_section => unreachable, | 3387 | .input_section => unreachable, |
| | 3388 | .copied_global => unreachable, |
| 3297 | inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), | 3389 | inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 3298 | }; | 3390 | }; |
| 3299 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); | 3391 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| ... | @@ -3312,6 +3404,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { | ... | @@ -3312,6 +3404,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 3312 | .shdr => unreachable, // cannot contain relocs | 3404 | .shdr => unreachable, // cannot contain relocs |
| 3313 | .segment => unreachable, // cannot contain relocs | 3405 | .segment => unreachable, // cannot contain relocs |
| 3314 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) | 3406 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| | 3407 | .copied_global => unreachable, // cannot contain relocs |
| 3315 | .input_section => |isi| .{ | 3408 | .input_section => |isi| .{ |
| 3316 | &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc, | 3409 | &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc, |
| 3317 | &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc, | 3410 | &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc, |
| ... | @@ -4270,7 +4363,7 @@ fn loadObject( | ... | @@ -4270,7 +4363,7 @@ fn loadObject( |
| 4270 | .{rel.info.sym}, | 4363 | .{rel.info.sym}, |
| 4271 | ); | 4364 | ); |
| 4272 | } | 4365 | } |
| 4273 | elf.addRelocAssumeCapacity( | 4366 | try elf.addRelocAssumeCapacity( |
| 4274 | loc_node, | 4367 | loc_node, |
| 4275 | rel.offset - loc_sec.shdr.addr, | 4368 | rel.offset - loc_sec.shdr.addr, |
| 4276 | target, | 4369 | target, |
| ... | @@ -4303,12 +4396,19 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | ... | @@ -4303,12 +4396,19 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 4303 | if (ehdr.machine != elf.ehdrField(.machine)) | 4396 | if (ehdr.machine != elf.ehdrField(.machine)) |
| 4304 | return diags.failParse(path, "bad machine", .{}); | 4397 | return diags.failParse(path, "bad machine", .{}); |
| 4305 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); | 4398 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); |
| | 4399 | // We're going to need to know the alignment of every section later. |
| | 4400 | const section_aligns = try gpa.alloc(std.mem.Alignment, ehdr.shnum); |
| | 4401 | defer gpa.free(section_aligns); |
| 4306 | const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: { | 4402 | const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: { |
| 4307 | var dynamic_sh: ?ElfN.Shdr = null; | 4403 | var dynamic_sh: ?ElfN.Shdr = null; |
| 4308 | var dynsym_sh: ?ElfN.Shdr = null; | 4404 | var dynsym_sh: ?ElfN.Shdr = null; |
| 4309 | for (0..ehdr.shnum) |_| { | 4405 | for (section_aligns) |*section_align| { |
| 4310 | const sh = try r.peekStruct(ElfN.Shdr, target_endian); | 4406 | const sh = try r.peekStruct(ElfN.Shdr, target_endian); |
| 4311 | try r.discardAll(ehdr.shentsize); | 4407 | try r.discardAll(ehdr.shentsize); |
| | 4408 | section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| | 4409 | usize, |
| | 4410 | @intCast(@max(sh.addralign, 1)), |
| | 4411 | )); |
| 4312 | switch (sh.type) { | 4412 | switch (sh.type) { |
| 4313 | else => {}, | 4413 | else => {}, |
| 4314 | .DYNAMIC => dynamic_sh = sh, | 4414 | .DYNAMIC => dynamic_sh = sh, |
| ... | @@ -4395,17 +4495,51 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | ... | @@ -4395,17 +4495,51 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 4395 | } | 4495 | } |
| 4396 | | 4496 | |
| 4397 | if (sym.shndx == std.elf.SHN_UNDEF) continue; | 4497 | if (sym.shndx == std.elf.SHN_UNDEF) continue; |
| | 4498 | if (sym.shndx >= ehdr.shnum) continue; |
| 4398 | | 4499 | |
| 4399 | if (sym.name >= dynstr.len) { | 4500 | if (sym.name >= dynstr.len) { |
| 4400 | return diags.failParse(path, "bad symbol name string", .{}); | 4501 | return diags.failParse(path, "bad symbol name string", .{}); |
| 4401 | } | 4502 | } |
| 4402 | | 4503 | |
| | 4504 | // We need to guess the worst-case alignment of the symbol. Yes, I know this seems |
| | 4505 | // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`. |
| | 4506 | const sym_align: std.mem.Alignment = switch (sym.value) { |
| | 4507 | 0 => section_aligns[sym.shndx], |
| | 4508 | else => section_aligns[sym.shndx].min(@enumFromInt(@ctz(sym.value))), |
| | 4509 | }; |
| | 4510 | |
| 4403 | const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0)); | 4511 | const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0)); |
| 4404 | const gop = elf.dso_globals.getOrPutAssumeCapacity(name); | 4512 | const gop = elf.dso_globals.getOrPutAssumeCapacity(name); |
| 4405 | if (!gop.found_existing or gop.value_ptr.* == .NOTYPE) { | 4513 | |
| 4406 | gop.value_ptr.* = sym.info.type; | 4514 | if (gop.found_existing and gop.value_ptr.type != .NOTYPE) { |
| | 4515 | if (sym.size > gop.value_ptr.size or |
| | 4516 | sym_align.compare(.gt, gop.value_ptr.alignment)) |
| | 4517 | { |
| | 4518 | gop.value_ptr.size = @max(gop.value_ptr.size, sym.size); |
| | 4519 | gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align); |
| | 4520 | if (elf.copied_globals.get(name)) |copied_global| { |
| | 4521 | // We have a copy relocation for this global, but the amount of space we |
| | 4522 | // reserved for it could be too small or underaligned! |
| | 4523 | try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size); |
| | 4524 | try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment); |
| | 4525 | const global_ptr = elf.globalByName(name).?; |
| | 4526 | switch (elf.symPtr(global_ptr.symtab_index)) { |
| | 4527 | inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)), |
| | 4528 | } |
| | 4529 | switch (elf.dynsymPtr(global_ptr.dynsym_index)) { |
| | 4530 | inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)), |
| | 4531 | } |
| | 4532 | } |
| | 4533 | } |
| | 4534 | continue; |
| 4407 | } | 4535 | } |
| 4408 | | 4536 | |
| | 4537 | gop.value_ptr.* = .{ |
| | 4538 | .type = sym.info.type, |
| | 4539 | .size = sym.size, |
| | 4540 | .alignment = sym_align, |
| | 4541 | }; |
| | 4542 | |
| 4409 | // If there's already an undefined symbol by this name of type STT_NOTYPE, populate | 4543 | // If there's already an undefined symbol by this name of type STT_NOTYPE, populate |
| 4410 | // its type now. | 4544 | // its type now. |
| 4411 | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse | 4545 | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse |
| ... | @@ -4414,14 +4548,20 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | ... | @@ -4414,14 +4548,20 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 4414 | | 4548 | |
| 4415 | if (global_ptr.dynsym_index == 0) continue; | 4549 | if (global_ptr.dynsym_index == 0) continue; |
| 4416 | | 4550 | |
| | 4551 | if (elf.want_copied_globals.swapRemove(name)) { |
| | 4552 | // We just found a DSO definition of a symbol for which we wanted a copy |
| | 4553 | // relocation, so add one if we can! |
| | 4554 | _ = try elf.maybeAddCopyRelocation(name); |
| | 4555 | } |
| | 4556 | |
| 4417 | const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class)); | 4557 | const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class)); |
| | 4558 | errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr` |
| | 4559 | |
| 4418 | switch (elf.targetLoad(&sym_ptr.other).visibility) { | 4560 | switch (elf.targetLoad(&sym_ptr.other).visibility) { |
| 4419 | .HIDDEN, .INTERNAL, .PROTECTED => continue, | 4561 | .HIDDEN, .INTERNAL, .PROTECTED => continue, |
| 4420 | .DEFAULT => {}, | 4562 | .DEFAULT => {}, |
| 4421 | } | 4563 | } |
| 4422 | | 4564 | |
| 4423 | if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue; | | |
| 4424 | | | |
| 4425 | const cur_info = elf.targetLoad(&sym_ptr.info); | 4565 | const cur_info = elf.targetLoad(&sym_ptr.info); |
| 4426 | if (cur_info.type == .NOTYPE) { | 4566 | if (cur_info.type == .NOTYPE) { |
| 4427 | const new_type: std.elf.STT = switch (sym.info.type) { | 4567 | const new_type: std.elf.STT = switch (sym.info.type) { |
| ... | @@ -4440,9 +4580,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | ... | @@ -4440,9 +4580,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 4440 | .type = new_type, | 4580 | .type = new_type, |
| 4441 | }); | 4581 | }); |
| 4442 | | 4582 | |
| 4443 | // If we just turned this into an STT_FUNC symbol, then we have determined | | |
| 4444 | // that it needs a PLT entry. | | |
| 4445 | if (new_type == .FUNC) { | 4583 | if (new_type == .FUNC) { |
| | 4584 | // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry... |
| 4446 | elf.addPltEntry(name, global_ptr.dynsym_index); | 4585 | elf.addPltEntry(name, global_ptr.dynsym_index); |
| 4447 | // ...and therefore, we need to re-apply that symbol's relocations, as | 4586 | // ...and therefore, we need to re-apply that symbol's relocations, as |
| 4448 | // some might be targeting its PLT entry. | 4587 | // some might be targeting its PLT entry. |
| ... | @@ -4630,6 +4769,9 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -4630,6 +4769,9 @@ fn prelinkInner(elf: *Elf) !void { |
| 4630 | } | 4769 | } |
| 4631 | break :rpath try elf.string(.dynstr, buf.items); | 4770 | break :rpath try elf.string(.dynstr, buf.items); |
| 4632 | }; | 4771 | }; |
| | 4772 | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { |
| | 4773 | break :str try elf.string(.dynstr, soname_slice); |
| | 4774 | } else null; |
| 4633 | const needed_len = elf.needed.count(); | 4775 | const needed_len = elf.needed.count(); |
| 4634 | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + | 4776 | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + |
| 4635 | @intFromBool(rpath != .empty) + | 4777 | @intFromBool(rpath != .empty) + |
| ... | @@ -4644,141 +4786,155 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -4644,141 +4786,155 @@ fn prelinkInner(elf: *Elf) !void { |
| 4644 | switch (elf.shdrPtr(elf.shndx.dynamic)) { | 4786 | switch (elf.shdrPtr(elf.shndx.dynamic)) { |
| 4645 | inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size), | 4787 | inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size), |
| 4646 | } | 4788 | } |
| 4647 | const sec_dynamic = dynamic_ni.slice(&elf.mf); | 4789 | |
| 4648 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); | 4790 | const dynamic_indices: struct { |
| 4649 | var dynamic_index: usize = 0; | 4791 | init_array: ?usize, |
| 4650 | for ( | 4792 | fini_array: ?usize, |
| 4651 | dynamic_entries[dynamic_index..][0..needed_len], | 4793 | preinit_array: ?usize, |
| 4652 | elf.needed.keys(), | 4794 | } = indices: { |
| 4653 | ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) }; | 4795 | const sec_dynamic = dynamic_ni.slice(&elf.mf); |
| 4654 | dynamic_index += needed_len; | 4796 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); |
| 4655 | if (elf.options.soname) |soname| { | 4797 | errdefer comptime unreachable; // don't invalidate `dynamic_entries` |
| 4656 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) }; | 4798 | var dynamic_index: usize = 0; |
| 4657 | dynamic_index += 1; | 4799 | for ( |
| 4658 | } | 4800 | dynamic_entries[dynamic_index..][0..needed_len], |
| 4659 | if (rpath != .empty) { | 4801 | elf.needed.keys(), |
| 4660 | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) }; | 4802 | ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) }; |
| 4661 | dynamic_index += 1; | 4803 | dynamic_index += needed_len; |
| 4662 | } | 4804 | if (soname) |soname_dynstr| { |
| 4663 | if (flags != 0) { | 4805 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) }; |
| 4664 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; | 4806 | dynamic_index += 1; |
| 4665 | dynamic_index += 1; | 4807 | } |
| 4666 | } | 4808 | if (rpath != .empty) { |
| 4667 | if (flags_1 != 0) { | 4809 | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) }; |
| 4668 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 }; | 4810 | dynamic_index += 1; |
| 4669 | dynamic_index += 1; | 4811 | } |
| 4670 | } | 4812 | if (flags != 0) { |
| 4671 | if (comp.config.output_mode == .Exe) { | 4813 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; |
| 4672 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; | 4814 | dynamic_index += 1; |
| 4673 | dynamic_index += 1; | 4815 | } |
| 4674 | } | 4816 | if (flags_1 != 0) { |
| 4675 | if (elf.shndx.init_array != .UNDEF) { | 4817 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 }; |
| 4676 | dynamic_entries[dynamic_index..][0..2].* = .{ | 4818 | dynamic_index += 1; |
| 4677 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, | 4819 | } |
| 4678 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( | 4820 | if (comp.config.output_mode == .Exe) { |
| 4679 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, | 4821 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; |
| | 4822 | dynamic_index += 1; |
| | 4823 | } |
| | 4824 | const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: { |
| | 4825 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| | 4826 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, |
| | 4827 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( |
| | 4828 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, |
| | 4829 | ) }, |
| | 4830 | }; |
| | 4831 | defer dynamic_index += 2; |
| | 4832 | break :i dynamic_index; |
| | 4833 | } else null; |
| | 4834 | const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: { |
| | 4835 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| | 4836 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, |
| | 4837 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( |
| | 4838 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, |
| | 4839 | ) }, |
| | 4840 | }; |
| | 4841 | defer dynamic_index += 2; |
| | 4842 | break :i dynamic_index; |
| | 4843 | } else null; |
| | 4844 | const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: { |
| | 4845 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| | 4846 | .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) }, |
| | 4847 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( |
| | 4848 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, |
| | 4849 | ) }, |
| | 4850 | }; |
| | 4851 | defer dynamic_index += 2; |
| | 4852 | break :i dynamic_index; |
| | 4853 | } else null; |
| | 4854 | dynamic_entries[dynamic_index..][0..12].* = .{ |
| | 4855 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, |
| | 4856 | .{ std.elf.DT_RELASZ, elf.targetLoad( |
| | 4857 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, |
| 4680 | ) }, | 4858 | ) }, |
| 4681 | }; | 4859 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 4682 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 1); | 4860 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, |
| 4683 | elf.addRelocAssumeCapacity( | 4861 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( |
| 4684 | dynamic_ni, | 4862 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, |
| 4685 | @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1), | | |
| 4686 | .local(elf.shndx.init_array.get(elf).lsi), | | |
| 4687 | 0, | | |
| 4688 | .absAddr(elf), | | |
| 4689 | ); | | |
| 4690 | dynamic_index += 2; | | |
| 4691 | } | | |
| 4692 | if (elf.shndx.fini_array != .UNDEF) { | | |
| 4693 | dynamic_entries[dynamic_index..][0..2].* = .{ | | |
| 4694 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, | | |
| 4695 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( | | |
| 4696 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, | | |
| 4697 | ) }, | 4863 | ) }, |
| 4698 | }; | 4864 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, |
| 4699 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 1); | 4865 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| 4700 | elf.addRelocAssumeCapacity( | 4866 | .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) }, |
| 4701 | dynamic_ni, | 4867 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 4702 | @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1), | 4868 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, |
| 4703 | .local(elf.shndx.fini_array.get(elf).lsi), | 4869 | .{ std.elf.DT_STRSZ, elf.targetLoad( |
| 4704 | 0, | 4870 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, |
| 4705 | .absAddr(elf), | | |
| 4706 | ); | | |
| 4707 | dynamic_index += 2; | | |
| 4708 | } | | |
| 4709 | if (elf.shndx.preinit_array != .UNDEF) { | | |
| 4710 | dynamic_entries[dynamic_index..][0..2].* = .{ | | |
| 4711 | .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) }, | | |
| 4712 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( | | |
| 4713 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, | | |
| 4714 | ) }, | 4871 | ) }, |
| | 4872 | .{ std.elf.DT_NULL, 0 }, |
| | 4873 | }; |
| | 4874 | dynamic_index += 12; |
| | 4875 | assert(dynamic_index == dynamic_len); |
| | 4876 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| | 4877 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| | 4878 | |
| | 4879 | break :indices .{ |
| | 4880 | .init_array = init_array_index, |
| | 4881 | .fini_array = fini_array_index, |
| | 4882 | .preinit_array = preinit_array_index, |
| 4715 | }; | 4883 | }; |
| 4716 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 1); | | |
| 4717 | elf.addRelocAssumeCapacity( | | |
| 4718 | dynamic_ni, | | |
| 4719 | @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1), | | |
| 4720 | .local(elf.shndx.preinit_array.get(elf).lsi), | | |
| 4721 | 0, | | |
| 4722 | .absAddr(elf), | | |
| 4723 | ); | | |
| 4724 | dynamic_index += 2; | | |
| 4725 | } | | |
| 4726 | dynamic_entries[dynamic_index..][0..12].* = .{ | | |
| 4727 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, | | |
| 4728 | .{ std.elf.DT_RELASZ, elf.targetLoad( | | |
| 4729 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, | | |
| 4730 | ) }, | | |
| 4731 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, | | |
| 4732 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, | | |
| 4733 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( | | |
| 4734 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, | | |
| 4735 | ) }, | | |
| 4736 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, | | |
| 4737 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, | | |
| 4738 | .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) }, | | |
| 4739 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, | | |
| 4740 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, | | |
| 4741 | .{ std.elf.DT_STRSZ, elf.targetLoad( | | |
| 4742 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, | | |
| 4743 | ) }, | | |
| 4744 | .{ std.elf.DT_NULL, 0 }, | | |
| 4745 | }; | 4884 | }; |
| 4746 | dynamic_index += 12; | | |
| 4747 | assert(dynamic_index == dynamic_len); | | |
| 4748 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| | | |
| 4749 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); | | |
| 4750 | | 4885 | |
| 4751 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 4886 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 4752 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); | 4887 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); |
| 4753 | elf.addRelocAssumeCapacity( | 4888 | if (dynamic_indices.init_array) |index| try elf.addRelocAssumeCapacity( |
| | 4889 | dynamic_ni, |
| | 4890 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| | 4891 | .local(elf.shndx.init_array.get(elf).lsi), |
| | 4892 | 0, |
| | 4893 | .absAddr(elf), |
| | 4894 | ); |
| | 4895 | if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity( |
| | 4896 | dynamic_ni, |
| | 4897 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| | 4898 | .local(elf.shndx.fini_array.get(elf).lsi), |
| | 4899 | 0, |
| | 4900 | .absAddr(elf), |
| | 4901 | ); |
| | 4902 | if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity( |
| | 4903 | dynamic_ni, |
| | 4904 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| | 4905 | .local(elf.shndx.preinit_array.get(elf).lsi), |
| | 4906 | 0, |
| | 4907 | .absAddr(elf), |
| | 4908 | ); |
| | 4909 | try elf.addRelocAssumeCapacity( |
| 4754 | dynamic_ni, | 4910 | dynamic_ni, |
| 4755 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), | 4911 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), |
| 4756 | .local(elf.shndx.rela_dyn.get(elf).lsi), | 4912 | .local(elf.shndx.rela_dyn.get(elf).lsi), |
| 4757 | 0, | 4913 | 0, |
| 4758 | .absAddr(elf), | 4914 | .absAddr(elf), |
| 4759 | ); | 4915 | ); |
| 4760 | elf.addRelocAssumeCapacity( | 4916 | try elf.addRelocAssumeCapacity( |
| 4761 | dynamic_ni, | 4917 | dynamic_ni, |
| 4762 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), | 4918 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), |
| 4763 | .local(elf.shndx.rela_plt.get(elf).lsi), | 4919 | .local(elf.shndx.rela_plt.get(elf).lsi), |
| 4764 | 0, | 4920 | 0, |
| 4765 | .absAddr(elf), | 4921 | .absAddr(elf), |
| 4766 | ); | 4922 | ); |
| 4767 | elf.addRelocAssumeCapacity( | 4923 | try elf.addRelocAssumeCapacity( |
| 4768 | dynamic_ni, | 4924 | dynamic_ni, |
| 4769 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1), | 4925 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1), |
| 4770 | .local(elf.shndx.got_plt.get(elf).lsi), | 4926 | .local(elf.shndx.got_plt.get(elf).lsi), |
| 4771 | 0, | 4927 | 0, |
| 4772 | .absAddr(elf), | 4928 | .absAddr(elf), |
| 4773 | ); | 4929 | ); |
| 4774 | elf.addRelocAssumeCapacity( | 4930 | try elf.addRelocAssumeCapacity( |
| 4775 | dynamic_ni, | 4931 | dynamic_ni, |
| 4776 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), | 4932 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), |
| 4777 | .local(elf.shndx.dynsym.get(elf).lsi), | 4933 | .local(elf.shndx.dynsym.get(elf).lsi), |
| 4778 | 0, | 4934 | 0, |
| 4779 | .absAddr(elf), | 4935 | .absAddr(elf), |
| 4780 | ); | 4936 | ); |
| 4781 | elf.addRelocAssumeCapacity( | 4937 | try elf.addRelocAssumeCapacity( |
| 4782 | dynamic_ni, | 4938 | dynamic_ni, |
| 4783 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), | 4939 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), |
| 4784 | .local(elf.shndx.dynstr.get(elf).lsi), | 4940 | .local(elf.shndx.dynstr.get(elf).lsi), |
| ... | @@ -4951,6 +5107,8 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) | ... | @@ -4951,6 +5107,8 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 4951 | }, | 5107 | }, |
| 4952 | } | 5108 | } |
| 4953 | } | 5109 | } |
| | 5110 | /// Although this function requires a preceding call to `ensureUnusedRelocCapacity`, it is still |
| | 5111 | /// fallible, because there are some rare cases for which we cannot reserve capacity upfront. |
| 4954 | fn addRelocAssumeCapacity( | 5112 | fn addRelocAssumeCapacity( |
| 4955 | elf: *Elf, | 5113 | elf: *Elf, |
| 4956 | node: MappedFile.Node.Index, | 5114 | node: MappedFile.Node.Index, |
| ... | @@ -4958,7 +5116,7 @@ fn addRelocAssumeCapacity( | ... | @@ -4958,7 +5116,7 @@ fn addRelocAssumeCapacity( |
| 4958 | target: Symbol.Id, | 5116 | target: Symbol.Id, |
| 4959 | addend: i64, | 5117 | addend: i64, |
| 4960 | @"type": MachineRelocType, | 5118 | @"type": MachineRelocType, |
| 4961 | ) void { | 5119 | ) !void { |
| 4962 | assert(node != .none); | 5120 | assert(node != .none); |
| 4963 | switch (elf.ehdrField(.type)) { | 5121 | switch (elf.ehdrField(.type)) { |
| 4964 | .NONE, .CORE, _ => unreachable, | 5122 | .NONE, .CORE, _ => unreachable, |
| ... | @@ -5021,25 +5179,25 @@ fn addRelocAssumeCapacity( | ... | @@ -5021,25 +5179,25 @@ fn addRelocAssumeCapacity( |
| 5021 | .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"), | 5179 | .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"), |
| 5022 | | 5180 | |
| 5023 | // Relocations targeting a symbol | 5181 | // Relocations targeting a symbol |
| 5024 | .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), | 5182 | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 5025 | .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), | 5183 | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 5026 | .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s), | 5184 | .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s), |
| 5027 | .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), | 5185 | .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), |
| 5028 | .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), | 5186 | .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), |
| 5029 | .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32), | 5187 | .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32), |
| 5030 | .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64), | 5188 | .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64), |
| 5031 | .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32), | 5189 | .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32), |
| 5032 | .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64), | 5190 | .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64), |
| 5033 | .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32), | 5191 | .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32), |
| 5034 | .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64), | 5192 | .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64), |
| 5035 | .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32), | 5193 | .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32), |
| 5036 | .GOTPC64 => { | 5194 | .GOTPC64 => { |
| 5037 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); | 5195 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 5038 | return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64); | 5196 | try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64); |
| 5039 | }, | 5197 | }, |
| 5040 | .GOTPC32 => { | 5198 | .GOTPC32 => { |
| 5041 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); | 5199 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 5042 | return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32); | 5200 | try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32); |
| 5043 | }, | 5201 | }, |
| 5044 | | 5202 | |
| 5045 | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the | 5203 | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the |
| ... | @@ -5077,11 +5235,16 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5077,11 +5235,16 @@ fn addSymbolRelocAssumeCapacity( |
| 5077 | target: Symbol.Id, | 5235 | target: Symbol.Id, |
| 5078 | addend: i64, | 5236 | addend: i64, |
| 5079 | @"type": SymbolReloc.Type, | 5237 | @"type": SymbolReloc.Type, |
| 5080 | ) void { | 5238 | ) !void { |
| 5081 | assert(elf.ehdrField(.type) != .REL); | 5239 | assert(elf.ehdrField(.type) != .REL); |
| 5082 | | 5240 | |
| 5083 | const rela_index: Section.RelaIndex.Optional = r: { | 5241 | const rela_index: Section.RelaIndex.Optional = r: { |
| 5084 | if (elf.shndx.dynamic == .UNDEF) break :r .none; | 5242 | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| | 5243 | const global_name = switch (target.unwrap()) { |
| | 5244 | .local => break :r .none, |
| | 5245 | .global => |name| name, |
| | 5246 | }; |
| | 5247 | |
| 5085 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { | 5248 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5086 | else => |machine| @panic(@tagName(machine)), | 5249 | else => |machine| @panic(@tagName(machine)), |
| 5087 | .X86_64 => .{ .X86_64 = switch (@"type") { | 5250 | .X86_64 => .{ .X86_64 = switch (@"type") { |
| ... | @@ -5101,21 +5264,31 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5101,21 +5264,31 @@ fn addSymbolRelocAssumeCapacity( |
| 5101 | .size32 => .SIZE32, | 5264 | .size32 => .SIZE32, |
| 5102 | } }, | 5265 | } }, |
| 5103 | }; | 5266 | }; |
| 5104 | const dynsym_index: u32 = switch (target.unwrap()) { | 5267 | // TODO: even if the symbol is locally defined, preemption/interposition is a |
| 5105 | .local => break :r .none, | 5268 | // possibility, which this condition does not currently consider! |
| 5106 | // TODO: even if the symbol is locally defined, preemption/interposition is a | 5269 | if (elf.globals.strong_def.contains(global_name) or |
| 5107 | // possibility, which this condition does not currently consider! | 5270 | elf.globals.weak_def.contains(global_name)) |
| 5108 | .global => |name| if (elf.globals.strong_def.contains(name) or | 5271 | { |
| 5109 | elf.globals.weak_def.contains(name)) | 5272 | break :r .none; |
| 5110 | { | 5273 | } |
| 5111 | break :r .none; | 5274 | |
| 5112 | } else elf.globalByName(name).?.dynsym_index, | 5275 | const dynsym_index = elf.globalByName(global_name).?.dynsym_index; |
| 5113 | }; | 5276 | if (dynsym_index == 0) break :r .none; |
| 5114 | | 5277 | |
| 5115 | switch (elf.nodeWantsDsoRelocation(node)) { | 5278 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 5116 | .no => break :r .none, | 5279 | .no => break :r .none, |
| 5117 | .yes => {}, | 5280 | .yes => {}, |
| 5118 | .yes_textrel => elf.textrel_count += 1, | 5281 | .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) { |
| | 5282 | // We were able to use a copy relocation on this symbol to avoid a text relocation, |
| | 5283 | // which is apparently considered a good thing despite copy relocations being an |
| | 5284 | // abomination. (This is necessary for correctness in some cases, because e.g. a |
| | 5285 | // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to |
| | 5286 | // the DSOs being loaded too far apart.) |
| | 5287 | break :r .none; |
| | 5288 | } else { |
| | 5289 | // At least for now, our only choice is a text relocation. |
| | 5290 | elf.textrel_count += 1; |
| | 5291 | }, |
| 5119 | } | 5292 | } |
| 5120 | | 5293 | |
| 5121 | // It currently looks like we need a runtime relocation for this. | 5294 | // It currently looks like we need a runtime relocation for this. |
| ... | @@ -5175,6 +5348,7 @@ fn addGotRelocAssumeCapacity( | ... | @@ -5175,6 +5348,7 @@ fn addGotRelocAssumeCapacity( |
| 5175 | .ehdr => unreachable, // cannot contain relocs | 5348 | .ehdr => unreachable, // cannot contain relocs |
| 5176 | .shdr => unreachable, // cannot contain relocs | 5349 | .shdr => unreachable, // cannot contain relocs |
| 5177 | .segment => unreachable, // cannot contain relocs | 5350 | .segment => unreachable, // cannot contain relocs |
| | 5351 | .copied_global => unreachable, // cannot contain relocs |
| 5178 | } | 5352 | } |
| 5179 | | 5353 | |
| 5180 | const gop = elf.got.getOrPutAssumeCapacity(target); | 5354 | const gop = elf.got.getOrPutAssumeCapacity(target); |
| ... | @@ -5275,29 +5449,37 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -5275,29 +5449,37 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5275 | .global => |name| name, | 5449 | .global => |name| name, |
| 5276 | }; | 5450 | }; |
| 5277 | // If the symbol is *defined* in this module, we might be able to avoid the relocation. | 5451 | // If the symbol is *defined* in this module, we might be able to avoid the relocation. |
| 5278 | if (elf.globals.strong_def.getPtr(name) orelse | 5452 | const need_reloc: bool = need_reloc: { |
| 5279 | elf.globals.weak_def.getPtr(name)) |global| | 5453 | const global = g: { |
| 5280 | { | 5454 | if (elf.globals.strong_def.getPtr(name)) |g| break :g g; |
| | 5455 | if (elf.globals.weak_def.getPtr(name)) |g| break :g g; |
| | 5456 | // The global is undefined, which probably means we need a relocation---unless |
| | 5457 | // we have created a copy relocation for it, in which case we own the canonical |
| | 5458 | // address of this symbol in this DSO! |
| | 5459 | break :need_reloc !elf.copied_globals.contains(name); |
| | 5460 | }; |
| | 5461 | |
| 5281 | // We have a definition, but it might be interposable (aka preemptible). There | 5462 | // We have a definition, but it might be interposable (aka preemptible). There |
| 5282 | // are two cases where it is not and so we can (and, in fact, must) elide the | 5463 | // are two cases where it is not and so we can (and, in fact, must) elide the |
| 5283 | // runtime relocation: | 5464 | // runtime relocation: |
| 5284 | // * We are the executable. Symbols from executables cannot be interposed. | 5465 | // * We are the executable. Symbols from executables cannot be interposed. |
| 5285 | // * The symbol's visibility disallows interposition. | 5466 | // * The symbol's visibility disallows interposition. |
| 5286 | if (elf.base.comp.config.output_mode == .Exe) { | 5467 | if (elf.base.comp.config.output_mode == .Exe) { |
| 5287 | // No relocation needed. | 5468 | break :need_reloc false; |
| 5288 | break :val .{ .unsigned = sym_id.value(elf) }; | | |
| 5289 | } | 5469 | } |
| 5290 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { | 5470 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 5291 | inline else => |sym| elf.targetLoad(&sym.other).visibility, | 5471 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 5292 | }; | 5472 | }; |
| 5293 | switch (visibility) { | 5473 | break :need_reloc switch (visibility) { |
| 5294 | .DEFAULT => {}, | 5474 | .DEFAULT => true, |
| 5295 | .INTERNAL, .HIDDEN, .PROTECTED => { | 5475 | .INTERNAL, .HIDDEN, .PROTECTED => false, |
| 5296 | // No relocation needed. | 5476 | }; |
| 5297 | break :val .{ .unsigned = sym_id.value(elf) }; | 5477 | }; |
| 5298 | }, | 5478 | |
| 5299 | } | 5479 | if (!need_reloc) { |
| | 5480 | break :val .{ .unsigned = sym_id.value(elf) }; |
| 5300 | } | 5481 | } |
| | 5482 | |
| 5301 | break :val .{ .reloc = .{ | 5483 | break :val .{ .reloc = .{ |
| 5302 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), | 5484 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), |
| 5303 | .dynsym_index = elf.globalByName(name).?.dynsym_index, | 5485 | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| ... | @@ -5403,6 +5585,80 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye | ... | @@ -5403,6 +5585,80 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye |
| 5403 | return .yes; | 5585 | return .yes; |
| 5404 | } | 5586 | } |
| 5405 | | 5587 | |
| | 5588 | /// If the given undefined global could have a copy relocation, creates that relocation if it does |
| | 5589 | /// not already exist, and returns `true`. |
| | 5590 | /// |
| | 5591 | /// Returns `false` iff a copy relocation cannot currently be created for the global. If it may be |
| | 5592 | /// possible in future, the symbol is added to `elf.want_copied_globals` so that the copy relocation |
| | 5593 | /// will be created if and when we discover a suitable definition in an input DSO. |
| | 5594 | /// |
| | 5595 | /// If this function creates a new copy relocation, it will also update relocations targeting the |
| | 5596 | /// global where needed---the caller does not need to do this. |
| | 5597 | /// |
| | 5598 | /// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global. |
| | 5599 | fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) !bool { |
| | 5600 | assert(elf.shndx.dynamic != .UNDEF); |
| | 5601 | |
| | 5602 | const gpa = elf.base.comp.gpa; |
| | 5603 | |
| | 5604 | const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse |
| | 5605 | elf.globals.weak_undef.getPtr(global_name).?; |
| | 5606 | |
| | 5607 | assert(global_ptr.dynsym_index != 0); |
| | 5608 | |
| | 5609 | // Only dynamic executables may contain `R_*_COPY` relocations. |
| | 5610 | if (elf.shndx.dynamic == .UNDEF) return false; |
| | 5611 | if (elf.base.comp.config.output_mode != .Exe) return false; |
| | 5612 | |
| | 5613 | const dso_global = elf.dso_globals.get(global_name) orelse { |
| | 5614 | // We do not have a definition to provide the correct size for the symbol. If a definition |
| | 5615 | // is discovered in a later DSO, we may at that point be able to add a copy relocation. |
| | 5616 | try elf.want_copied_globals.put(gpa, global_name, {}); |
| | 5617 | return false; |
| | 5618 | }; |
| | 5619 | |
| | 5620 | if (dso_global.type != .OBJECT) return false; |
| | 5621 | |
| | 5622 | const gop = try elf.copied_globals.getOrPut(gpa, global_name); |
| | 5623 | if (gop.found_existing) return true; |
| | 5624 | errdefer assert(elf.copied_globals.pop().?.key == global_name); |
| | 5625 | |
| | 5626 | try elf.nodes.ensureUnusedCapacity(gpa, 1); |
| | 5627 | const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{ |
| | 5628 | .size = dso_global.size, |
| | 5629 | .alignment = dso_global.alignment, |
| | 5630 | }); |
| | 5631 | errdefer comptime unreachable; |
| | 5632 | |
| | 5633 | const vaddr = elf.computeNodeVAddr(node); |
| | 5634 | elf.nodes.appendAssumeCapacity(.{ .copied_global = global_name }); |
| | 5635 | const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| | 5636 | .type = .copy(elf), |
| | 5637 | .offset = vaddr, |
| | 5638 | .raw_sym_index = global_ptr.dynsym_index, |
| | 5639 | .addend = 0, |
| | 5640 | }); |
| | 5641 | gop.value_ptr.* = .{ |
| | 5642 | .node = node, |
| | 5643 | .rela_index = rela_index, |
| | 5644 | }; |
| | 5645 | |
| | 5646 | switch (elf.symPtr(global_ptr.symtab_index)) { |
| | 5647 | inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)), |
| | 5648 | } |
| | 5649 | switch (elf.dynsymPtr(global_ptr.dynsym_index)) { |
| | 5650 | inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)), |
| | 5651 | } |
| | 5652 | |
| | 5653 | // Because we now have a copy relocation, any dynamic relocations which target this symbol are |
| | 5654 | // now incorrect, since we now own the canonical address of the symbol. So delete those relocs |
| | 5655 | // and then update the symbol's address (and re-apply relocations targeting it of course). |
| | 5656 | Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf); |
| | 5657 | Symbol.Id.global(global_name).flushMoved(elf, vaddr); |
| | 5658 | |
| | 5659 | return true; |
| | 5660 | } |
| | 5661 | |
| 5406 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 5662 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| 5407 | elf.updateNavInner(pt, nav_index) catch |err| switch (err) { | 5663 | elf.updateNavInner(pt, nav_index) catch |err| switch (err) { |
| 5408 | error.OutOfMemory, | 5664 | error.OutOfMemory, |
| ... | @@ -5561,10 +5817,8 @@ pub fn flush( | ... | @@ -5561,10 +5817,8 @@ pub fn flush( |
| 5561 | error.Canceled => |e| return e, | 5817 | error.Canceled => |e| return e, |
| 5562 | else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}), | 5818 | else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}), |
| 5563 | }; | 5819 | }; |
| 5564 | const global = elf.globalByName(sym_name_strtab) orelse break :entry 0; | 5820 | if (elf.globalByName(sym_name_strtab) == null) break :entry 0; |
| 5565 | switch (elf.symPtr(global.symtab_index)) { | 5821 | break :entry Symbol.Id.global(sym_name_strtab).value(elf); |
| 5566 | inline else => |sym| break :entry elf.targetLoad(&sym.value), | | |
| 5567 | } | | |
| 5568 | }; | 5822 | }; |
| 5569 | switch (elf.ehdrPtr()) { | 5823 | switch (elf.ehdrPtr()) { |
| 5570 | inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)), | 5824 | inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)), |
| ... | @@ -5923,15 +6177,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5923,15 +6177,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5923 | assert(first_name != .empty); | 6177 | assert(first_name != .empty); |
| 5924 | var name = first_name; | 6178 | var name = first_name; |
| 5925 | while (name != .empty) { | 6179 | while (name != .empty) { |
| 5926 | const global = elf.globalByName(name).?; | 6180 | const old_sym_addr = Symbol.Id.global(name).value(elf); |
| 5927 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { | | |
| 5928 | inline else => |sym| elf.targetLoad(&sym.value), | | |
| 5929 | }; | | |
| 5930 | Symbol.Id.global(name).flushMoved( | 6181 | Symbol.Id.global(name).flushMoved( |
| 5931 | elf, | 6182 | elf, |
| 5932 | old_sym_addr - old_addr + addr, | 6183 | old_sym_addr - old_addr + addr, |
| 5933 | ); | 6184 | ); |
| 5934 | name = global.next_in_node; | 6185 | name = elf.globalByName(name).?.next_in_node; |
| 5935 | } | 6186 | } |
| 5936 | } | 6187 | } |
| 5937 | | 6188 | |
| ... | @@ -5968,18 +6219,20 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5968,18 +6219,20 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5968 | var lsi, const end_lsi = ii.localSymbolRange(elf); | 6219 | var lsi, const end_lsi = ii.localSymbolRange(elf); |
| 5969 | while (lsi != end_lsi) : (lsi = @enumFromInt(@intFromEnum(lsi) + 1)) { | 6220 | while (lsi != end_lsi) : (lsi = @enumFromInt(@intFromEnum(lsi) + 1)) { |
| 5970 | if (lsi.index().ptr(elf).node != ni) continue; | 6221 | if (lsi.index().ptr(elf).node != ni) continue; |
| 5971 | const old_sym_addr: u64 = switch (elf.symPtr(lsi.index())) { | 6222 | const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) { |
| 5972 | inline else => |sym| switch (elf.targetLoad(&sym.other).visibility) { | 6223 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 5973 | .HIDDEN, .INTERNAL => { | | |
| 5974 | // This is actually a global symbol which got demoted to STB_LOCAL due | | |
| 5975 | // to its visibility. It will be handled in the global symbols pass | | |
| 5976 | // below; don't touch it now. | | |
| 5977 | continue; | | |
| 5978 | }, | | |
| 5979 | .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol | | |
| 5980 | .DEFAULT => elf.targetLoad(&sym.value), | | |
| 5981 | }, | | |
| 5982 | }; | 6224 | }; |
| | 6225 | switch (visibility) { |
| | 6226 | .HIDDEN, .INTERNAL => { |
| | 6227 | // This is actually a global symbol which got demoted to STB_LOCAL due |
| | 6228 | // to its visibility. It will be handled in the global symbols pass |
| | 6229 | // below; don't touch it now. |
| | 6230 | continue; |
| | 6231 | }, |
| | 6232 | .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol |
| | 6233 | .DEFAULT => {}, |
| | 6234 | } |
| | 6235 | const old_sym_addr = Symbol.Id.local(lsi).value(elf); |
| 5983 | Symbol.Id.local(lsi).flushMoved( | 6236 | Symbol.Id.local(lsi).flushMoved( |
| 5984 | elf, | 6237 | elf, |
| 5985 | old_sym_addr - old_section_addr + new_section_addr, | 6238 | old_sym_addr - old_section_addr + new_section_addr, |
| ... | @@ -5991,15 +6244,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5991,15 +6244,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5991 | assert(first_name != .empty); | 6244 | assert(first_name != .empty); |
| 5992 | var name = first_name; | 6245 | var name = first_name; |
| 5993 | while (name != .empty) { | 6246 | while (name != .empty) { |
| 5994 | const global = elf.globalByName(name).?; | 6247 | const old_sym_addr = Symbol.Id.global(name).value(elf); |
| 5995 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { | | |
| 5996 | inline else => |sym| elf.targetLoad(&sym.value), | | |
| 5997 | }; | | |
| 5998 | Symbol.Id.global(name).flushMoved( | 6248 | Symbol.Id.global(name).flushMoved( |
| 5999 | elf, | 6249 | elf, |
| 6000 | old_sym_addr - old_section_addr + new_section_addr, | 6250 | old_sym_addr - old_section_addr + new_section_addr, |
| 6001 | ); | 6251 | ); |
| 6002 | name = global.next_in_node; | 6252 | name = elf.globalByName(name).?.next_in_node; |
| 6003 | } | 6253 | } |
| 6004 | } | 6254 | } |
| 6005 | | 6255 | |
| ... | @@ -6010,6 +6260,19 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -6010,6 +6260,19 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 6010 | isi.ptrConst(elf).first_got_reloc, | 6260 | isi.ptrConst(elf).first_got_reloc, |
| 6011 | ); | 6261 | ); |
| 6012 | }, | 6262 | }, |
| | 6263 | .copied_global => |global_name| { |
| | 6264 | const copied_global = elf.copied_globals.getPtr(global_name) orelse { |
| | 6265 | // TODO: this node is orphaned, which is possible because `MappedFile` does not yet |
| | 6266 | // support deleting nodes. See logic in `setGlobalSymbolValue`. |
| | 6267 | return; |
| | 6268 | }; |
| | 6269 | assert(copied_global.node == ni); |
| | 6270 | |
| | 6271 | const new_addr = elf.computeNodeVAddr(ni); |
| | 6272 | elf.shndx.rela_dyn.relaSetOffset(elf, copied_global.rela_index, new_addr); |
| | 6273 | |
| | 6274 | Symbol.Id.global(global_name).flushMoved(elf, new_addr); |
| | 6275 | }, |
| 6013 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { | 6276 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { |
| 6014 | const new_addr = elf.computeNodeVAddr(ni); | 6277 | const new_addr = elf.computeNodeVAddr(ni); |
| 6015 | Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr); | 6278 | Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr); |
| ... | @@ -6137,7 +6400,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -6137,7 +6400,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 6137 | } | 6400 | } |
| 6138 | }, | 6401 | }, |
| 6139 | }, | 6402 | }, |
| 6140 | .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {}, | 6403 | .copied_global, .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {}, |
| 6141 | } | 6404 | } |
| 6142 | } | 6405 | } |
| 6143 | fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void { | 6406 | fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void { |
| ... | @@ -6262,9 +6525,9 @@ fn updateExportsInner( | ... | @@ -6262,9 +6525,9 @@ fn updateExportsInner( |
| 6262 | .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT }, | 6525 | .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT }, |
| 6263 | }; | 6526 | }; |
| 6264 | while (try elf.idle(pt.tid)) {} | 6527 | while (try elf.idle(pt.tid)) {} |
| 6265 | const value: u64, const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) { | 6528 | const value: u64 = Symbol.Id.local(exported_lsi).value(elf); |
| | 6529 | const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) { |
| 6266 | inline else => |exported_sym| .{ | 6530 | inline else => |exported_sym| .{ |
| 6267 | elf.targetLoad(&exported_sym.value), | | |
| 6268 | elf.targetLoad(&exported_sym.size), | 6531 | elf.targetLoad(&exported_sym.size), |
| 6269 | .fromSection(elf.targetLoad(&exported_sym.shndx)), | 6532 | .fromSection(elf.targetLoad(&exported_sym.shndx)), |
| 6270 | }, | 6533 | }, |
| ... | @@ -6368,6 +6631,7 @@ pub fn printNode( | ... | @@ -6368,6 +6631,7 @@ pub fn printNode( |
| 6368 | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), | 6631 | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), |
| 6369 | }); | 6632 | }); |
| 6370 | }, | 6633 | }, |
| | 6634 | .copied_global => |name| try w.print("(copy:{s})", .{name}), |
| 6371 | .nav => |nmi| { | 6635 | .nav => |nmi| { |
| 6372 | const zcu = elf.base.comp.zcu.?; | 6636 | const zcu = elf.base.comp.zcu.?; |
| 6373 | const ip = &zcu.intern_pool; | 6637 | const ip = &zcu.intern_pool; |