| ... | ... | @@ -100,31 +100,29 @@ data_section_index: ?u16 = null, |
| 100 | 100 | /// The absolute address of the entry point. |
| 101 | 101 | entry_addr: ?u64 = null, |
| 102 | 102 | |
| 103 | | /// Table of all local symbols |
| 104 | | /// Internally references string table for names (which are optional). |
| 105 | 103 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 106 | | /// Table of all global symbols |
| 107 | 104 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 108 | | /// Table of all extern nonlazy symbols, indexed by name. |
| 109 | | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 110 | | /// Table of all extern lazy symbols, indexed by name. |
| 111 | | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 105 | imports: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 106 | symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, |
| 112 | 107 | |
| 113 | 108 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 114 | 109 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 115 | | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 116 | 110 | |
| 117 | 111 | stub_helper_stubs_start_off: ?u64 = null, |
| 118 | 112 | |
| 119 | 113 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 120 | | strtab_cache: std.StringHashMapUnmanaged(u32) = .{}, |
| 121 | 114 | |
| 122 | | /// Table of GOT entries. |
| 123 | | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, |
| 115 | got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{}, |
| 116 | got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{}, |
| 117 | |
| 118 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 119 | |
| 120 | stubs: std.ArrayListUnmanaged(u32) = .{}, |
| 121 | stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{}, |
| 124 | 122 | |
| 125 | 123 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 126 | 124 | |
| 127 | | offset_table_count_dirty: bool = false, |
| 125 | got_entries_count_dirty: bool = false, |
| 128 | 126 | load_commands_dirty: bool = false, |
| 129 | 127 | rebase_info_dirty: bool = false, |
| 130 | 128 | binding_info_dirty: bool = false, |
| ... | ... | @@ -170,31 +168,25 @@ pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{}, |
| 170 | 168 | /// rather than sitting in the global scope. |
| 171 | 169 | stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, |
| 172 | 170 | |
| 173 | | pub const GOTEntry = struct { |
| 174 | | /// GOT entry can either be a local pointer or an extern (nonlazy) import. |
| 175 | | kind: enum { |
| 176 | | Local, |
| 177 | | Extern, |
| 171 | const SymbolWithLoc = struct { |
| 172 | // Table where the symbol can be found. |
| 173 | where: enum { |
| 174 | global, |
| 175 | import, |
| 176 | undef, |
| 177 | tentative, |
| 178 | 178 | }, |
| 179 | | |
| 180 | | /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports. |
| 181 | | /// TODO I'm more and more inclined to just manage a single, max two symbol tables |
| 182 | | /// rather than 4 as we currently do, but I'll follow up in the future PR. |
| 183 | | symbol: u32, |
| 184 | | |
| 185 | | /// Index of this entry in the GOT. |
| 186 | | index: u32, |
| 179 | where_index: u32, |
| 180 | local_sym_index: u32 = 0, |
| 181 | file: u16 = 0, |
| 187 | 182 | }; |
| 188 | 183 | |
| 189 | | pub const Import = struct { |
| 190 | | /// MachO symbol table entry. |
| 191 | | symbol: macho.nlist_64, |
| 192 | | |
| 193 | | /// Id of the dynamic library where the specified entries can be found. |
| 194 | | dylib_ordinal: i64, |
| 195 | | |
| 196 | | /// Index of this import within the import list. |
| 197 | | index: u32, |
| 184 | pub const GotIndirectionKey = struct { |
| 185 | where: enum { |
| 186 | local, |
| 187 | import, |
| 188 | }, |
| 189 | where_index: u32, |
| 198 | 190 | }; |
| 199 | 191 | |
| 200 | 192 | pub const PIEFixup = struct { |
| ... | ... | @@ -253,9 +245,6 @@ pub const TextBlock = struct { |
| 253 | 245 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or |
| 254 | 246 | /// offset table entry. |
| 255 | 247 | local_sym_index: u32, |
| 256 | | /// Index into offset table |
| 257 | | /// This field is undefined for symbols with size = 0. |
| 258 | | offset_table_index: u32, |
| 259 | 248 | /// Size of this text block |
| 260 | 249 | /// Unlike in Elf, we need to store the size of this symbol as part of |
| 261 | 250 | /// the TextBlock since macho.nlist_64 lacks this information. |
| ... | ... | @@ -275,7 +264,6 @@ pub const TextBlock = struct { |
| 275 | 264 | |
| 276 | 265 | pub const empty = TextBlock{ |
| 277 | 266 | .local_sym_index = 0, |
| 278 | | .offset_table_index = undefined, |
| 279 | 267 | .size = 0, |
| 280 | 268 | .prev = null, |
| 281 | 269 | .next = null, |
| ... | ... | @@ -433,7 +421,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 433 | 421 | } |
| 434 | 422 | } |
| 435 | 423 | |
| 436 | | if (build_options.have_llvm) { |
| 424 | if (build_options.is_stage1) { |
| 437 | 425 | return self.linkWithZld(comp); |
| 438 | 426 | } else { |
| 439 | 427 | switch (self.base.options.effectiveOutputMode()) { |
| ... | ... | @@ -500,7 +488,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 500 | 488 | self.error_flags.no_entry_point_found = false; |
| 501 | 489 | } |
| 502 | 490 | |
| 503 | | assert(!self.offset_table_count_dirty); |
| 491 | assert(!self.got_entries_count_dirty); |
| 504 | 492 | assert(!self.load_commands_dirty); |
| 505 | 493 | assert(!self.rebase_info_dirty); |
| 506 | 494 | assert(!self.binding_info_dirty); |
| ... | ... | @@ -971,31 +959,27 @@ pub fn deinit(self: *MachO) void { |
| 971 | 959 | if (self.d_sym) |*ds| { |
| 972 | 960 | ds.deinit(self.base.allocator); |
| 973 | 961 | } |
| 974 | | for (self.lazy_imports.keys()) |*key| { |
| 975 | | self.base.allocator.free(key.*); |
| 976 | | } |
| 977 | | self.lazy_imports.deinit(self.base.allocator); |
| 978 | | for (self.nonlazy_imports.keys()) |*key| { |
| 979 | | self.base.allocator.free(key.*); |
| 980 | | } |
| 981 | | self.nonlazy_imports.deinit(self.base.allocator); |
| 962 | |
| 982 | 963 | self.pie_fixups.deinit(self.base.allocator); |
| 983 | 964 | self.stub_fixups.deinit(self.base.allocator); |
| 984 | 965 | self.text_block_free_list.deinit(self.base.allocator); |
| 985 | | self.offset_table.deinit(self.base.allocator); |
| 986 | | self.offset_table_free_list.deinit(self.base.allocator); |
| 987 | | { |
| 988 | | var it = self.strtab_cache.keyIterator(); |
| 989 | | while (it.next()) |key| { |
| 990 | | self.base.allocator.free(key.*); |
| 991 | | } |
| 992 | | } |
| 993 | | self.strtab_cache.deinit(self.base.allocator); |
| 966 | self.got_entries.deinit(self.base.allocator); |
| 967 | self.got_entries_map.deinit(self.base.allocator); |
| 968 | self.got_entries_free_list.deinit(self.base.allocator); |
| 969 | self.stubs.deinit(self.base.allocator); |
| 970 | self.stubs_map.deinit(self.base.allocator); |
| 994 | 971 | self.strtab.deinit(self.base.allocator); |
| 972 | self.imports.deinit(self.base.allocator); |
| 995 | 973 | self.globals.deinit(self.base.allocator); |
| 996 | 974 | self.globals_free_list.deinit(self.base.allocator); |
| 997 | 975 | self.locals.deinit(self.base.allocator); |
| 998 | 976 | self.locals_free_list.deinit(self.base.allocator); |
| 977 | |
| 978 | for (self.symbol_resolver.keys()) |key| { |
| 979 | self.base.allocator.free(key); |
| 980 | } |
| 981 | self.symbol_resolver.deinit(self.base.allocator); |
| 982 | |
| 999 | 983 | for (self.load_commands.items) |*lc| { |
| 1000 | 984 | lc.deinit(self.base.allocator); |
| 1001 | 985 | } |
| ... | ... | @@ -1086,8 +1070,8 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig |
| 1086 | 1070 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1087 | 1071 | if (decl.link.macho.local_sym_index != 0) return; |
| 1088 | 1072 | |
| 1089 | | try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1); |
| 1090 | | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 1073 | try self.locals.ensureUnusedCapacity(self.base.allocator, 1); |
| 1074 | try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1); |
| 1091 | 1075 | |
| 1092 | 1076 | if (self.locals_free_list.popOrNull()) |i| { |
| 1093 | 1077 | log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); |
| ... | ... | @@ -1098,16 +1082,19 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1098 | 1082 | _ = self.locals.addOneAssumeCapacity(); |
| 1099 | 1083 | } |
| 1100 | 1084 | |
| 1101 | | if (self.offset_table_free_list.popOrNull()) |i| { |
| 1102 | | log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name }); |
| 1103 | | decl.link.macho.offset_table_index = i; |
| 1104 | | } else { |
| 1105 | | log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name }); |
| 1106 | | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 1107 | | _ = self.offset_table.addOneAssumeCapacity(); |
| 1108 | | self.offset_table_count_dirty = true; |
| 1109 | | self.rebase_info_dirty = true; |
| 1110 | | } |
| 1085 | const got_index: u32 = blk: { |
| 1086 | if (self.got_entries_free_list.popOrNull()) |i| { |
| 1087 | log.debug("reusing GOT entry index {d} for {s}", .{ i, decl.name }); |
| 1088 | break :blk i; |
| 1089 | } else { |
| 1090 | const got_index = @intCast(u32, self.got_entries.items.len); |
| 1091 | log.debug("allocating GOT entry index {d} for {s}", .{ got_index, decl.name }); |
| 1092 | _ = self.got_entries.addOneAssumeCapacity(); |
| 1093 | self.got_entries_count_dirty = true; |
| 1094 | self.rebase_info_dirty = true; |
| 1095 | break :blk got_index; |
| 1096 | } |
| 1097 | }; |
| 1111 | 1098 | |
| 1112 | 1099 | self.locals.items[decl.link.macho.local_sym_index] = .{ |
| 1113 | 1100 | .n_strx = 0, |
| ... | ... | @@ -1116,11 +1103,12 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1116 | 1103 | .n_desc = 0, |
| 1117 | 1104 | .n_value = 0, |
| 1118 | 1105 | }; |
| 1119 | | self.offset_table.items[decl.link.macho.offset_table_index] = .{ |
| 1120 | | .kind = .Local, |
| 1121 | | .symbol = decl.link.macho.local_sym_index, |
| 1122 | | .index = decl.link.macho.offset_table_index, |
| 1106 | const got_entry = GotIndirectionKey{ |
| 1107 | .where = .local, |
| 1108 | .where_index = decl.link.macho.local_sym_index, |
| 1123 | 1109 | }; |
| 1110 | self.got_entries.items[got_index] = got_entry; |
| 1111 | try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index); |
| 1124 | 1112 | } |
| 1125 | 1113 | |
| 1126 | 1114 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -1191,13 +1179,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1191 | 1179 | log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); |
| 1192 | 1180 | |
| 1193 | 1181 | if (vaddr != symbol.n_value) { |
| 1194 | | log.debug(" (writing new offset table entry)", .{}); |
| 1195 | | self.offset_table.items[decl.link.macho.offset_table_index] = .{ |
| 1196 | | .kind = .Local, |
| 1197 | | .symbol = decl.link.macho.local_sym_index, |
| 1198 | | .index = decl.link.macho.offset_table_index, |
| 1199 | | }; |
| 1200 | | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1182 | log.debug(" (writing new GOT entry)", .{}); |
| 1183 | const got_index = self.got_entries_map.get(.{ |
| 1184 | .where = .local, |
| 1185 | .where_index = decl.link.macho.local_sym_index, |
| 1186 | }) orelse unreachable; |
| 1187 | try self.writeGotEntry(got_index); |
| 1201 | 1188 | } |
| 1202 | 1189 | |
| 1203 | 1190 | symbol.n_value = vaddr; |
| ... | ... | @@ -1235,16 +1222,17 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1235 | 1222 | .n_desc = 0, |
| 1236 | 1223 | .n_value = addr, |
| 1237 | 1224 | }; |
| 1238 | | self.offset_table.items[decl.link.macho.offset_table_index] = .{ |
| 1239 | | .kind = .Local, |
| 1240 | | .symbol = decl.link.macho.local_sym_index, |
| 1241 | | .index = decl.link.macho.offset_table_index, |
| 1242 | | }; |
| 1243 | 1225 | |
| 1244 | 1226 | try self.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1227 | |
| 1245 | 1228 | if (self.d_sym) |*ds| |
| 1246 | 1229 | try ds.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1247 | | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1230 | |
| 1231 | const got_index = self.got_entries_map.get(.{ |
| 1232 | .where = .local, |
| 1233 | .where_index = decl.link.macho.local_sym_index, |
| 1234 | }) orelse unreachable; |
| 1235 | try self.writeGotEntry(got_index); |
| 1248 | 1236 | } |
| 1249 | 1237 | |
| 1250 | 1238 | // Calculate displacements to target addr (if any). |
| ... | ... | @@ -1291,7 +1279,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1291 | 1279 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1292 | 1280 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| 1293 | 1281 | for (self.stub_fixups.items) |fixup| { |
| 1294 | | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; |
| 1282 | const stubs_index = self.stubs_map.get(fixup.symbol) orelse unreachable; |
| 1283 | const stub_addr = stubs.addr + stubs_index * stubs.reserved2; |
| 1295 | 1284 | const text_addr = symbol.n_value + fixup.start; |
| 1296 | 1285 | switch (self.base.options.target.cpu.arch) { |
| 1297 | 1286 | .x86_64 => { |
| ... | ... | @@ -1309,9 +1298,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1309 | 1298 | else => unreachable, // unsupported target architecture |
| 1310 | 1299 | } |
| 1311 | 1300 | if (!fixup.already_defined) { |
| 1312 | | try self.writeStub(fixup.symbol); |
| 1313 | | try self.writeStubInStubHelper(fixup.symbol); |
| 1314 | | try self.writeLazySymbolPointer(fixup.symbol); |
| 1301 | try self.writeStub(stubs_index); |
| 1302 | try self.writeStubInStubHelper(stubs_index); |
| 1303 | try self.writeLazySymbolPointer(stubs_index); |
| 1315 | 1304 | |
| 1316 | 1305 | self.rebase_info_dirty = true; |
| 1317 | 1306 | self.lazy_binding_info_dirty = true; |
| ... | ... | @@ -1448,10 +1437,16 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 1448 | 1437 | self.freeTextBlock(&decl.link.macho); |
| 1449 | 1438 | if (decl.link.macho.local_sym_index != 0) { |
| 1450 | 1439 | self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; |
| 1451 | | self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {}; |
| 1452 | 1440 | |
| 1453 | | self.locals.items[decl.link.macho.local_sym_index].n_type = 0; |
| 1441 | const got_key = GotIndirectionKey{ |
| 1442 | .where = .local, |
| 1443 | .where_index = decl.link.macho.local_sym_index, |
| 1444 | }; |
| 1445 | const got_index = self.got_entries_map.get(got_key) orelse unreachable; |
| 1446 | _ = self.got_entries_map.remove(got_key); |
| 1447 | self.got_entries_free_list.append(self.base.allocator, got_index) catch {}; |
| 1454 | 1448 | |
| 1449 | self.locals.items[decl.link.macho.local_sym_index].n_type = 0; |
| 1455 | 1450 | decl.link.macho.local_sym_index = 0; |
| 1456 | 1451 | } |
| 1457 | 1452 | if (self.d_sym) |*ds| { |
| ... | ... | @@ -1506,8 +1501,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1506 | 1501 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 1507 | 1502 | |
| 1508 | 1503 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1509 | | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1510 | | const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint; |
| 1504 | const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1505 | const ideal_size = self.header_pad + program_code_size_hint + 3 * got_size_hint; |
| 1511 | 1506 | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); |
| 1512 | 1507 | |
| 1513 | 1508 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| ... | ... | @@ -1934,28 +1929,28 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1934 | 1929 | }); |
| 1935 | 1930 | self.load_commands_dirty = true; |
| 1936 | 1931 | } |
| 1937 | | if (!self.nonlazy_imports.contains("dyld_stub_binder")) { |
| 1938 | | const index = @intCast(u32, self.nonlazy_imports.count()); |
| 1939 | | const name = try self.base.allocator.dupe(u8, "dyld_stub_binder"); |
| 1940 | | const offset = try self.makeString("dyld_stub_binder"); |
| 1941 | | try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{ |
| 1942 | | .symbol = .{ |
| 1943 | | .n_strx = offset, |
| 1944 | | .n_type = std.macho.N_UNDF | std.macho.N_EXT, |
| 1945 | | .n_sect = 0, |
| 1946 | | .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, |
| 1947 | | .n_value = 0, |
| 1948 | | }, |
| 1949 | | .dylib_ordinal = 1, // TODO this is currently hardcoded. |
| 1950 | | .index = index, |
| 1932 | if (!self.symbol_resolver.contains("dyld_stub_binder")) { |
| 1933 | const import_sym_index = @intCast(u32, self.imports.items.len); |
| 1934 | try self.imports.append(self.base.allocator, .{ |
| 1935 | .n_strx = try self.makeString("dyld_stub_binder"), |
| 1936 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 1937 | .n_sect = 0, |
| 1938 | .n_desc = packDylibOrdinal(1), |
| 1939 | .n_value = 0, |
| 1951 | 1940 | }); |
| 1952 | | const off_index = @intCast(u32, self.offset_table.items.len); |
| 1953 | | try self.offset_table.append(self.base.allocator, .{ |
| 1954 | | .kind = .Extern, |
| 1955 | | .symbol = index, |
| 1956 | | .index = off_index, |
| 1941 | const name = try self.base.allocator.dupe(u8, "dyld_stub_binder"); |
| 1942 | try self.symbol_resolver.putNoClobber(self.base.allocator, name, .{ |
| 1943 | .where = .import, |
| 1944 | .where_index = import_sym_index, |
| 1957 | 1945 | }); |
| 1958 | | try self.writeOffsetTableEntry(off_index); |
| 1946 | const got_key = GotIndirectionKey{ |
| 1947 | .where = .import, |
| 1948 | .where_index = import_sym_index, |
| 1949 | }; |
| 1950 | const got_index = @intCast(u32, self.got_entries.items.len); |
| 1951 | try self.got_entries.append(self.base.allocator, got_key); |
| 1952 | try self.got_entries_map.putNoClobber(self.base.allocator, got_key, got_index); |
| 1953 | try self.writeGotEntry(got_index); |
| 1959 | 1954 | self.binding_info_dirty = true; |
| 1960 | 1955 | } |
| 1961 | 1956 | if (self.stub_helper_stubs_start_off == null) { |
| ... | ... | @@ -2068,24 +2063,25 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2068 | 2063 | return vaddr; |
| 2069 | 2064 | } |
| 2070 | 2065 | |
| 2071 | | pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { |
| 2072 | | const index = @intCast(u32, self.lazy_imports.count()); |
| 2073 | | const offset = try self.makeString(name); |
| 2074 | | const sym_name = try self.base.allocator.dupe(u8, name); |
| 2075 | | const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem. |
| 2076 | | try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{ |
| 2077 | | .symbol = .{ |
| 2078 | | .n_strx = offset, |
| 2079 | | .n_type = macho.N_UNDF | macho.N_EXT, |
| 2080 | | .n_sect = 0, |
| 2081 | | .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, |
| 2082 | | .n_value = 0, |
| 2083 | | }, |
| 2084 | | .dylib_ordinal = dylib_ordinal, |
| 2085 | | .index = index, |
| 2066 | pub fn addExternFn(self: *MachO, name: []const u8) !SymbolWithLoc { |
| 2067 | log.debug("adding new extern function '{s}' with dylib ordinal 1", .{name}); |
| 2068 | const import_sym_index = @intCast(u32, self.imports.items.len); |
| 2069 | try self.imports.append(self.base.allocator, .{ |
| 2070 | .n_strx = try self.makeString(name), |
| 2071 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 2072 | .n_sect = 0, |
| 2073 | .n_desc = packDylibOrdinal(1), |
| 2074 | .n_value = 0, |
| 2086 | 2075 | }); |
| 2087 | | log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal }); |
| 2088 | | return index; |
| 2076 | const resolv = .{ |
| 2077 | .where = .import, |
| 2078 | .where_index = import_sym_index, |
| 2079 | }; |
| 2080 | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, name), resolv); |
| 2081 | const stubs_index = @intCast(u32, self.stubs.items.len); |
| 2082 | try self.stubs.append(self.base.allocator, import_sym_index); |
| 2083 | try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index); |
| 2084 | return resolv; |
| 2089 | 2085 | } |
| 2090 | 2086 | |
| 2091 | 2087 | const NextSegmentAddressAndOffset = struct { |
| ... | ... | @@ -2239,29 +2235,26 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2239 | 2235 | return st; |
| 2240 | 2236 | } |
| 2241 | 2237 | |
| 2242 | | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2238 | fn writeGotEntry(self: *MachO, index: usize) !void { |
| 2243 | 2239 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2244 | 2240 | const sect = &seg.sections.items[self.got_section_index.?]; |
| 2245 | 2241 | const off = sect.offset + @sizeOf(u64) * index; |
| 2246 | 2242 | |
| 2247 | | if (self.offset_table_count_dirty) { |
| 2243 | if (self.got_entries_count_dirty) { |
| 2248 | 2244 | // TODO relocate. |
| 2249 | | self.offset_table_count_dirty = false; |
| 2245 | self.got_entries_count_dirty = false; |
| 2250 | 2246 | } |
| 2251 | 2247 | |
| 2252 | | const got_entry = self.offset_table.items[index]; |
| 2253 | | const sym = blk: { |
| 2254 | | switch (got_entry.kind) { |
| 2255 | | .Local => { |
| 2256 | | break :blk self.locals.items[got_entry.symbol]; |
| 2257 | | }, |
| 2258 | | .Extern => { |
| 2259 | | break :blk self.nonlazy_imports.values()[got_entry.symbol].symbol; |
| 2260 | | }, |
| 2261 | | } |
| 2248 | const got_entry = self.got_entries.items[index]; |
| 2249 | const sym = switch (got_entry.where) { |
| 2250 | .local => self.locals.items[got_entry.where_index], |
| 2251 | .import => self.imports.items[got_entry.where_index], |
| 2262 | 2252 | }; |
| 2263 | | const sym_name = self.getString(sym.n_strx) orelse unreachable; |
| 2264 | | log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name }); |
| 2253 | log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ |
| 2254 | off, |
| 2255 | sym.n_value, |
| 2256 | self.getString(sym.n_strx), |
| 2257 | }); |
| 2265 | 2258 | try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off); |
| 2266 | 2259 | } |
| 2267 | 2260 | |
| ... | ... | @@ -2539,7 +2532,7 @@ fn relocateSymbolTable(self: *MachO) !void { |
| 2539 | 2532 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2540 | 2533 | const nlocals = self.locals.items.len; |
| 2541 | 2534 | const nglobals = self.globals.items.len; |
| 2542 | | const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count(); |
| 2535 | const nundefs = self.imports.items.len; |
| 2543 | 2536 | const nsyms = nlocals + nglobals + nundefs; |
| 2544 | 2537 | |
| 2545 | 2538 | if (symtab.nsyms < nsyms) { |
| ... | ... | @@ -2584,17 +2577,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2584 | 2577 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2585 | 2578 | const nlocals = self.locals.items.len; |
| 2586 | 2579 | const nglobals = self.globals.items.len; |
| 2587 | | |
| 2588 | | const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count(); |
| 2589 | | var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 2590 | | defer undefs.deinit(); |
| 2591 | | try undefs.ensureCapacity(nundefs); |
| 2592 | | for (self.lazy_imports.values()) |*value| { |
| 2593 | | undefs.appendAssumeCapacity(value.symbol); |
| 2594 | | } |
| 2595 | | for (self.nonlazy_imports.values()) |*value| { |
| 2596 | | undefs.appendAssumeCapacity(value.symbol); |
| 2597 | | } |
| 2580 | const nundefs = self.imports.items.len; |
| 2598 | 2581 | |
| 2599 | 2582 | const locals_off = symtab.symoff; |
| 2600 | 2583 | const locals_size = nlocals * @sizeOf(macho.nlist_64); |
| ... | ... | @@ -2607,7 +2590,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2607 | 2590 | const undefs_off = globals_off + globals_size; |
| 2608 | 2591 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); |
| 2609 | 2592 | log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); |
| 2610 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); |
| 2593 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off); |
| 2611 | 2594 | |
| 2612 | 2595 | // Update dynamic symbol table. |
| 2613 | 2596 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| ... | ... | @@ -2633,10 +2616,10 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2633 | 2616 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2634 | 2617 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 2635 | 2618 | |
| 2636 | | const lazy_count = self.lazy_imports.count(); |
| 2637 | | const got_entries = self.offset_table.items; |
| 2619 | const nstubs = @intCast(u32, self.stubs.items.len); |
| 2620 | const ngot_entries = @intCast(u32, self.got_entries.items.len); |
| 2638 | 2621 | const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff); |
| 2639 | | const nindirectsyms = @intCast(u32, lazy_count * 2 + got_entries.len); |
| 2622 | const nindirectsyms = nstubs * 2 + ngot_entries; |
| 2640 | 2623 | const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32)); |
| 2641 | 2624 | |
| 2642 | 2625 | if (needed_size > allocated_size) { |
| ... | ... | @@ -2655,35 +2638,25 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2655 | 2638 | var writer = stream.writer(); |
| 2656 | 2639 | |
| 2657 | 2640 | stubs.reserved1 = 0; |
| 2658 | | { |
| 2659 | | var i: usize = 0; |
| 2660 | | while (i < lazy_count) : (i += 1) { |
| 2661 | | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2662 | | try writer.writeIntLittle(u32, symtab_idx); |
| 2663 | | } |
| 2641 | for (self.stubs.items) |id| { |
| 2642 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); |
| 2664 | 2643 | } |
| 2665 | 2644 | |
| 2666 | | const base_id = @intCast(u32, lazy_count); |
| 2667 | | got.reserved1 = base_id; |
| 2668 | | for (got_entries) |entry| { |
| 2669 | | switch (entry.kind) { |
| 2670 | | .Local => { |
| 2671 | | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); |
| 2645 | got.reserved1 = nstubs; |
| 2646 | for (self.got_entries.items) |entry| { |
| 2647 | switch (entry.where) { |
| 2648 | .import => { |
| 2649 | try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index); |
| 2672 | 2650 | }, |
| 2673 | | .Extern => { |
| 2674 | | const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id); |
| 2675 | | try writer.writeIntLittle(u32, symtab_idx); |
| 2651 | .local => { |
| 2652 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); |
| 2676 | 2653 | }, |
| 2677 | 2654 | } |
| 2678 | 2655 | } |
| 2679 | 2656 | |
| 2680 | | la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len); |
| 2681 | | { |
| 2682 | | var i: usize = 0; |
| 2683 | | while (i < lazy_count) : (i += 1) { |
| 2684 | | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2685 | | try writer.writeIntLittle(u32, symtab_idx); |
| 2686 | | } |
| 2657 | la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; |
| 2658 | for (self.stubs.items) |id| { |
| 2659 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); |
| 2687 | 2660 | } |
| 2688 | 2661 | |
| 2689 | 2662 | try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); |
| ... | ... | @@ -2756,13 +2729,18 @@ fn writeExportTrie(self: *MachO) !void { |
| 2756 | 2729 | defer trie.deinit(); |
| 2757 | 2730 | |
| 2758 | 2731 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2759 | | for (self.globals.items) |symbol| { |
| 2760 | | // TODO figure out if we should put all global symbols into the export trie |
| 2761 | | const name = self.getString(symbol.n_strx) orelse unreachable; |
| 2762 | | assert(symbol.n_value >= text_segment.inner.vmaddr); |
| 2732 | const base_address = text_segment.inner.vmaddr; |
| 2733 | |
| 2734 | // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. |
| 2735 | log.debug("writing export trie", .{}); |
| 2736 | |
| 2737 | for (self.globals.items) |sym| { |
| 2738 | const sym_name = self.getString(sym.n_strx); |
| 2739 | log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value }); |
| 2740 | |
| 2763 | 2741 | try trie.put(.{ |
| 2764 | | .name = name, |
| 2765 | | .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr, |
| 2742 | .name = sym_name, |
| 2743 | .vmaddr_offset = sym.n_value - base_address, |
| 2766 | 2744 | .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, |
| 2767 | 2745 | }); |
| 2768 | 2746 | } |
| ... | ... | @@ -2804,27 +2782,28 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 2804 | 2782 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2805 | 2783 | const sect = seg.sections.items[idx]; |
| 2806 | 2784 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 2807 | | const segment_id = self.data_const_segment_cmd_index.?; |
| 2785 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); |
| 2786 | |
| 2787 | for (self.got_entries.items) |entry, i| { |
| 2788 | if (entry.where == .import) continue; |
| 2808 | 2789 | |
| 2809 | | for (self.offset_table.items) |entry| { |
| 2810 | | if (entry.kind == .Extern) continue; |
| 2811 | 2790 | try pointers.append(.{ |
| 2812 | | .offset = base_offset + entry.index * @sizeOf(u64), |
| 2791 | .offset = base_offset + i * @sizeOf(u64), |
| 2813 | 2792 | .segment_id = segment_id, |
| 2814 | 2793 | }); |
| 2815 | 2794 | } |
| 2816 | 2795 | } |
| 2817 | 2796 | |
| 2818 | 2797 | if (self.la_symbol_ptr_section_index) |idx| { |
| 2819 | | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.count()); |
| 2820 | 2798 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2821 | 2799 | const sect = seg.sections.items[idx]; |
| 2822 | 2800 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 2823 | | const segment_id = self.data_segment_cmd_index.?; |
| 2801 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); |
| 2824 | 2802 | |
| 2825 | | for (self.lazy_imports.values()) |*value| { |
| 2803 | try pointers.ensureUnusedCapacity(self.stubs.items.len); |
| 2804 | for (self.stubs.items) |_, i| { |
| 2826 | 2805 | pointers.appendAssumeCapacity(.{ |
| 2827 | | .offset = base_offset + value.index * @sizeOf(u64), |
| 2806 | .offset = base_offset + i * @sizeOf(u64), |
| 2828 | 2807 | .segment_id = segment_id, |
| 2829 | 2808 | }); |
| 2830 | 2809 | } |
| ... | ... | @@ -2872,15 +2851,15 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2872 | 2851 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 2873 | 2852 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); |
| 2874 | 2853 | |
| 2875 | | for (self.offset_table.items) |entry| { |
| 2876 | | if (entry.kind == .Local) continue; |
| 2877 | | const import_key = self.nonlazy_imports.keys()[entry.symbol]; |
| 2878 | | const import_ordinal = self.nonlazy_imports.values()[entry.symbol].dylib_ordinal; |
| 2854 | for (self.got_entries.items) |entry, i| { |
| 2855 | if (entry.where == .local) continue; |
| 2856 | |
| 2857 | const sym = self.imports.items[entry.where_index]; |
| 2879 | 2858 | try pointers.append(.{ |
| 2880 | | .offset = base_offset + entry.index * @sizeOf(u64), |
| 2859 | .offset = base_offset + i * @sizeOf(u64), |
| 2881 | 2860 | .segment_id = segment_id, |
| 2882 | | .dylib_ordinal = import_ordinal, |
| 2883 | | .name = import_key, |
| 2861 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), |
| 2862 | .name = self.getString(sym.n_strx), |
| 2884 | 2863 | }); |
| 2885 | 2864 | } |
| 2886 | 2865 | } |
| ... | ... | @@ -2920,21 +2899,20 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2920 | 2899 | defer pointers.deinit(); |
| 2921 | 2900 | |
| 2922 | 2901 | if (self.la_symbol_ptr_section_index) |idx| { |
| 2923 | | try pointers.ensureCapacity(self.lazy_imports.count()); |
| 2924 | 2902 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2925 | 2903 | const sect = seg.sections.items[idx]; |
| 2926 | 2904 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 2927 | 2905 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); |
| 2928 | 2906 | |
| 2929 | | const slice = self.lazy_imports.entries.slice(); |
| 2930 | | const keys = slice.items(.key); |
| 2931 | | const values = slice.items(.value); |
| 2932 | | for (keys) |*key, i| { |
| 2907 | try pointers.ensureUnusedCapacity(self.stubs.items.len); |
| 2908 | |
| 2909 | for (self.stubs.items) |import_id, i| { |
| 2910 | const sym = self.imports.items[import_id]; |
| 2933 | 2911 | pointers.appendAssumeCapacity(.{ |
| 2934 | | .offset = base_offset + values[i].index * @sizeOf(u64), |
| 2912 | .offset = base_offset + i * @sizeOf(u64), |
| 2935 | 2913 | .segment_id = segment_id, |
| 2936 | | .dylib_ordinal = values[i].dylib_ordinal, |
| 2937 | | .name = key.*, |
| 2914 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), |
| 2915 | .name = self.getString(sym.n_strx), |
| 2938 | 2916 | }); |
| 2939 | 2917 | } |
| 2940 | 2918 | } |
| ... | ... | @@ -2966,7 +2944,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2966 | 2944 | } |
| 2967 | 2945 | |
| 2968 | 2946 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2969 | | if (self.lazy_imports.count() == 0) return; |
| 2947 | if (self.stubs.items.len == 0) return; |
| 2970 | 2948 | |
| 2971 | 2949 | var stream = std.io.fixedBufferStream(buffer); |
| 2972 | 2950 | var reader = stream.reader(); |
| ... | ... | @@ -3011,7 +2989,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 3011 | 2989 | else => {}, |
| 3012 | 2990 | } |
| 3013 | 2991 | } |
| 3014 | | assert(self.lazy_imports.count() <= offsets.items.len); |
| 2992 | assert(self.stubs.items.len <= offsets.items.len); |
| 3015 | 2993 | |
| 3016 | 2994 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 3017 | 2995 | .x86_64 => 10, |
| ... | ... | @@ -3024,9 +3002,9 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 3024 | 3002 | else => unreachable, |
| 3025 | 3003 | }; |
| 3026 | 3004 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 3027 | | for (offsets.items[0..self.lazy_imports.count()]) |offset, i| { |
| 3028 | | const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off; |
| 3029 | | mem.writeIntLittle(u32, &buf, offset); |
| 3005 | for (self.stubs.items) |_, index| { |
| 3006 | const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; |
| 3007 | mem.writeIntLittle(u32, &buf, offsets.items[index]); |
| 3030 | 3008 | try self.base.file.?.pwriteAll(&buf, placeholder_off); |
| 3031 | 3009 | } |
| 3032 | 3010 | } |
| ... | ... | @@ -3182,11 +3160,6 @@ fn hasTlvDescriptors(_: *MachO) bool { |
| 3182 | 3160 | } |
| 3183 | 3161 | |
| 3184 | 3162 | pub fn makeString(self: *MachO, string: []const u8) !u32 { |
| 3185 | | if (self.strtab_cache.get(string)) |off| { |
| 3186 | | log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off }); |
| 3187 | | return off; |
| 3188 | | } |
| 3189 | | |
| 3190 | 3163 | try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1); |
| 3191 | 3164 | const new_off = @intCast(u32, self.strtab.items.len); |
| 3192 | 3165 | |
| ... | ... | @@ -3195,12 +3168,18 @@ pub fn makeString(self: *MachO, string: []const u8) !u32 { |
| 3195 | 3168 | self.strtab.appendSliceAssumeCapacity(string); |
| 3196 | 3169 | self.strtab.appendAssumeCapacity(0); |
| 3197 | 3170 | |
| 3198 | | try self.strtab_cache.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, string), new_off); |
| 3199 | | |
| 3200 | 3171 | return new_off; |
| 3201 | 3172 | } |
| 3202 | 3173 | |
| 3203 | | pub fn getString(self: *MachO, off: u32) ?[]const u8 { |
| 3174 | pub fn getString(self: *MachO, off: u32) []const u8 { |
| 3204 | 3175 | assert(off < self.strtab.items.len); |
| 3205 | 3176 | return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off)); |
| 3206 | 3177 | } |
| 3178 | |
| 3179 | fn packDylibOrdinal(ordinal: u16) u16 { |
| 3180 | return ordinal * macho.N_SYMBOL_RESOLVER; |
| 3181 | } |
| 3182 | |
| 3183 | fn unpackDylibOrdinal(pack: u16) u16 { |
| 3184 | return @divExact(pack, macho.N_SYMBOL_RESOLVER); |
| 3185 | } |