| ... | ... | @@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig"); |
| 11 | 11 | const aarch64 = @import("../codegen/aarch64.zig"); |
| 12 | 12 | const math = std.math; |
| 13 | 13 | const mem = std.mem; |
| 14 | const meta = std.meta; |
| 14 | 15 | |
| 16 | const bind = @import("MachO/bind.zig"); |
| 15 | 17 | const trace = @import("../tracy.zig").trace; |
| 16 | 18 | const build_options = @import("build_options"); |
| 17 | 19 | const Module = @import("../Module.zig"); |
| ... | ... | @@ -26,7 +28,6 @@ const Trie = @import("MachO/Trie.zig"); |
| 26 | 28 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 27 | 29 | |
| 28 | 30 | usingnamespace @import("MachO/commands.zig"); |
| 29 | | usingnamespace @import("MachO/imports.zig"); |
| 30 | 31 | |
| 31 | 32 | pub const base_tag: File.Tag = File.Tag.macho; |
| 32 | 33 | |
| ... | ... | @@ -87,14 +88,12 @@ code_signature_cmd_index: ?u16 = null, |
| 87 | 88 | |
| 88 | 89 | /// Index into __TEXT,__text section. |
| 89 | 90 | text_section_index: ?u16 = null, |
| 90 | | /// Index into __TEXT,__ziggot section. |
| 91 | | got_section_index: ?u16 = null, |
| 92 | 91 | /// Index into __TEXT,__stubs section. |
| 93 | 92 | stubs_section_index: ?u16 = null, |
| 94 | 93 | /// Index into __TEXT,__stub_helper section. |
| 95 | 94 | stub_helper_section_index: ?u16 = null, |
| 96 | 95 | /// Index into __DATA_CONST,__got section. |
| 97 | | data_got_section_index: ?u16 = null, |
| 96 | got_section_index: ?u16 = null, |
| 98 | 97 | /// Index into __DATA,__la_symbol_ptr section. |
| 99 | 98 | la_symbol_ptr_section_index: ?u16 = null, |
| 100 | 99 | /// Index into __DATA,__data section. |
| ... | ... | @@ -104,16 +103,16 @@ entry_addr: ?u64 = null, |
| 104 | 103 | |
| 105 | 104 | /// Table of all local symbols |
| 106 | 105 | /// Internally references string table for names (which are optional). |
| 107 | | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 106 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 108 | 107 | /// Table of all global symbols |
| 109 | | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 108 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 110 | 109 | /// Table of all extern nonlazy symbols, indexed by name. |
| 111 | | extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, |
| 110 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 112 | 111 | /// Table of all extern lazy symbols, indexed by name. |
| 113 | | extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, |
| 112 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 114 | 113 | |
| 115 | | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 116 | | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 114 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 115 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 117 | 116 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 118 | 117 | |
| 119 | 118 | stub_helper_stubs_start_off: ?u64 = null, |
| ... | ... | @@ -122,8 +121,8 @@ stub_helper_stubs_start_off: ?u64 = null, |
| 122 | 121 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 123 | 122 | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, |
| 124 | 123 | |
| 125 | | /// Table of trampolines to the actual symbols in __text section. |
| 126 | | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 124 | /// Table of GOT entries. |
| 125 | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, |
| 127 | 126 | |
| 128 | 127 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 129 | 128 | |
| ... | ... | @@ -154,14 +153,19 @@ string_table_needs_relocation: bool = false, |
| 154 | 153 | /// allocate a fresh text block, which will have ideal capacity, and then grow it |
| 155 | 154 | /// by 1 byte. It will then have -1 overcapacity. |
| 156 | 155 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 156 | |
| 157 | 157 | /// Pointer to the last allocated text block |
| 158 | 158 | last_text_block: ?*TextBlock = null, |
| 159 | |
| 159 | 160 | /// A list of all PIE fixups required for this run of the linker. |
| 160 | 161 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| 161 | 162 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| 162 | 163 | /// prior to calling `generateSymbol`, and then immediately deallocated |
| 163 | 164 | /// rather than sitting in the global scope. |
| 164 | | pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, |
| 165 | /// TODO We should also rewrite this using generic relocations common to all |
| 166 | /// backends. |
| 167 | pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{}, |
| 168 | |
| 165 | 169 | /// A list of all stub (extern decls) fixups required for this run of the linker. |
| 166 | 170 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| 167 | 171 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| ... | ... | @@ -169,14 +173,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, |
| 169 | 173 | /// rather than sitting in the global scope. |
| 170 | 174 | stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, |
| 171 | 175 | |
| 172 | | pub const PieFixup = struct { |
| 173 | | /// Target address we wanted to address in absolute terms. |
| 174 | | address: u64, |
| 175 | | /// Where in the byte stream we should perform the fixup. |
| 176 | | start: usize, |
| 177 | | /// The length of the byte stream. For x86_64, this will be |
| 178 | | /// variable. For aarch64, it will be fixed at 4 bytes. |
| 179 | | len: usize, |
| 176 | pub const GOTEntry = struct { |
| 177 | /// GOT entry can either be a local pointer or an extern (nonlazy) import. |
| 178 | kind: enum { |
| 179 | Local, |
| 180 | Extern, |
| 181 | }, |
| 182 | |
| 183 | /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports. |
| 184 | /// TODO I'm more and more inclined to just manage a single, max two symbol tables |
| 185 | /// rather than 4 as we currently do, but I'll follow up in the future PR. |
| 186 | symbol: u32, |
| 187 | |
| 188 | /// Index of this entry in the GOT. |
| 189 | index: u32, |
| 190 | }; |
| 191 | |
| 192 | pub const Import = struct { |
| 193 | /// MachO symbol table entry. |
| 194 | symbol: macho.nlist_64, |
| 195 | |
| 196 | /// Id of the dynamic library where the specified entries can be found. |
| 197 | dylib_ordinal: i64, |
| 198 | |
| 199 | /// Index of this import within the import list. |
| 200 | index: u32, |
| 201 | }; |
| 202 | |
| 203 | pub const PIEFixup = struct { |
| 204 | /// Target VM address of this relocation. |
| 205 | target_addr: u64, |
| 206 | |
| 207 | /// Offset within the byte stream. |
| 208 | offset: usize, |
| 209 | |
| 210 | /// Size of the relocation. |
| 211 | size: usize, |
| 180 | 212 | }; |
| 181 | 213 | |
| 182 | 214 | pub const StubFixup = struct { |
| ... | ... | @@ -260,9 +292,9 @@ pub const TextBlock = struct { |
| 260 | 292 | /// File offset relocation happens transparently, so it is not included in |
| 261 | 293 | /// this calculation. |
| 262 | 294 | fn capacity(self: TextBlock, macho_file: MachO) u64 { |
| 263 | | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; |
| 295 | const self_sym = macho_file.locals.items[self.local_sym_index]; |
| 264 | 296 | if (self.next) |next| { |
| 265 | | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; |
| 297 | const next_sym = macho_file.locals.items[next.local_sym_index]; |
| 266 | 298 | return next_sym.n_value - self_sym.n_value; |
| 267 | 299 | } else { |
| 268 | 300 | // We are the last block. |
| ... | ... | @@ -274,8 +306,8 @@ pub const TextBlock = struct { |
| 274 | 306 | fn freeListEligible(self: TextBlock, macho_file: MachO) bool { |
| 275 | 307 | // No need to keep a free list node for the last block. |
| 276 | 308 | const next = self.next orelse return false; |
| 277 | | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; |
| 278 | | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; |
| 309 | const self_sym = macho_file.locals.items[self.local_sym_index]; |
| 310 | const next_sym = macho_file.locals.items[next.local_sym_index]; |
| 279 | 311 | const cap = next_sym.n_value - self_sym.n_value; |
| 280 | 312 | const ideal_cap = padToIdeal(self.size); |
| 281 | 313 | if (cap <= ideal_cap) return false; |
| ... | ... | @@ -344,7 +376,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 344 | 376 | }; |
| 345 | 377 | |
| 346 | 378 | // Index 0 is always a null symbol. |
| 347 | | try self.local_symbols.append(allocator, .{ |
| 379 | try self.locals.append(allocator, .{ |
| 348 | 380 | .n_strx = 0, |
| 349 | 381 | .n_type = 0, |
| 350 | 382 | .n_sect = 0, |
| ... | ... | @@ -834,7 +866,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 834 | 866 | } |
| 835 | 867 | }, |
| 836 | 868 | else => { |
| 837 | | log.err("{s} terminated", .{ argv.items[0] }); |
| 869 | log.err("{s} terminated", .{argv.items[0]}); |
| 838 | 870 | return error.LLDCrashed; |
| 839 | 871 | }, |
| 840 | 872 | } |
| ... | ... | @@ -1019,14 +1051,14 @@ pub fn deinit(self: *MachO) void { |
| 1019 | 1051 | if (self.d_sym) |*ds| { |
| 1020 | 1052 | ds.deinit(self.base.allocator); |
| 1021 | 1053 | } |
| 1022 | | for (self.extern_lazy_symbols.items()) |*entry| { |
| 1054 | for (self.lazy_imports.items()) |*entry| { |
| 1023 | 1055 | self.base.allocator.free(entry.key); |
| 1024 | 1056 | } |
| 1025 | | self.extern_lazy_symbols.deinit(self.base.allocator); |
| 1026 | | for (self.extern_nonlazy_symbols.items()) |*entry| { |
| 1057 | self.lazy_imports.deinit(self.base.allocator); |
| 1058 | for (self.nonlazy_imports.items()) |*entry| { |
| 1027 | 1059 | self.base.allocator.free(entry.key); |
| 1028 | 1060 | } |
| 1029 | | self.extern_nonlazy_symbols.deinit(self.base.allocator); |
| 1061 | self.nonlazy_imports.deinit(self.base.allocator); |
| 1030 | 1062 | self.pie_fixups.deinit(self.base.allocator); |
| 1031 | 1063 | self.stub_fixups.deinit(self.base.allocator); |
| 1032 | 1064 | self.text_block_free_list.deinit(self.base.allocator); |
| ... | ... | @@ -1040,10 +1072,10 @@ pub fn deinit(self: *MachO) void { |
| 1040 | 1072 | } |
| 1041 | 1073 | self.string_table_directory.deinit(self.base.allocator); |
| 1042 | 1074 | self.string_table.deinit(self.base.allocator); |
| 1043 | | self.global_symbols.deinit(self.base.allocator); |
| 1044 | | self.global_symbol_free_list.deinit(self.base.allocator); |
| 1045 | | self.local_symbols.deinit(self.base.allocator); |
| 1046 | | self.local_symbol_free_list.deinit(self.base.allocator); |
| 1075 | self.globals.deinit(self.base.allocator); |
| 1076 | self.globals_free_list.deinit(self.base.allocator); |
| 1077 | self.locals.deinit(self.base.allocator); |
| 1078 | self.locals_free_list.deinit(self.base.allocator); |
| 1047 | 1079 | for (self.load_commands.items) |*lc| { |
| 1048 | 1080 | lc.deinit(self.base.allocator); |
| 1049 | 1081 | } |
| ... | ... | @@ -1098,7 +1130,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo |
| 1098 | 1130 | } |
| 1099 | 1131 | |
| 1100 | 1132 | fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 1101 | | const sym = self.local_symbols.items[text_block.local_sym_index]; |
| 1133 | const sym = self.locals.items[text_block.local_sym_index]; |
| 1102 | 1134 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; |
| 1103 | 1135 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); |
| 1104 | 1136 | if (!need_realloc) return sym.n_value; |
| ... | ... | @@ -1108,34 +1140,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig |
| 1108 | 1140 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1109 | 1141 | if (decl.link.macho.local_sym_index != 0) return; |
| 1110 | 1142 | |
| 1111 | | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); |
| 1143 | try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1); |
| 1112 | 1144 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 1113 | 1145 | |
| 1114 | | if (self.local_symbol_free_list.popOrNull()) |i| { |
| 1146 | if (self.locals_free_list.popOrNull()) |i| { |
| 1115 | 1147 | log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); |
| 1116 | 1148 | decl.link.macho.local_sym_index = i; |
| 1117 | 1149 | } else { |
| 1118 | | log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name }); |
| 1119 | | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| 1120 | | _ = self.local_symbols.addOneAssumeCapacity(); |
| 1150 | log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name }); |
| 1151 | decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len); |
| 1152 | _ = self.locals.addOneAssumeCapacity(); |
| 1121 | 1153 | } |
| 1122 | 1154 | |
| 1123 | 1155 | if (self.offset_table_free_list.popOrNull()) |i| { |
| 1156 | log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name }); |
| 1124 | 1157 | decl.link.macho.offset_table_index = i; |
| 1125 | 1158 | } else { |
| 1159 | log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name }); |
| 1126 | 1160 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 1127 | 1161 | _ = self.offset_table.addOneAssumeCapacity(); |
| 1128 | 1162 | self.offset_table_count_dirty = true; |
| 1163 | self.rebase_info_dirty = true; |
| 1129 | 1164 | } |
| 1130 | 1165 | |
| 1131 | | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ |
| 1166 | self.locals.items[decl.link.macho.local_sym_index] = .{ |
| 1132 | 1167 | .n_strx = 0, |
| 1133 | 1168 | .n_type = 0, |
| 1134 | 1169 | .n_sect = 0, |
| 1135 | 1170 | .n_desc = 0, |
| 1136 | 1171 | .n_value = 0, |
| 1137 | 1172 | }; |
| 1138 | | self.offset_table.items[decl.link.macho.offset_table_index] = 0; |
| 1173 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ |
| 1174 | .kind = .Local, |
| 1175 | .symbol = decl.link.macho.local_sym_index, |
| 1176 | .index = decl.link.macho.offset_table_index, |
| 1177 | }; |
| 1139 | 1178 | } |
| 1140 | 1179 | |
| 1141 | 1180 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -1178,8 +1217,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1178 | 1217 | .externally_managed => |x| x, |
| 1179 | 1218 | .appended => code_buffer.items, |
| 1180 | 1219 | .fail => |em| { |
| 1181 | | // Clear any PIE fixups and stub fixups for this decl. |
| 1220 | // Clear any PIE fixups for this decl. |
| 1182 | 1221 | self.pie_fixups.shrinkRetainingCapacity(0); |
| 1222 | // Clear any stub fixups for this decl. |
| 1183 | 1223 | self.stub_fixups.shrinkRetainingCapacity(0); |
| 1184 | 1224 | decl.analysis = .codegen_failure; |
| 1185 | 1225 | try module.failed_decls.put(module.gpa, decl, em); |
| ... | ... | @@ -1189,7 +1229,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1189 | 1229 | |
| 1190 | 1230 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 1191 | 1231 | assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes() |
| 1192 | | const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 1232 | const symbol = &self.locals.items[decl.link.macho.local_sym_index]; |
| 1193 | 1233 | |
| 1194 | 1234 | if (decl.link.macho.size != 0) { |
| 1195 | 1235 | const capacity = decl.link.macho.capacity(self.*); |
| ... | ... | @@ -1198,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1198 | 1238 | const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); |
| 1199 | 1239 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); |
| 1200 | 1240 | if (vaddr != symbol.n_value) { |
| 1201 | | symbol.n_value = vaddr; |
| 1202 | 1241 | log.debug(" (writing new offset table entry)", .{}); |
| 1203 | | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; |
| 1242 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ |
| 1243 | .kind = .Local, |
| 1244 | .symbol = decl.link.macho.local_sym_index, |
| 1245 | .index = decl.link.macho.offset_table_index, |
| 1246 | }; |
| 1204 | 1247 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1205 | 1248 | } |
| 1206 | 1249 | } else if (code.len < decl.link.macho.size) { |
| ... | ... | @@ -1229,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1229 | 1272 | .n_desc = 0, |
| 1230 | 1273 | .n_value = addr, |
| 1231 | 1274 | }; |
| 1232 | | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| 1275 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ |
| 1276 | .kind = .Local, |
| 1277 | .symbol = decl.link.macho.local_sym_index, |
| 1278 | .index = decl.link.macho.offset_table_index, |
| 1279 | }; |
| 1233 | 1280 | |
| 1234 | 1281 | try self.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1235 | 1282 | if (self.d_sym) |*ds| |
| ... | ... | @@ -1237,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1237 | 1284 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1238 | 1285 | } |
| 1239 | 1286 | |
| 1240 | | // Perform PIE fixups (if any) |
| 1241 | | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1242 | | const got_section = text_segment.sections.items[self.got_section_index.?]; |
| 1287 | // Calculate displacements to target addr (if any). |
| 1243 | 1288 | while (self.pie_fixups.popOrNull()) |fixup| { |
| 1244 | | const target_addr = fixup.address; |
| 1245 | | const this_addr = symbol.n_value + fixup.start; |
| 1289 | assert(fixup.size == 4); |
| 1290 | const this_addr = symbol.n_value + fixup.offset; |
| 1291 | const target_addr = fixup.target_addr; |
| 1292 | |
| 1246 | 1293 | switch (self.base.options.target.cpu.arch) { |
| 1247 | 1294 | .x86_64 => { |
| 1248 | | assert(target_addr >= this_addr + fixup.len); |
| 1249 | | const displacement = try math.cast(u32, target_addr - this_addr - fixup.len); |
| 1250 | | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| 1251 | | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| 1295 | const displacement = try math.cast(u32, target_addr - this_addr - 4); |
| 1296 | mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement); |
| 1252 | 1297 | }, |
| 1253 | 1298 | .aarch64 => { |
| 1254 | | assert(target_addr >= this_addr); |
| 1255 | | const displacement = try math.cast(u27, target_addr - this_addr); |
| 1256 | | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; |
| 1257 | | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32()); |
| 1299 | // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible). |
| 1300 | { |
| 1301 | const inst = code_buffer.items[fixup.offset..][0..4]; |
| 1302 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 1303 | aarch64.Instruction, |
| 1304 | aarch64.Instruction.PCRelativeAddress, |
| 1305 | ), inst); |
| 1306 | const this_page = @intCast(i32, this_addr >> 12); |
| 1307 | const target_page = @intCast(i32, target_addr >> 12); |
| 1308 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); |
| 1309 | parsed.immhi = @truncate(u19, pages >> 2); |
| 1310 | parsed.immlo = @truncate(u2, pages); |
| 1311 | } |
| 1312 | { |
| 1313 | const inst = code_buffer.items[fixup.offset + 4 ..][0..4]; |
| 1314 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 1315 | aarch64.Instruction, |
| 1316 | aarch64.Instruction.LoadStoreRegister, |
| 1317 | ), inst); |
| 1318 | const narrowed = @truncate(u12, target_addr); |
| 1319 | const offset = try math.divExact(u12, narrowed, 8); |
| 1320 | parsed.offset = offset; |
| 1321 | } |
| 1258 | 1322 | }, |
| 1259 | 1323 | else => unreachable, // unsupported target architecture |
| 1260 | 1324 | } |
| 1261 | 1325 | } |
| 1262 | 1326 | |
| 1263 | 1327 | // Resolve stubs (if any) |
| 1328 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1264 | 1329 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| 1265 | 1330 | for (self.stub_fixups.items) |fixup| { |
| 1266 | 1331 | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; |
| ... | ... | @@ -1285,9 +1350,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1285 | 1350 | try self.writeStubInStubHelper(fixup.symbol); |
| 1286 | 1351 | try self.writeLazySymbolPointer(fixup.symbol); |
| 1287 | 1352 | |
| 1288 | | const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value; |
| 1289 | | extern_sym.segment = self.data_segment_cmd_index.?; |
| 1290 | | extern_sym.offset = fixup.symbol * @sizeOf(u64); |
| 1291 | 1353 | self.rebase_info_dirty = true; |
| 1292 | 1354 | self.lazy_binding_info_dirty = true; |
| 1293 | 1355 | } |
| ... | ... | @@ -1329,9 +1391,9 @@ pub fn updateDeclExports( |
| 1329 | 1391 | const tracy = trace(@src()); |
| 1330 | 1392 | defer tracy.end(); |
| 1331 | 1393 | |
| 1332 | | try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len); |
| 1394 | try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len); |
| 1333 | 1395 | if (decl.link.macho.local_sym_index == 0) return; |
| 1334 | | const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 1396 | const decl_sym = &self.locals.items[decl.link.macho.local_sym_index]; |
| 1335 | 1397 | |
| 1336 | 1398 | for (exports) |exp| { |
| 1337 | 1399 | if (exp.options.section) |section_name| { |
| ... | ... | @@ -1364,7 +1426,7 @@ pub fn updateDeclExports( |
| 1364 | 1426 | }; |
| 1365 | 1427 | const n_type = decl_sym.n_type | macho.N_EXT; |
| 1366 | 1428 | if (exp.link.macho.sym_index) |i| { |
| 1367 | | const sym = &self.global_symbols.items[i]; |
| 1429 | const sym = &self.globals.items[i]; |
| 1368 | 1430 | sym.* = .{ |
| 1369 | 1431 | .n_strx = try self.updateString(sym.n_strx, exp.options.name), |
| 1370 | 1432 | .n_type = n_type, |
| ... | ... | @@ -1374,12 +1436,12 @@ pub fn updateDeclExports( |
| 1374 | 1436 | }; |
| 1375 | 1437 | } else { |
| 1376 | 1438 | const name_str_index = try self.makeString(exp.options.name); |
| 1377 | | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { |
| 1378 | | _ = self.global_symbols.addOneAssumeCapacity(); |
| 1439 | const i = if (self.globals_free_list.popOrNull()) |i| i else blk: { |
| 1440 | _ = self.globals.addOneAssumeCapacity(); |
| 1379 | 1441 | self.export_info_dirty = true; |
| 1380 | | break :blk self.global_symbols.items.len - 1; |
| 1442 | break :blk self.globals.items.len - 1; |
| 1381 | 1443 | }; |
| 1382 | | self.global_symbols.items[i] = .{ |
| 1444 | self.globals.items[i] = .{ |
| 1383 | 1445 | .n_strx = name_str_index, |
| 1384 | 1446 | .n_type = n_type, |
| 1385 | 1447 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| ... | ... | @@ -1394,18 +1456,18 @@ pub fn updateDeclExports( |
| 1394 | 1456 | |
| 1395 | 1457 | pub fn deleteExport(self: *MachO, exp: Export) void { |
| 1396 | 1458 | const sym_index = exp.sym_index orelse return; |
| 1397 | | self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; |
| 1398 | | self.global_symbols.items[sym_index].n_type = 0; |
| 1459 | self.globals_free_list.append(self.base.allocator, sym_index) catch {}; |
| 1460 | self.globals.items[sym_index].n_type = 0; |
| 1399 | 1461 | } |
| 1400 | 1462 | |
| 1401 | 1463 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 1402 | 1464 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 1403 | 1465 | self.freeTextBlock(&decl.link.macho); |
| 1404 | 1466 | if (decl.link.macho.local_sym_index != 0) { |
| 1405 | | self.local_symbol_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; |
| 1467 | self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; |
| 1406 | 1468 | self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {}; |
| 1407 | 1469 | |
| 1408 | | self.local_symbols.items[decl.link.macho.local_sym_index].n_type = 0; |
| 1470 | self.locals.items[decl.link.macho.local_sym_index].n_type = 0; |
| 1409 | 1471 | |
| 1410 | 1472 | decl.link.macho.local_sym_index = 0; |
| 1411 | 1473 | } |
| ... | ... | @@ -1413,7 +1475,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 1413 | 1475 | |
| 1414 | 1476 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 1415 | 1477 | assert(decl.link.macho.local_sym_index != 0); |
| 1416 | | return self.local_symbols.items[decl.link.macho.local_sym_index].n_value; |
| 1478 | return self.locals.items[decl.link.macho.local_sym_index].n_value; |
| 1417 | 1479 | } |
| 1418 | 1480 | |
| 1419 | 1481 | pub fn populateMissingMetadata(self: *MachO) !void { |
| ... | ... | @@ -1553,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1553 | 1615 | self.header_dirty = true; |
| 1554 | 1616 | self.load_commands_dirty = true; |
| 1555 | 1617 | } |
| 1556 | | if (self.got_section_index == null) { |
| 1557 | | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1558 | | self.got_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1559 | | |
| 1560 | | const alignment: u2 = switch (self.base.options.target.cpu.arch) { |
| 1561 | | .x86_64 => 0, |
| 1562 | | .aarch64 => 2, |
| 1563 | | else => unreachable, // unhandled architecture type |
| 1564 | | }; |
| 1565 | | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1566 | | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1567 | | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); |
| 1568 | | assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. |
| 1569 | | |
| 1570 | | log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1571 | | |
| 1572 | | try text_segment.addSection(self.base.allocator, .{ |
| 1573 | | .sectname = makeStaticString("__ziggot"), |
| 1574 | | .segname = makeStaticString("__TEXT"), |
| 1575 | | .addr = text_segment.inner.vmaddr + off, |
| 1576 | | .size = needed_size, |
| 1577 | | .offset = @intCast(u32, off), |
| 1578 | | .@"align" = alignment, |
| 1579 | | .reloff = 0, |
| 1580 | | .nreloc = 0, |
| 1581 | | .flags = flags, |
| 1582 | | .reserved1 = 0, |
| 1583 | | .reserved2 = 0, |
| 1584 | | .reserved3 = 0, |
| 1585 | | }); |
| 1586 | | self.header_dirty = true; |
| 1587 | | self.load_commands_dirty = true; |
| 1588 | | } |
| 1589 | 1618 | if (self.stubs_section_index == null) { |
| 1590 | 1619 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1591 | 1620 | self.stubs_section_index = @intCast(u16, text_segment.sections.items.len); |
| ... | ... | @@ -1597,7 +1626,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1597 | 1626 | }; |
| 1598 | 1627 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 1599 | 1628 | .x86_64 => 6, |
| 1600 | | .aarch64 => 2 * @sizeOf(u32), |
| 1629 | .aarch64 => 3 * @sizeOf(u32), |
| 1601 | 1630 | else => unreachable, // unhandled architecture type |
| 1602 | 1631 | }; |
| 1603 | 1632 | const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| ... | ... | @@ -1686,9 +1715,9 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1686 | 1715 | self.header_dirty = true; |
| 1687 | 1716 | self.load_commands_dirty = true; |
| 1688 | 1717 | } |
| 1689 | | if (self.data_got_section_index == null) { |
| 1718 | if (self.got_section_index == null) { |
| 1690 | 1719 | const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 1691 | | self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len); |
| 1720 | self.got_section_index = @intCast(u16, dc_segment.sections.items.len); |
| 1692 | 1721 | |
| 1693 | 1722 | const flags = macho.S_NON_LAZY_SYMBOL_POINTERS; |
| 1694 | 1723 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| ... | ... | @@ -2060,12 +2089,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2060 | 2089 | self.header_dirty = true; |
| 2061 | 2090 | self.load_commands_dirty = true; |
| 2062 | 2091 | } |
| 2063 | | if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) { |
| 2064 | | const index = @intCast(u32, self.extern_nonlazy_symbols.items().len); |
| 2092 | if (!self.nonlazy_imports.contains("dyld_stub_binder")) { |
| 2093 | const index = @intCast(u32, self.nonlazy_imports.items().len); |
| 2065 | 2094 | const name = try self.base.allocator.dupe(u8, "dyld_stub_binder"); |
| 2066 | 2095 | const offset = try self.makeString("dyld_stub_binder"); |
| 2067 | | try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{ |
| 2068 | | .inner = .{ |
| 2096 | try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{ |
| 2097 | .symbol = .{ |
| 2069 | 2098 | .n_strx = offset, |
| 2070 | 2099 | .n_type = std.macho.N_UNDF | std.macho.N_EXT, |
| 2071 | 2100 | .n_sect = 0, |
| ... | ... | @@ -2073,68 +2102,19 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2073 | 2102 | .n_value = 0, |
| 2074 | 2103 | }, |
| 2075 | 2104 | .dylib_ordinal = 1, // TODO this is currently hardcoded. |
| 2076 | | .segment = self.data_const_segment_cmd_index.?, |
| 2077 | | .offset = index * @sizeOf(u64), |
| 2105 | .index = index, |
| 2078 | 2106 | }); |
| 2107 | const off_index = @intCast(u32, self.offset_table.items.len); |
| 2108 | try self.offset_table.append(self.base.allocator, .{ |
| 2109 | .kind = .Extern, |
| 2110 | .symbol = index, |
| 2111 | .index = off_index, |
| 2112 | }); |
| 2113 | try self.writeOffsetTableEntry(off_index); |
| 2079 | 2114 | self.binding_info_dirty = true; |
| 2080 | 2115 | } |
| 2081 | 2116 | if (self.stub_helper_stubs_start_off == null) { |
| 2082 | | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2083 | | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; |
| 2084 | | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2085 | | const data = &data_segment.sections.items[self.data_section_index.?]; |
| 2086 | | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2087 | | const got = &data_const_segment.sections.items[self.data_got_section_index.?]; |
| 2088 | | switch (self.base.options.target.cpu.arch) { |
| 2089 | | .x86_64 => { |
| 2090 | | const code_size = 15; |
| 2091 | | var code: [code_size]u8 = undefined; |
| 2092 | | // lea %r11, [rip + disp] |
| 2093 | | code[0] = 0x4c; |
| 2094 | | code[1] = 0x8d; |
| 2095 | | code[2] = 0x1d; |
| 2096 | | { |
| 2097 | | const displacement = try math.cast(u32, data.addr - stub_helper.addr - 7); |
| 2098 | | mem.writeIntLittle(u32, code[3..7], displacement); |
| 2099 | | } |
| 2100 | | // push %r11 |
| 2101 | | code[7] = 0x41; |
| 2102 | | code[8] = 0x53; |
| 2103 | | // jmp [rip + disp] |
| 2104 | | code[9] = 0xff; |
| 2105 | | code[10] = 0x25; |
| 2106 | | { |
| 2107 | | const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size); |
| 2108 | | mem.writeIntLittle(u32, code[11..], displacement); |
| 2109 | | } |
| 2110 | | self.stub_helper_stubs_start_off = stub_helper.offset + code_size; |
| 2111 | | try self.base.file.?.pwriteAll(&code, stub_helper.offset); |
| 2112 | | }, |
| 2113 | | .aarch64 => { |
| 2114 | | var code: [4 * @sizeOf(u32)]u8 = undefined; |
| 2115 | | { |
| 2116 | | const displacement = try math.cast(i21, data.addr - stub_helper.addr); |
| 2117 | | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); |
| 2118 | | } |
| 2119 | | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( |
| 2120 | | .x16, |
| 2121 | | .x17, |
| 2122 | | aarch64.Register.sp, |
| 2123 | | aarch64.Instruction.LoadStorePairOffset.pre_index(-16), |
| 2124 | | ).toU32()); |
| 2125 | | { |
| 2126 | | const displacement = try math.divExact(u64, got.addr - stub_helper.addr - 2 * @sizeOf(u32), 4); |
| 2127 | | const literal = try math.cast(u19, displacement); |
| 2128 | | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ |
| 2129 | | .literal = literal, |
| 2130 | | }).toU32()); |
| 2131 | | } |
| 2132 | | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); |
| 2133 | | self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32); |
| 2134 | | try self.base.file.?.pwriteAll(&code, stub_helper.offset); |
| 2135 | | }, |
| 2136 | | else => unreachable, |
| 2137 | | } |
| 2117 | try self.writeStubHelperPreamble(); |
| 2138 | 2118 | } |
| 2139 | 2119 | } |
| 2140 | 2120 | |
| ... | ... | @@ -2159,7 +2139,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2159 | 2139 | const big_block = self.text_block_free_list.items[i]; |
| 2160 | 2140 | // We now have a pointer to a live text block that has too much capacity. |
| 2161 | 2141 | // Is it enough that we could fit this new text block? |
| 2162 | | const sym = self.local_symbols.items[big_block.local_sym_index]; |
| 2142 | const sym = self.locals.items[big_block.local_sym_index]; |
| 2163 | 2143 | const capacity = big_block.capacity(self.*); |
| 2164 | 2144 | const ideal_capacity = padToIdeal(capacity); |
| 2165 | 2145 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; |
| ... | ... | @@ -2190,7 +2170,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2190 | 2170 | } |
| 2191 | 2171 | break :blk new_start_vaddr; |
| 2192 | 2172 | } else if (self.last_text_block) |last| { |
| 2193 | | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 2173 | const last_symbol = self.locals.items[last.local_sym_index]; |
| 2194 | 2174 | // TODO We should pad out the excess capacity with NOPs. For executables, |
| 2195 | 2175 | // no padding seems to be OK, but it will probably not be for objects. |
| 2196 | 2176 | const ideal_capacity = padToIdeal(last.size); |
| ... | ... | @@ -2288,12 +2268,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { |
| 2288 | 2268 | } |
| 2289 | 2269 | |
| 2290 | 2270 | pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { |
| 2291 | | const index = @intCast(u32, self.extern_lazy_symbols.items().len); |
| 2271 | const index = @intCast(u32, self.lazy_imports.items().len); |
| 2292 | 2272 | const offset = try self.makeString(name); |
| 2293 | 2273 | const sym_name = try self.base.allocator.dupe(u8, name); |
| 2294 | 2274 | const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem. |
| 2295 | | try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{ |
| 2296 | | .inner = .{ |
| 2275 | try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{ |
| 2276 | .symbol = .{ |
| 2297 | 2277 | .n_strx = offset, |
| 2298 | 2278 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 2299 | 2279 | .n_sect = 0, |
| ... | ... | @@ -2301,6 +2281,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { |
| 2301 | 2281 | .n_value = 0, |
| 2302 | 2282 | }, |
| 2303 | 2283 | .dylib_ordinal = dylib_ordinal, |
| 2284 | .index = index, |
| 2304 | 2285 | }); |
| 2305 | 2286 | log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal }); |
| 2306 | 2287 | return index; |
| ... | ... | @@ -2459,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2459 | 2440 | } |
| 2460 | 2441 | |
| 2461 | 2442 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2462 | | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2463 | | const sect = &text_segment.sections.items[self.got_section_index.?]; |
| 2443 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2444 | const sect = &seg.sections.items[self.got_section_index.?]; |
| 2464 | 2445 | const off = sect.offset + @sizeOf(u64) * index; |
| 2465 | | const vmaddr = sect.addr + @sizeOf(u64) * index; |
| 2466 | 2446 | |
| 2467 | 2447 | if (self.offset_table_count_dirty) { |
| 2468 | 2448 | // TODO relocate. |
| 2469 | 2449 | self.offset_table_count_dirty = false; |
| 2470 | 2450 | } |
| 2471 | 2451 | |
| 2472 | | var code: [8]u8 = undefined; |
| 2473 | | switch (self.base.options.target.cpu.arch) { |
| 2474 | | .x86_64 => { |
| 2475 | | const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7); |
| 2476 | | const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1); |
| 2477 | | // lea %rax, [rip - disp] |
| 2478 | | code[0] = 0x48; |
| 2479 | | code[1] = 0x8D; |
| 2480 | | code[2] = 0x5; |
| 2481 | | mem.writeIntLittle(u32, code[3..7], symbol_off); |
| 2482 | | // ret |
| 2483 | | code[7] = 0xC3; |
| 2484 | | }, |
| 2485 | | .aarch64 => { |
| 2486 | | const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]); |
| 2487 | | const symbol_off = @as(i21, pos_symbol_off) * -1; |
| 2488 | | // adr x0, #-disp |
| 2489 | | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); |
| 2490 | | // ret x28 |
| 2491 | | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32()); |
| 2492 | | }, |
| 2493 | | else => unreachable, // unsupported target architecture |
| 2494 | | } |
| 2495 | | log.debug("writing offset table entry 0x{x} at 0x{x}", .{ self.offset_table.items[index], off }); |
| 2496 | | try self.base.file.?.pwriteAll(&code, off); |
| 2452 | const got_entry = self.offset_table.items[index]; |
| 2453 | const sym = blk: { |
| 2454 | switch (got_entry.kind) { |
| 2455 | .Local => { |
| 2456 | break :blk self.locals.items[got_entry.symbol]; |
| 2457 | }, |
| 2458 | .Extern => { |
| 2459 | break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol; |
| 2460 | }, |
| 2461 | } |
| 2462 | }; |
| 2463 | const sym_name = self.getString(sym.n_strx); |
| 2464 | log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name }); |
| 2465 | try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off); |
| 2497 | 2466 | } |
| 2498 | 2467 | |
| 2499 | 2468 | fn writeLazySymbolPointer(self: *MachO, index: u32) !void { |
| ... | ... | @@ -2516,6 +2485,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void { |
| 2516 | 2485 | try self.base.file.?.pwriteAll(&buf, off); |
| 2517 | 2486 | } |
| 2518 | 2487 | |
| 2488 | fn writeStubHelperPreamble(self: *MachO) !void { |
| 2489 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2490 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; |
| 2491 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2492 | const got = &data_const_segment.sections.items[self.got_section_index.?]; |
| 2493 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2494 | const data = &data_segment.sections.items[self.data_section_index.?]; |
| 2495 | |
| 2496 | switch (self.base.options.target.cpu.arch) { |
| 2497 | .x86_64 => { |
| 2498 | const code_size = 15; |
| 2499 | var code: [code_size]u8 = undefined; |
| 2500 | // lea %r11, [rip + disp] |
| 2501 | code[0] = 0x4c; |
| 2502 | code[1] = 0x8d; |
| 2503 | code[2] = 0x1d; |
| 2504 | { |
| 2505 | const target_addr = data.addr; |
| 2506 | const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7); |
| 2507 | mem.writeIntLittle(u32, code[3..7], displacement); |
| 2508 | } |
| 2509 | // push %r11 |
| 2510 | code[7] = 0x41; |
| 2511 | code[8] = 0x53; |
| 2512 | // jmp [rip + disp] |
| 2513 | code[9] = 0xff; |
| 2514 | code[10] = 0x25; |
| 2515 | { |
| 2516 | const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size); |
| 2517 | mem.writeIntLittle(u32, code[11..], displacement); |
| 2518 | } |
| 2519 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); |
| 2520 | self.stub_helper_stubs_start_off = stub_helper.offset + code_size; |
| 2521 | }, |
| 2522 | .aarch64 => { |
| 2523 | var code: [6 * @sizeOf(u32)]u8 = undefined; |
| 2524 | |
| 2525 | data_blk_outer: { |
| 2526 | const this_addr = stub_helper.addr; |
| 2527 | const target_addr = data.addr; |
| 2528 | data_blk: { |
| 2529 | const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk; |
| 2530 | // adr x17, disp |
| 2531 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); |
| 2532 | // nop |
| 2533 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); |
| 2534 | break :data_blk_outer; |
| 2535 | } |
| 2536 | data_blk: { |
| 2537 | const new_this_addr = this_addr + @sizeOf(u32); |
| 2538 | const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk; |
| 2539 | // nop |
| 2540 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); |
| 2541 | // adr x17, disp |
| 2542 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); |
| 2543 | break :data_blk_outer; |
| 2544 | } |
| 2545 | // Jump is too big, replace adr with adrp and add. |
| 2546 | const this_page = @intCast(i32, this_addr >> 12); |
| 2547 | const target_page = @intCast(i32, target_addr >> 12); |
| 2548 | const pages = @intCast(i21, target_page - this_page); |
| 2549 | // adrp x17, pages |
| 2550 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); |
| 2551 | const narrowed = @truncate(u12, target_addr); |
| 2552 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); |
| 2553 | } |
| 2554 | |
| 2555 | // stp x16, x17, [sp, #-16]! |
| 2556 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.stp( |
| 2557 | .x16, |
| 2558 | .x17, |
| 2559 | aarch64.Register.sp, |
| 2560 | aarch64.Instruction.LoadStorePairOffset.pre_index(-16), |
| 2561 | ).toU32()); |
| 2562 | |
| 2563 | binder_blk_outer: { |
| 2564 | const this_addr = stub_helper.addr + 3 * @sizeOf(u32); |
| 2565 | const target_addr = got.addr; |
| 2566 | binder_blk: { |
| 2567 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk; |
| 2568 | const literal = math.cast(u18, displacement) catch |_| break :binder_blk; |
| 2569 | // ldr x16, label |
| 2570 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ |
| 2571 | .literal = literal, |
| 2572 | }).toU32()); |
| 2573 | // nop |
| 2574 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); |
| 2575 | break :binder_blk_outer; |
| 2576 | } |
| 2577 | binder_blk: { |
| 2578 | const new_this_addr = this_addr + @sizeOf(u32); |
| 2579 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk; |
| 2580 | const literal = math.cast(u18, displacement) catch |_| break :binder_blk; |
| 2581 | // nop |
| 2582 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); |
| 2583 | // ldr x16, label |
| 2584 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ |
| 2585 | .literal = literal, |
| 2586 | }).toU32()); |
| 2587 | break :binder_blk_outer; |
| 2588 | } |
| 2589 | // Jump is too big, replace ldr with adrp and ldr(register). |
| 2590 | const this_page = @intCast(i32, this_addr >> 12); |
| 2591 | const target_page = @intCast(i32, target_addr >> 12); |
| 2592 | const pages = @intCast(i21, target_page - this_page); |
| 2593 | // adrp x16, pages |
| 2594 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); |
| 2595 | const narrowed = @truncate(u12, target_addr); |
| 2596 | const offset = try math.divExact(u12, narrowed, 8); |
| 2597 | // ldr x16, x16, offset |
| 2598 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ |
| 2599 | .register = .{ |
| 2600 | .rn = .x16, |
| 2601 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), |
| 2602 | }, |
| 2603 | }).toU32()); |
| 2604 | } |
| 2605 | |
| 2606 | // br x16 |
| 2607 | mem.writeIntLittle(u32, code[20..24], aarch64.Instruction.br(.x16).toU32()); |
| 2608 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); |
| 2609 | self.stub_helper_stubs_start_off = stub_helper.offset + code.len; |
| 2610 | }, |
| 2611 | else => unreachable, |
| 2612 | } |
| 2613 | } |
| 2614 | |
| 2519 | 2615 | fn writeStub(self: *MachO, index: u32) !void { |
| 2520 | 2616 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2521 | 2617 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| ... | ... | @@ -2525,9 +2621,12 @@ fn writeStub(self: *MachO, index: u32) !void { |
| 2525 | 2621 | const stub_off = stubs.offset + index * stubs.reserved2; |
| 2526 | 2622 | const stub_addr = stubs.addr + index * stubs.reserved2; |
| 2527 | 2623 | const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); |
| 2624 | |
| 2528 | 2625 | log.debug("writing stub at 0x{x}", .{stub_off}); |
| 2626 | |
| 2529 | 2627 | var code = try self.base.allocator.alloc(u8, stubs.reserved2); |
| 2530 | 2628 | defer self.base.allocator.free(code); |
| 2629 | |
| 2531 | 2630 | switch (self.base.options.target.cpu.arch) { |
| 2532 | 2631 | .x86_64 => { |
| 2533 | 2632 | assert(la_ptr_addr >= stub_addr + stubs.reserved2); |
| ... | ... | @@ -2539,12 +2638,50 @@ fn writeStub(self: *MachO, index: u32) !void { |
| 2539 | 2638 | }, |
| 2540 | 2639 | .aarch64 => { |
| 2541 | 2640 | assert(la_ptr_addr >= stub_addr); |
| 2542 | | const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4); |
| 2543 | | const literal = try math.cast(u19, displacement); |
| 2544 | | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ |
| 2545 | | .literal = literal, |
| 2546 | | }).toU32()); |
| 2547 | | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32()); |
| 2641 | outer: { |
| 2642 | const this_addr = stub_addr; |
| 2643 | const target_addr = la_ptr_addr; |
| 2644 | inner: { |
| 2645 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner; |
| 2646 | const literal = math.cast(u18, displacement) catch |_| break :inner; |
| 2647 | // ldr x16, literal |
| 2648 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ |
| 2649 | .literal = literal, |
| 2650 | }).toU32()); |
| 2651 | // nop |
| 2652 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); |
| 2653 | break :outer; |
| 2654 | } |
| 2655 | inner: { |
| 2656 | const new_this_addr = this_addr + @sizeOf(u32); |
| 2657 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner; |
| 2658 | const literal = math.cast(u18, displacement) catch |_| break :inner; |
| 2659 | // nop |
| 2660 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); |
| 2661 | // ldr x16, literal |
| 2662 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ |
| 2663 | .literal = literal, |
| 2664 | }).toU32()); |
| 2665 | break :outer; |
| 2666 | } |
| 2667 | // Use adrp followed by ldr(register). |
| 2668 | const this_page = @intCast(i32, this_addr >> 12); |
| 2669 | const target_page = @intCast(i32, target_addr >> 12); |
| 2670 | const pages = @intCast(i21, target_page - this_page); |
| 2671 | // adrp x16, pages |
| 2672 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32()); |
| 2673 | const narrowed = @truncate(u12, target_addr); |
| 2674 | const offset = try math.divExact(u12, narrowed, 8); |
| 2675 | // ldr x16, x16, offset |
| 2676 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ |
| 2677 | .register = .{ |
| 2678 | .rn = .x16, |
| 2679 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), |
| 2680 | }, |
| 2681 | }).toU32()); |
| 2682 | } |
| 2683 | // br x16 |
| 2684 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); |
| 2548 | 2685 | }, |
| 2549 | 2686 | else => unreachable, |
| 2550 | 2687 | } |
| ... | ... | @@ -2561,8 +2698,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2561 | 2698 | else => unreachable, |
| 2562 | 2699 | }; |
| 2563 | 2700 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; |
| 2701 | |
| 2564 | 2702 | var code = try self.base.allocator.alloc(u8, stub_size); |
| 2565 | 2703 | defer self.base.allocator.free(code); |
| 2704 | |
| 2566 | 2705 | switch (self.base.options.target.cpu.arch) { |
| 2567 | 2706 | .x86_64 => { |
| 2568 | 2707 | const displacement = try math.cast( |
| ... | ... | @@ -2577,12 +2716,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2577 | 2716 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); |
| 2578 | 2717 | }, |
| 2579 | 2718 | .aarch64 => { |
| 2580 | | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); |
| 2719 | const literal = blk: { |
| 2720 | const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4); |
| 2721 | break :blk try math.cast(u18, div_res); |
| 2722 | }; |
| 2723 | // ldr w16, literal |
| 2581 | 2724 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ |
| 2582 | | .literal = @divExact(stub_size - @sizeOf(u32), 4), |
| 2725 | .literal = literal, |
| 2583 | 2726 | }).toU32()); |
| 2727 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); |
| 2728 | // b disp |
| 2584 | 2729 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); |
| 2585 | | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 2730 | // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 2731 | mem.writeIntLittle(u32, code[8..12], 0x0); |
| 2586 | 2732 | }, |
| 2587 | 2733 | else => unreachable, |
| 2588 | 2734 | } |
| ... | ... | @@ -2591,9 +2737,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2591 | 2737 | |
| 2592 | 2738 | fn relocateSymbolTable(self: *MachO) !void { |
| 2593 | 2739 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2594 | | const nlocals = self.local_symbols.items.len; |
| 2595 | | const nglobals = self.global_symbols.items.len; |
| 2596 | | const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; |
| 2740 | const nlocals = self.locals.items.len; |
| 2741 | const nglobals = self.globals.items.len; |
| 2742 | const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len; |
| 2597 | 2743 | const nsyms = nlocals + nglobals + nundefs; |
| 2598 | 2744 | |
| 2599 | 2745 | if (symtab.nsyms < nsyms) { |
| ... | ... | @@ -2628,7 +2774,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void { |
| 2628 | 2774 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2629 | 2775 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 2630 | 2776 | log.debug("writing local symbol {} at 0x{x}", .{ index, off }); |
| 2631 | | try self.base.file.?.pwriteAll(mem.asBytes(&self.local_symbols.items[index]), off); |
| 2777 | try self.base.file.?.pwriteAll(mem.asBytes(&self.locals.items[index]), off); |
| 2632 | 2778 | } |
| 2633 | 2779 | |
| 2634 | 2780 | fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| ... | ... | @@ -2637,18 +2783,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2637 | 2783 | |
| 2638 | 2784 | try self.relocateSymbolTable(); |
| 2639 | 2785 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2640 | | const nlocals = self.local_symbols.items.len; |
| 2641 | | const nglobals = self.global_symbols.items.len; |
| 2786 | const nlocals = self.locals.items.len; |
| 2787 | const nglobals = self.globals.items.len; |
| 2642 | 2788 | |
| 2643 | | const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; |
| 2789 | const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len; |
| 2644 | 2790 | var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 2645 | 2791 | defer undefs.deinit(); |
| 2646 | 2792 | try undefs.ensureCapacity(nundefs); |
| 2647 | | for (self.extern_lazy_symbols.items()) |entry| { |
| 2648 | | undefs.appendAssumeCapacity(entry.value.inner); |
| 2793 | for (self.lazy_imports.items()) |entry| { |
| 2794 | undefs.appendAssumeCapacity(entry.value.symbol); |
| 2649 | 2795 | } |
| 2650 | | for (self.extern_nonlazy_symbols.items()) |entry| { |
| 2651 | | undefs.appendAssumeCapacity(entry.value.inner); |
| 2796 | for (self.nonlazy_imports.items()) |entry| { |
| 2797 | undefs.appendAssumeCapacity(entry.value.symbol); |
| 2652 | 2798 | } |
| 2653 | 2799 | |
| 2654 | 2800 | const locals_off = symtab.symoff; |
| ... | ... | @@ -2657,7 +2803,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2657 | 2803 | const globals_off = locals_off + locals_size; |
| 2658 | 2804 | const globals_size = nglobals * @sizeOf(macho.nlist_64); |
| 2659 | 2805 | log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off }); |
| 2660 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off); |
| 2806 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), globals_off); |
| 2661 | 2807 | |
| 2662 | 2808 | const undefs_off = globals_off + globals_size; |
| 2663 | 2809 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); |
| ... | ... | @@ -2683,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2683 | 2829 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2684 | 2830 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; |
| 2685 | 2831 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2686 | | const got = &data_const_seg.sections.items[self.data_got_section_index.?]; |
| 2832 | const got = &data_const_seg.sections.items[self.got_section_index.?]; |
| 2687 | 2833 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2688 | 2834 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2689 | 2835 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 2690 | 2836 | |
| 2691 | | const lazy = self.extern_lazy_symbols.items(); |
| 2692 | | const nonlazy = self.extern_nonlazy_symbols.items(); |
| 2837 | const lazy = self.lazy_imports.items(); |
| 2838 | const got_entries = self.offset_table.items; |
| 2693 | 2839 | const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff); |
| 2694 | | const nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len); |
| 2840 | const nindirectsyms = @intCast(u32, lazy.len * 2 + got_entries.len); |
| 2695 | 2841 | const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32)); |
| 2696 | 2842 | |
| 2697 | 2843 | if (needed_size > allocated_size) { |
| ... | ... | @@ -2710,20 +2856,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2710 | 2856 | var writer = stream.writer(); |
| 2711 | 2857 | |
| 2712 | 2858 | stubs.reserved1 = 0; |
| 2713 | | for (self.extern_lazy_symbols.items()) |_, i| { |
| 2859 | for (lazy) |_, i| { |
| 2714 | 2860 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2715 | 2861 | try writer.writeIntLittle(u32, symtab_idx); |
| 2716 | 2862 | } |
| 2717 | 2863 | |
| 2718 | 2864 | const base_id = @intCast(u32, lazy.len); |
| 2719 | 2865 | got.reserved1 = base_id; |
| 2720 | | for (self.extern_nonlazy_symbols.items()) |_, i| { |
| 2721 | | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id); |
| 2722 | | try writer.writeIntLittle(u32, symtab_idx); |
| 2866 | for (got_entries) |entry| { |
| 2867 | switch (entry.kind) { |
| 2868 | .Local => { |
| 2869 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); |
| 2870 | }, |
| 2871 | .Extern => { |
| 2872 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id); |
| 2873 | try writer.writeIntLittle(u32, symtab_idx); |
| 2874 | }, |
| 2875 | } |
| 2723 | 2876 | } |
| 2724 | 2877 | |
| 2725 | | la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len); |
| 2726 | | for (self.extern_lazy_symbols.items()) |_, i| { |
| 2878 | la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len); |
| 2879 | for (lazy) |_, i| { |
| 2727 | 2880 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2728 | 2881 | try writer.writeIntLittle(u32, symtab_idx); |
| 2729 | 2882 | } |
| ... | ... | @@ -2789,7 +2942,7 @@ fn writeCodeSignature(self: *MachO) !void { |
| 2789 | 2942 | |
| 2790 | 2943 | fn writeExportTrie(self: *MachO) !void { |
| 2791 | 2944 | if (!self.export_info_dirty) return; |
| 2792 | | if (self.global_symbols.items.len == 0) return; |
| 2945 | if (self.globals.items.len == 0) return; |
| 2793 | 2946 | |
| 2794 | 2947 | const tracy = trace(@src()); |
| 2795 | 2948 | defer tracy.end(); |
| ... | ... | @@ -2798,7 +2951,7 @@ fn writeExportTrie(self: *MachO) !void { |
| 2798 | 2951 | defer trie.deinit(); |
| 2799 | 2952 | |
| 2800 | 2953 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2801 | | for (self.global_symbols.items) |symbol| { |
| 2954 | for (self.globals.items) |symbol| { |
| 2802 | 2955 | // TODO figure out if we should put all global symbols into the export trie |
| 2803 | 2956 | const name = self.getString(symbol.n_strx); |
| 2804 | 2957 | assert(symbol.n_value >= text_segment.inner.vmaddr); |
| ... | ... | @@ -2840,14 +2993,48 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 2840 | 2993 | const tracy = trace(@src()); |
| 2841 | 2994 | defer tracy.end(); |
| 2842 | 2995 | |
| 2843 | | const size = try rebaseInfoSize(self.extern_lazy_symbols.items()); |
| 2996 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); |
| 2997 | defer pointers.deinit(); |
| 2998 | |
| 2999 | if (self.got_section_index) |idx| { |
| 3000 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 3001 | const sect = seg.sections.items[idx]; |
| 3002 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3003 | const segment_id = self.data_const_segment_cmd_index.?; |
| 3004 | |
| 3005 | for (self.offset_table.items) |entry| { |
| 3006 | if (entry.kind == .Extern) continue; |
| 3007 | try pointers.append(.{ |
| 3008 | .offset = base_offset + entry.index * @sizeOf(u64), |
| 3009 | .segment_id = segment_id, |
| 3010 | }); |
| 3011 | } |
| 3012 | } |
| 3013 | |
| 3014 | if (self.la_symbol_ptr_section_index) |idx| { |
| 3015 | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len); |
| 3016 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 3017 | const sect = seg.sections.items[idx]; |
| 3018 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3019 | const segment_id = self.data_segment_cmd_index.?; |
| 3020 | |
| 3021 | for (self.lazy_imports.items()) |entry| { |
| 3022 | pointers.appendAssumeCapacity(.{ |
| 3023 | .offset = base_offset + entry.value.index * @sizeOf(u64), |
| 3024 | .segment_id = segment_id, |
| 3025 | }); |
| 3026 | } |
| 3027 | } |
| 3028 | |
| 3029 | std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp); |
| 3030 | |
| 3031 | const size = try bind.rebaseInfoSize(pointers.items); |
| 2844 | 3032 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2845 | 3033 | defer self.base.allocator.free(buffer); |
| 2846 | 3034 | |
| 2847 | 3035 | var stream = std.io.fixedBufferStream(buffer); |
| 2848 | | try writeRebaseInfo(self.extern_lazy_symbols.items(), stream.writer()); |
| 3036 | try bind.writeRebaseInfo(pointers.items, stream.writer()); |
| 2849 | 3037 | |
| 2850 | | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2851 | 3038 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2852 | 3039 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off); |
| 2853 | 3040 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| ... | ... | @@ -2872,14 +3059,34 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2872 | 3059 | const tracy = trace(@src()); |
| 2873 | 3060 | defer tracy.end(); |
| 2874 | 3061 | |
| 2875 | | const size = try bindInfoSize(self.extern_nonlazy_symbols.items()); |
| 3062 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); |
| 3063 | defer pointers.deinit(); |
| 3064 | |
| 3065 | if (self.got_section_index) |idx| { |
| 3066 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 3067 | const sect = seg.sections.items[idx]; |
| 3068 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3069 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); |
| 3070 | |
| 3071 | for (self.offset_table.items) |entry| { |
| 3072 | if (entry.kind == .Local) continue; |
| 3073 | const import = self.nonlazy_imports.items()[entry.symbol]; |
| 3074 | try pointers.append(.{ |
| 3075 | .offset = base_offset + entry.index * @sizeOf(u64), |
| 3076 | .segment_id = segment_id, |
| 3077 | .dylib_ordinal = import.value.dylib_ordinal, |
| 3078 | .name = import.key, |
| 3079 | }); |
| 3080 | } |
| 3081 | } |
| 3082 | |
| 3083 | const size = try bind.bindInfoSize(pointers.items); |
| 2876 | 3084 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2877 | 3085 | defer self.base.allocator.free(buffer); |
| 2878 | 3086 | |
| 2879 | 3087 | var stream = std.io.fixedBufferStream(buffer); |
| 2880 | | try writeBindInfo(self.extern_nonlazy_symbols.items(), stream.writer()); |
| 3088 | try bind.writeBindInfo(pointers.items, stream.writer()); |
| 2881 | 3089 | |
| 2882 | | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2883 | 3090 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2884 | 3091 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off); |
| 2885 | 3092 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| ... | ... | @@ -2901,14 +3108,36 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2901 | 3108 | fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2902 | 3109 | if (!self.lazy_binding_info_dirty) return; |
| 2903 | 3110 | |
| 2904 | | const size = try lazyBindInfoSize(self.extern_lazy_symbols.items()); |
| 3111 | const tracy = trace(@src()); |
| 3112 | defer tracy.end(); |
| 3113 | |
| 3114 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); |
| 3115 | defer pointers.deinit(); |
| 3116 | |
| 3117 | if (self.la_symbol_ptr_section_index) |idx| { |
| 3118 | try pointers.ensureCapacity(self.lazy_imports.items().len); |
| 3119 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 3120 | const sect = seg.sections.items[idx]; |
| 3121 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3122 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); |
| 3123 | |
| 3124 | for (self.lazy_imports.items()) |entry| { |
| 3125 | pointers.appendAssumeCapacity(.{ |
| 3126 | .offset = base_offset + entry.value.index * @sizeOf(u64), |
| 3127 | .segment_id = segment_id, |
| 3128 | .dylib_ordinal = entry.value.dylib_ordinal, |
| 3129 | .name = entry.key, |
| 3130 | }); |
| 3131 | } |
| 3132 | } |
| 3133 | |
| 3134 | const size = try bind.lazyBindInfoSize(pointers.items); |
| 2905 | 3135 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2906 | 3136 | defer self.base.allocator.free(buffer); |
| 2907 | 3137 | |
| 2908 | 3138 | var stream = std.io.fixedBufferStream(buffer); |
| 2909 | | try writeLazyBindInfo(self.extern_lazy_symbols.items(), stream.writer()); |
| 3139 | try bind.writeLazyBindInfo(pointers.items, stream.writer()); |
| 2910 | 3140 | |
| 2911 | | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2912 | 3141 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2913 | 3142 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off); |
| 2914 | 3143 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| ... | ... | @@ -2929,7 +3158,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2929 | 3158 | } |
| 2930 | 3159 | |
| 2931 | 3160 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2932 | | if (self.extern_lazy_symbols.items().len == 0) return; |
| 3161 | if (self.lazy_imports.items().len == 0) return; |
| 2933 | 3162 | |
| 2934 | 3163 | var stream = std.io.fixedBufferStream(buffer); |
| 2935 | 3164 | var reader = stream.reader(); |
| ... | ... | @@ -2975,7 +3204,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2975 | 3204 | else => {}, |
| 2976 | 3205 | } |
| 2977 | 3206 | } |
| 2978 | | assert(self.extern_lazy_symbols.items().len <= offsets.items.len); |
| 3207 | assert(self.lazy_imports.items().len <= offsets.items.len); |
| 2979 | 3208 | |
| 2980 | 3209 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 2981 | 3210 | .x86_64 => 10, |
| ... | ... | @@ -2988,7 +3217,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2988 | 3217 | else => unreachable, |
| 2989 | 3218 | }; |
| 2990 | 3219 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 2991 | | for (self.extern_lazy_symbols.items()) |_, i| { |
| 3220 | for (self.lazy_imports.items()) |_, i| { |
| 2992 | 3221 | const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off; |
| 2993 | 3222 | mem.writeIntLittle(u32, &buf, offsets.items[i]); |
| 2994 | 3223 | try self.base.file.?.pwriteAll(&buf, placeholder_off); |
| ... | ... | @@ -3193,12 +3422,12 @@ fn parseSymbolTable(self: *MachO) !void { |
| 3193 | 3422 | const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff); |
| 3194 | 3423 | assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len); |
| 3195 | 3424 | |
| 3196 | | try self.local_symbols.ensureCapacity(self.base.allocator, dysymtab.nlocalsym); |
| 3197 | | try self.global_symbols.ensureCapacity(self.base.allocator, dysymtab.nextdefsym); |
| 3425 | try self.locals.ensureCapacity(self.base.allocator, dysymtab.nlocalsym); |
| 3426 | try self.globals.ensureCapacity(self.base.allocator, dysymtab.nextdefsym); |
| 3198 | 3427 | try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym); |
| 3199 | 3428 | |
| 3200 | | self.local_symbols.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]); |
| 3201 | | self.global_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]); |
| 3429 | self.locals.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]); |
| 3430 | self.globals.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]); |
| 3202 | 3431 | self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]); |
| 3203 | 3432 | } |
| 3204 | 3433 | |