| ... | @@ -78,6 +78,7 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, | ... | @@ -78,6 +78,7 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 78 | | 78 | |
| 79 | threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, | 79 | threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, |
| 80 | rebases: std.ArrayListUnmanaged(Pointer) = .{}, | 80 | rebases: std.ArrayListUnmanaged(Pointer) = .{}, |
| | 81 | stubs: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 81 | got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, | 82 | got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, |
| 82 | | 83 | |
| 83 | stub_helper_stubs_start_off: ?u64 = null, | 84 | stub_helper_stubs_start_off: ?u64 = null, |
| ... | @@ -85,11 +86,6 @@ stub_helper_stubs_start_off: ?u64 = null, | ... | @@ -85,11 +86,6 @@ stub_helper_stubs_start_off: ?u64 = null, |
| 85 | mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, | 86 | mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, |
| 86 | unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, | 87 | unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, |
| 87 | | 88 | |
| 88 | // TODO this will require scanning the relocations at least one to work out | | |
| 89 | // the exact amount of local GOT indirections. For the time being, set some | | |
| 90 | // default value. | | |
| 91 | const max_local_got_indirections: u16 = 1000; | | |
| 92 | | | |
| 93 | const GotEntry = struct { | 89 | const GotEntry = struct { |
| 94 | index: u32, | 90 | index: u32, |
| 95 | target_addr: u64, | 91 | target_addr: u64, |
| ... | @@ -100,7 +96,7 @@ const MappingKey = struct { | ... | @@ -100,7 +96,7 @@ const MappingKey = struct { |
| 100 | source_sect_id: u16, | 96 | source_sect_id: u16, |
| 101 | }; | 97 | }; |
| 102 | | 98 | |
| 103 | const SectionMapping = struct { | 99 | pub const SectionMapping = struct { |
| 104 | source_sect_id: u16, | 100 | source_sect_id: u16, |
| 105 | target_seg_id: u16, | 101 | target_seg_id: u16, |
| 106 | target_sect_id: u16, | 102 | target_sect_id: u16, |
| ... | @@ -172,24 +168,30 @@ const DebugInfo = struct { | ... | @@ -172,24 +168,30 @@ const DebugInfo = struct { |
| 172 | }; | 168 | }; |
| 173 | | 169 | |
| 174 | /// Default path to dyld | 170 | /// Default path to dyld |
| 175 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths | | |
| 176 | /// instead but this will do for now. | | |
| 177 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; | 171 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; |
| 178 | | 172 | |
| 179 | /// Default lib search path | | |
| 180 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths | | |
| 181 | /// instead but this will do for now. | | |
| 182 | const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib"; | | |
| 183 | | | |
| 184 | const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; | 173 | const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; |
| 185 | /// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it | 174 | /// TODO this should be inferred from included libSystem.tbd or similar. |
| 186 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; | 175 | const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib"; |
| 187 | | 176 | |
| 188 | pub fn init(allocator: *Allocator) Zld { | 177 | pub fn init(allocator: *Allocator) Zld { |
| 189 | return .{ .allocator = allocator }; | 178 | return .{ .allocator = allocator }; |
| 190 | } | 179 | } |
| 191 | | 180 | |
| 192 | pub fn deinit(self: *Zld) void { | 181 | pub fn deinit(self: *Zld) void { |
| | 182 | self.threadlocal_offsets.deinit(self.allocator); |
| | 183 | self.rebases.deinit(self.allocator); |
| | 184 | |
| | 185 | for (self.stubs.items()) |entry| { |
| | 186 | self.allocator.free(entry.key); |
| | 187 | } |
| | 188 | self.stubs.deinit(self.allocator); |
| | 189 | |
| | 190 | for (self.got_entries.items()) |entry| { |
| | 191 | self.allocator.free(entry.key); |
| | 192 | } |
| | 193 | self.got_entries.deinit(self.allocator); |
| | 194 | |
| 193 | for (self.load_commands.items) |*lc| { | 195 | for (self.load_commands.items) |*lc| { |
| 194 | lc.deinit(self.allocator); | 196 | lc.deinit(self.allocator); |
| 195 | } | 197 | } |
| ... | @@ -263,6 +265,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { | ... | @@ -263,6 +265,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| 263 | try self.populateMetadata(); | 265 | try self.populateMetadata(); |
| 264 | try self.parseInputFiles(files); | 266 | try self.parseInputFiles(files); |
| 265 | try self.resolveSymbols(); | 267 | try self.resolveSymbols(); |
| | 268 | try self.resolveStubsAndGotEntries(); |
| 266 | try self.updateMetadata(); | 269 | try self.updateMetadata(); |
| 267 | try self.sortSections(); | 270 | try self.sortSections(); |
| 268 | try self.allocateTextSegment(); | 271 | try self.allocateTextSegment(); |
| ... | @@ -272,7 +275,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { | ... | @@ -272,7 +275,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| 272 | try self.allocateSymbols(); | 275 | try self.allocateSymbols(); |
| 273 | self.printSymtab(); | 276 | self.printSymtab(); |
| 274 | // try self.writeStubHelperCommon(); | 277 | // try self.writeStubHelperCommon(); |
| 275 | // try self.doRelocs(); | 278 | // try self.resolveRelocsAndWriteSections(); |
| 276 | // try self.flush(); | 279 | // try self.flush(); |
| 277 | } | 280 | } |
| 278 | | 281 | |
| ... | @@ -814,14 +817,7 @@ fn sortSections(self: *Zld) !void { | ... | @@ -814,14 +817,7 @@ fn sortSections(self: *Zld) !void { |
| 814 | | 817 | |
| 815 | fn allocateTextSegment(self: *Zld) !void { | 818 | fn allocateTextSegment(self: *Zld) !void { |
| 816 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 819 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 817 | // TODO This should be worked out by scanning the relocations in the __text sections of all combined | 820 | const nstubs = @intCast(u32, self.stubs.items().len); |
| 818 | // object files. For the time being, assume all externs are stubs (this is wasting space but should | | |
| 819 | // correspond to the worst-case upper bound). | | |
| 820 | var nexterns: u32 = 0; | | |
| 821 | for (self.symtab.items()) |entry| { | | |
| 822 | if (entry.value.tag != .Import) continue; | | |
| 823 | nexterns += 1; | | |
| 824 | } | | |
| 825 | | 821 | |
| 826 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; | 822 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; |
| 827 | seg.inner.fileoff = 0; | 823 | seg.inner.fileoff = 0; |
| ... | @@ -830,14 +826,14 @@ fn allocateTextSegment(self: *Zld) !void { | ... | @@ -830,14 +826,14 @@ fn allocateTextSegment(self: *Zld) !void { |
| 830 | // Set stubs and stub_helper sizes | 826 | // Set stubs and stub_helper sizes |
| 831 | const stubs = &seg.sections.items[self.stubs_section_index.?]; | 827 | const stubs = &seg.sections.items[self.stubs_section_index.?]; |
| 832 | const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; | 828 | const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; |
| 833 | stubs.size += nexterns * stubs.reserved2; | 829 | stubs.size += nstubs * stubs.reserved2; |
| 834 | | 830 | |
| 835 | const stub_size: u4 = switch (self.arch.?) { | 831 | const stub_size: u4 = switch (self.arch.?) { |
| 836 | .x86_64 => 10, | 832 | .x86_64 => 10, |
| 837 | .aarch64 => 3 * @sizeOf(u32), | 833 | .aarch64 => 3 * @sizeOf(u32), |
| 838 | else => unreachable, | 834 | else => unreachable, |
| 839 | }; | 835 | }; |
| 840 | stub_helper.size += nexterns * stub_size; | 836 | stub_helper.size += nstubs * stub_size; |
| 841 | | 837 | |
| 842 | var sizeofcmds: u64 = 0; | 838 | var sizeofcmds: u64 = 0; |
| 843 | for (self.load_commands.items) |lc| { | 839 | for (self.load_commands.items) |lc| { |
| ... | @@ -872,14 +868,7 @@ fn allocateTextSegment(self: *Zld) !void { | ... | @@ -872,14 +868,7 @@ fn allocateTextSegment(self: *Zld) !void { |
| 872 | | 868 | |
| 873 | fn allocateDataConstSegment(self: *Zld) !void { | 869 | fn allocateDataConstSegment(self: *Zld) !void { |
| 874 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | 870 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 875 | // TODO This should be worked out by scanning the relocations in the __text sections of all | 871 | const nentries = @intCast(u32, self.got_entries.items().len); |
| 876 | // combined object files. For the time being, assume all externs are GOT entries (this is wasting space but | | |
| 877 | // should correspond to the worst-case upper bound). | | |
| 878 | var nexterns: u32 = 0; | | |
| 879 | for (self.symtab.items()) |entry| { | | |
| 880 | if (entry.value.tag != .Import) continue; | | |
| 881 | nexterns += 1; | | |
| 882 | } | | |
| 883 | | 872 | |
| 884 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 873 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 885 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; | 874 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; |
| ... | @@ -887,24 +876,14 @@ fn allocateDataConstSegment(self: *Zld) !void { | ... | @@ -887,24 +876,14 @@ fn allocateDataConstSegment(self: *Zld) !void { |
| 887 | | 876 | |
| 888 | // Set got size | 877 | // Set got size |
| 889 | const got = &seg.sections.items[self.got_section_index.?]; | 878 | const got = &seg.sections.items[self.got_section_index.?]; |
| 890 | // TODO this will require scanning the relocations at least one to work out | 879 | got.size += nentries * @sizeOf(u64); |
| 891 | // the exact amount of local GOT indirections. For the time being, set some | | |
| 892 | // default value. | | |
| 893 | got.size += (max_local_got_indirections + nexterns) * @sizeOf(u64); | | |
| 894 | | 880 | |
| 895 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); | 881 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); |
| 896 | } | 882 | } |
| 897 | | 883 | |
| 898 | fn allocateDataSegment(self: *Zld) !void { | 884 | fn allocateDataSegment(self: *Zld) !void { |
| 899 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | 885 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 900 | // TODO This should be worked out by scanning the relocations in the __text sections of all combined | 886 | const nstubs = @intCast(u32, self.stubs.items().len); |
| 901 | // object files. For the time being, assume all externs are stubs (this is wasting space but should | | |
| 902 | // correspond to the worst-case upper bound). | | |
| 903 | var nexterns: u32 = 0; | | |
| 904 | for (self.symtab.items()) |entry| { | | |
| 905 | if (entry.value.tag != .Import) continue; | | |
| 906 | nexterns += 1; | | |
| 907 | } | | |
| 908 | | 887 | |
| 909 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | 888 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 910 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; | 889 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; |
| ... | @@ -913,7 +892,7 @@ fn allocateDataSegment(self: *Zld) !void { | ... | @@ -913,7 +892,7 @@ fn allocateDataSegment(self: *Zld) !void { |
| 913 | // Set la_symbol_ptr and data size | 892 | // Set la_symbol_ptr and data size |
| 914 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; | 893 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; |
| 915 | const data = &seg.sections.items[self.data_section_index.?]; | 894 | const data = &seg.sections.items[self.data_section_index.?]; |
| 916 | la_symbol_ptr.size += nexterns * @sizeOf(u64); | 895 | la_symbol_ptr.size += nstubs * @sizeOf(u64); |
| 917 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder | 896 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder |
| 918 | | 897 | |
| 919 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); | 898 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); |
| ... | @@ -1408,26 +1387,16 @@ fn resolveSymbols(self: *Zld) !void { | ... | @@ -1408,26 +1387,16 @@ fn resolveSymbols(self: *Zld) !void { |
| 1408 | } | 1387 | } |
| 1409 | } | 1388 | } |
| 1410 | | 1389 | |
| 1411 | fn doRelocs(self: *Zld) !void { | 1390 | fn resolveStubsAndGotEntries(self: *Zld) !void {} |
| | 1391 | |
| | 1392 | fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1412 | for (self.objects.items) |object, object_id| { | 1393 | for (self.objects.items) |object, object_id| { |
| 1413 | log.debug("\n\n", .{}); | 1394 | log.debug("\n\n", .{}); |
| 1414 | log.debug("relocating object {s}", .{object.name}); | 1395 | log.debug("relocating object {s}", .{object.name}); |
| 1415 | | 1396 | |
| 1416 | const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; | 1397 | for (object.sections.items) |sect, source_sect_id| { |
| 1417 | | 1398 | const segname = parseName(&sect.inner.segname); |
| 1418 | for (seg.sections.items) |sect, source_sect_id| { | 1399 | const sectname = parseName(&sect.inner.sectname); |
| 1419 | const segname = parseName(&sect.segname); | | |
| 1420 | const sectname = parseName(&sect.sectname); | | |
| 1421 | | | |
| 1422 | var code = try self.allocator.alloc(u8, sect.size); | | |
| 1423 | _ = try object.file.preadAll(code, sect.offset); | | |
| 1424 | defer self.allocator.free(code); | | |
| 1425 | | | |
| 1426 | // Parse relocs (if any) | | |
| 1427 | var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); | | |
| 1428 | defer self.allocator.free(raw_relocs); | | |
| 1429 | _ = try object.file.preadAll(raw_relocs, sect.reloff); | | |
| 1430 | const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs); | | |
| 1431 | | 1400 | |
| 1432 | // Get mapping | 1401 | // Get mapping |
| 1433 | const target_mapping = self.mappings.get(.{ | 1402 | const target_mapping = self.mappings.get(.{ |
| ... | @@ -1442,498 +1411,75 @@ fn doRelocs(self: *Zld) !void { | ... | @@ -1442,498 +1411,75 @@ fn doRelocs(self: *Zld) !void { |
| 1442 | const target_sect_addr = target_sect.addr + target_mapping.offset; | 1411 | const target_sect_addr = target_sect.addr + target_mapping.offset; |
| 1443 | const target_sect_off = target_sect.offset + target_mapping.offset; | 1412 | const target_sect_off = target_sect.offset + target_mapping.offset; |
| 1444 | | 1413 | |
| 1445 | var addend: ?u64 = null; | 1414 | for (sect.relocs) |reloc| { |
| 1446 | var sub: ?i64 = null; | 1415 | const source_addr = target_sect_addr + reloc.offset; |
| 1447 | | | |
| 1448 | for (relocs) |rel| { | | |
| 1449 | const off = @intCast(u32, rel.r_address); | | |
| 1450 | const this_addr = target_sect_addr + off; | | |
| 1451 | | | |
| 1452 | switch (self.arch.?) { | | |
| 1453 | .aarch64 => { | | |
| 1454 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | | |
| 1455 | log.debug("{s}", .{rel_type}); | | |
| 1456 | log.debug(" | source address 0x{x}", .{this_addr}); | | |
| 1457 | log.debug(" | offset 0x{x}", .{off}); | | |
| 1458 | | | |
| 1459 | if (rel_type == .ARM64_RELOC_ADDEND) { | | |
| 1460 | addend = rel.r_symbolnum; | | |
| 1461 | log.debug(" | calculated addend = 0x{x}", .{addend}); | | |
| 1462 | // TODO followed by either PAGE21 or PAGEOFF12 only. | | |
| 1463 | continue; | | |
| 1464 | } | | |
| 1465 | }, | | |
| 1466 | .x86_64 => { | | |
| 1467 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | | |
| 1468 | log.debug("{s}", .{rel_type}); | | |
| 1469 | log.debug(" | source address 0x{x}", .{this_addr}); | | |
| 1470 | log.debug(" | offset 0x{x}", .{off}); | | |
| 1471 | }, | | |
| 1472 | else => {}, | | |
| 1473 | } | | |
| 1474 | | 1416 | |
| 1475 | const target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel); | 1417 | var args: Relocation.ResolveArgs = .{ |
| 1476 | log.debug(" | target address 0x{x}", .{target_addr}); | 1418 | .source_addr = source_addr, |
| 1477 | if (rel.r_extern == 1) { | 1419 | .target_addr = undefined, |
| 1478 | const target_symname = object.getString(object.symtab.items[rel.r_symbolnum].n_strx); | 1420 | }; |
| 1479 | log.debug(" | target symbol '{s}'", .{target_symname}); | 1421 | |
| 1480 | } else { | 1422 | if (reloc.cast(Relocation.Unsigned)) |unsigned| { |
| 1481 | const target_sectname = seg.sections.items[rel.r_symbolnum - 1].sectname; | 1423 | // TODO resolve target addr |
| 1482 | log.debug(" | target section '{s}'", .{parseName(&target_sectname)}); | | |
| 1483 | } | | |
| 1484 | | 1424 | |
| 1485 | switch (self.arch.?) { | 1425 | if (unsigned.subtractor) |subtractor| { |
| 1486 | .x86_64 => { | 1426 | args.subtractor = undefined; // TODO resolve |
| 1487 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | 1427 | } |
| 1488 | | 1428 | |
| 1489 | switch (rel_type) { | 1429 | rebases: { |
| 1490 | .X86_64_RELOC_BRANCH => { | 1430 | var hit: bool = false; |
| 1491 | assert(rel.r_length == 2); | 1431 | if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { |
| 1492 | const inst = code[off..][0..4]; | 1432 | if (self.data_section_index) |index| { |
| 1493 | const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4)); | 1433 | if (index == target_mapping.target_sect_id) hit = true; |
| 1494 | mem.writeIntLittle(u32, inst, displacement); | 1434 | } |
| 1495 | }, | | |
| 1496 | .X86_64_RELOC_GOT_LOAD => { | | |
| 1497 | assert(rel.r_length == 2); | | |
| 1498 | const inst = code[off..][0..4]; | | |
| 1499 | const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4)); | | |
| 1500 | | | |
| 1501 | blk: { | | |
| 1502 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | | |
| 1503 | const got = data_const_seg.sections.items[self.got_section_index.?]; | | |
| 1504 | if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk; | | |
| 1505 | log.debug(" | rewriting to leaq", .{}); | | |
| 1506 | code[off - 2] = 0x8d; | | |
| 1507 | } | | |
| 1508 | | | |
| 1509 | mem.writeIntLittle(u32, inst, displacement); | | |
| 1510 | }, | | |
| 1511 | .X86_64_RELOC_GOT => { | | |
| 1512 | assert(rel.r_length == 2); | | |
| 1513 | // TODO Instead of referring to the target symbol directly, we refer to it | | |
| 1514 | // indirectly via GOT. Getting actual target address should be done in the | | |
| 1515 | // helper relocTargetAddr function rather than here. | | |
| 1516 | const sym = object.symtab.items[rel.r_symbolnum]; | | |
| 1517 | const sym_name = try self.allocator.dupe(u8, object.getString(sym.n_strx)); | | |
| 1518 | const res = try self.nonlazy_pointers.getOrPut(self.allocator, sym_name); | | |
| 1519 | defer if (res.found_existing) self.allocator.free(sym_name); | | |
| 1520 | | | |
| 1521 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | | |
| 1522 | const got = data_const_seg.sections.items[self.got_section_index.?]; | | |
| 1523 | | | |
| 1524 | if (!res.found_existing) { | | |
| 1525 | const index = @intCast(u32, self.nonlazy_pointers.items().len) - 1; | | |
| 1526 | assert(index < max_local_got_indirections); // TODO This is just a temp solution. | | |
| 1527 | res.entry.value = .{ | | |
| 1528 | .index = index, | | |
| 1529 | .target_addr = target_addr, | | |
| 1530 | }; | | |
| 1531 | var buf: [@sizeOf(u64)]u8 = undefined; | | |
| 1532 | mem.writeIntLittle(u64, &buf, target_addr); | | |
| 1533 | const got_offset = got.offset + (index + self.nonlazy_imports.items().len) * @sizeOf(u64); | | |
| 1534 | | | |
| 1535 | log.debug(" | GOT off 0x{x}", .{got.offset}); | | |
| 1536 | log.debug(" | writing GOT entry 0x{x} at 0x{x}", .{ target_addr, got_offset }); | | |
| 1537 | | | |
| 1538 | try self.file.?.pwriteAll(&buf, got_offset); | | |
| 1539 | } | | |
| 1540 | | | |
| 1541 | const index = res.entry.value.index + self.nonlazy_imports.items().len; | | |
| 1542 | const actual_target_addr = got.addr + index * @sizeOf(u64); | | |
| 1543 | | | |
| 1544 | log.debug(" | GOT addr 0x{x}", .{got.addr}); | | |
| 1545 | log.debug(" | actual target address in GOT 0x{x}", .{actual_target_addr}); | | |
| 1546 | | | |
| 1547 | const inst = code[off..][0..4]; | | |
| 1548 | const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, actual_target_addr) - @intCast(i64, this_addr) - 4)); | | |
| 1549 | mem.writeIntLittle(u32, inst, displacement); | | |
| 1550 | }, | | |
| 1551 | .X86_64_RELOC_TLV => { | | |
| 1552 | assert(rel.r_length == 2); | | |
| 1553 | // We need to rewrite the opcode from movq to leaq. | | |
| 1554 | code[off - 2] = 0x8d; | | |
| 1555 | // Add displacement. | | |
| 1556 | const inst = code[off..][0..4]; | | |
| 1557 | const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4)); | | |
| 1558 | mem.writeIntLittle(u32, inst, displacement); | | |
| 1559 | }, | | |
| 1560 | .X86_64_RELOC_SIGNED, | | |
| 1561 | .X86_64_RELOC_SIGNED_1, | | |
| 1562 | .X86_64_RELOC_SIGNED_2, | | |
| 1563 | .X86_64_RELOC_SIGNED_4, | | |
| 1564 | => { | | |
| 1565 | assert(rel.r_length == 2); | | |
| 1566 | const inst = code[off..][0..4]; | | |
| 1567 | const offset = @intCast(i64, mem.readIntLittle(i32, inst)); | | |
| 1568 | log.debug(" | calculated addend 0x{x}", .{offset}); | | |
| 1569 | const actual_target_addr = blk: { | | |
| 1570 | if (rel.r_extern == 1) { | | |
| 1571 | break :blk @intCast(i64, target_addr) + offset; | | |
| 1572 | } else { | | |
| 1573 | const correction: i4 = switch (rel_type) { | | |
| 1574 | .X86_64_RELOC_SIGNED => 0, | | |
| 1575 | .X86_64_RELOC_SIGNED_1 => 1, | | |
| 1576 | .X86_64_RELOC_SIGNED_2 => 2, | | |
| 1577 | .X86_64_RELOC_SIGNED_4 => 4, | | |
| 1578 | else => unreachable, | | |
| 1579 | }; | | |
| 1580 | log.debug(" | calculated correction 0x{x}", .{correction}); | | |
| 1581 | | | |
| 1582 | // The value encoded in the instruction is a displacement - 4 - correction. | | |
| 1583 | // To obtain the adjusted target address in the final binary, we need | | |
| 1584 | // calculate the original target address within the object file, establish | | |
| 1585 | // what the offset from the original target section was, and apply this | | |
| 1586 | // offset to the resultant target section with this relocated binary. | | |
| 1587 | const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1); | | |
| 1588 | const target_map = self.mappings.get(.{ | | |
| 1589 | .object_id = @intCast(u16, object_id), | | |
| 1590 | .source_sect_id = orig_sect_id, | | |
| 1591 | }) orelse unreachable; | | |
| 1592 | const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; | | |
| 1593 | const orig_sect = orig_seg.sections.items[orig_sect_id]; | | |
| 1594 | const orig_offset = off + offset + 4 + correction - @intCast(i64, orig_sect.addr); | | |
| 1595 | log.debug(" | original offset 0x{x}", .{orig_offset}); | | |
| 1596 | const adjusted = @intCast(i64, target_addr) + orig_offset; | | |
| 1597 | log.debug(" | adjusted target address 0x{x}", .{adjusted}); | | |
| 1598 | break :blk adjusted - correction; | | |
| 1599 | } | | |
| 1600 | }; | | |
| 1601 | const result = actual_target_addr - @intCast(i64, this_addr) - 4; | | |
| 1602 | const displacement = @bitCast(u32, @intCast(i32, result)); | | |
| 1603 | mem.writeIntLittle(u32, inst, displacement); | | |
| 1604 | }, | | |
| 1605 | .X86_64_RELOC_SUBTRACTOR => { | | |
| 1606 | sub = @intCast(i64, target_addr); | | |
| 1607 | }, | | |
| 1608 | .X86_64_RELOC_UNSIGNED => { | | |
| 1609 | switch (rel.r_length) { | | |
| 1610 | 3 => { | | |
| 1611 | const inst = code[off..][0..8]; | | |
| 1612 | const offset = mem.readIntLittle(i64, inst); | | |
| 1613 | | | |
| 1614 | const result = outer: { | | |
| 1615 | if (rel.r_extern == 1) { | | |
| 1616 | log.debug(" | calculated addend 0x{x}", .{offset}); | | |
| 1617 | if (sub) |s| { | | |
| 1618 | break :outer @intCast(i64, target_addr) - s + offset; | | |
| 1619 | } else { | | |
| 1620 | break :outer @intCast(i64, target_addr) + offset; | | |
| 1621 | } | | |
| 1622 | } else { | | |
| 1623 | // The value encoded in the instruction is an absolute offset | | |
| 1624 | // from the start of MachO header to the target address in the | | |
| 1625 | // object file. To extract the address, we calculate the offset from | | |
| 1626 | // the beginning of the source section to the address, and apply it to | | |
| 1627 | // the target address value. | | |
| 1628 | const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1); | | |
| 1629 | const target_map = self.mappings.get(.{ | | |
| 1630 | .object_id = @intCast(u16, object_id), | | |
| 1631 | .source_sect_id = orig_sect_id, | | |
| 1632 | }) orelse unreachable; | | |
| 1633 | const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; | | |
| 1634 | const orig_sect = orig_seg.sections.items[orig_sect_id]; | | |
| 1635 | const orig_offset = offset - @intCast(i64, orig_sect.addr); | | |
| 1636 | const actual_target_addr = inner: { | | |
| 1637 | if (sub) |s| { | | |
| 1638 | break :inner @intCast(i64, target_addr) - s + orig_offset; | | |
| 1639 | } else { | | |
| 1640 | break :inner @intCast(i64, target_addr) + orig_offset; | | |
| 1641 | } | | |
| 1642 | }; | | |
| 1643 | log.debug(" | adjusted target address 0x{x}", .{actual_target_addr}); | | |
| 1644 | break :outer actual_target_addr; | | |
| 1645 | } | | |
| 1646 | }; | | |
| 1647 | mem.writeIntLittle(u64, inst, @bitCast(u64, result)); | | |
| 1648 | sub = null; | | |
| 1649 | | | |
| 1650 | rebases: { | | |
| 1651 | var hit: bool = false; | | |
| 1652 | if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { | | |
| 1653 | if (self.data_section_index) |index| { | | |
| 1654 | if (index == target_mapping.target_sect_id) hit = true; | | |
| 1655 | } | | |
| 1656 | } | | |
| 1657 | if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) { | | |
| 1658 | if (self.data_const_section_index) |index| { | | |
| 1659 | if (index == target_mapping.target_sect_id) hit = true; | | |
| 1660 | } | | |
| 1661 | } | | |
| 1662 | | | |
| 1663 | if (!hit) break :rebases; | | |
| 1664 | | | |
| 1665 | try self.local_rebases.append(self.allocator, .{ | | |
| 1666 | .offset = this_addr - target_seg.inner.vmaddr, | | |
| 1667 | .segment_id = target_mapping.target_seg_id, | | |
| 1668 | }); | | |
| 1669 | } | | |
| 1670 | // TLV is handled via a separate offset mechanism. | | |
| 1671 | // Calculate the offset to the initializer. | | |
| 1672 | if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { | | |
| 1673 | assert(rel.r_extern == 1); | | |
| 1674 | const sym = object.symtab.items[rel.r_symbolnum]; | | |
| 1675 | if (isImport(&sym)) break :tlv; | | |
| 1676 | | | |
| 1677 | const base_addr = blk: { | | |
| 1678 | if (self.tlv_data_section_index) |index| { | | |
| 1679 | const tlv_data = target_seg.sections.items[index]; | | |
| 1680 | break :blk tlv_data.addr; | | |
| 1681 | } else { | | |
| 1682 | const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?]; | | |
| 1683 | break :blk tlv_bss.addr; | | |
| 1684 | } | | |
| 1685 | }; | | |
| 1686 | // Since we require TLV data to always preceed TLV bss section, we calculate | | |
| 1687 | // offsets wrt to the former if it is defined; otherwise, wrt to the latter. | | |
| 1688 | try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr); | | |
| 1689 | } | | |
| 1690 | }, | | |
| 1691 | 2 => { | | |
| 1692 | const inst = code[off..][0..4]; | | |
| 1693 | const offset = mem.readIntLittle(i32, inst); | | |
| 1694 | log.debug(" | calculated addend 0x{x}", .{offset}); | | |
| 1695 | const result = if (sub) |s| | | |
| 1696 | @intCast(i64, target_addr) - s + offset | | |
| 1697 | else | | |
| 1698 | @intCast(i64, target_addr) + offset; | | |
| 1699 | mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result))); | | |
| 1700 | sub = null; | | |
| 1701 | }, | | |
| 1702 | else => |len| { | | |
| 1703 | log.err("unexpected relocation length 0x{x}", .{len}); | | |
| 1704 | return error.UnexpectedRelocationLength; | | |
| 1705 | }, | | |
| 1706 | } | | |
| 1707 | }, | | |
| 1708 | } | 1435 | } |
| 1709 | }, | 1436 | if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) { |
| 1710 | .aarch64 => { | 1437 | if (self.data_const_section_index) |index| { |
| 1711 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | 1438 | if (index == target_mapping.target_sect_id) hit = true; |
| 1712 | | 1439 | } |
| 1713 | switch (rel_type) { | | |
| 1714 | .ARM64_RELOC_BRANCH26 => { | | |
| 1715 | assert(rel.r_length == 2); | | |
| 1716 | const inst = code[off..][0..4]; | | |
| 1717 | const displacement = @intCast( | | |
| 1718 | i28, | | |
| 1719 | @intCast(i64, target_addr) - @intCast(i64, this_addr), | | |
| 1720 | ); | | |
| 1721 | var parsed = mem.bytesAsValue( | | |
| 1722 | meta.TagPayload( | | |
| 1723 | aarch64.Instruction, | | |
| 1724 | aarch64.Instruction.unconditional_branch_immediate, | | |
| 1725 | ), | | |
| 1726 | inst, | | |
| 1727 | ); | | |
| 1728 | parsed.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); | | |
| 1729 | }, | | |
| 1730 | .ARM64_RELOC_PAGE21, | | |
| 1731 | .ARM64_RELOC_GOT_LOAD_PAGE21, | | |
| 1732 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | | |
| 1733 | => { | | |
| 1734 | assert(rel.r_length == 2); | | |
| 1735 | const inst = code[off..][0..4]; | | |
| 1736 | const ta = if (addend) |a| target_addr + a else target_addr; | | |
| 1737 | const this_page = @intCast(i32, this_addr >> 12); | | |
| 1738 | const target_page = @intCast(i32, ta >> 12); | | |
| 1739 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); | | |
| 1740 | log.debug(" | moving by {} pages", .{pages}); | | |
| 1741 | var parsed = mem.bytesAsValue( | | |
| 1742 | meta.TagPayload( | | |
| 1743 | aarch64.Instruction, | | |
| 1744 | aarch64.Instruction.pc_relative_address, | | |
| 1745 | ), | | |
| 1746 | inst, | | |
| 1747 | ); | | |
| 1748 | parsed.immhi = @truncate(u19, pages >> 2); | | |
| 1749 | parsed.immlo = @truncate(u2, pages); | | |
| 1750 | addend = null; | | |
| 1751 | }, | | |
| 1752 | .ARM64_RELOC_PAGEOFF12, | | |
| 1753 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | | |
| 1754 | => { | | |
| 1755 | const inst = code[off..][0..4]; | | |
| 1756 | if (aarch64IsArithmetic(inst)) { | | |
| 1757 | log.debug(" | detected ADD opcode", .{}); | | |
| 1758 | // add | | |
| 1759 | var parsed = mem.bytesAsValue( | | |
| 1760 | meta.TagPayload( | | |
| 1761 | aarch64.Instruction, | | |
| 1762 | aarch64.Instruction.add_subtract_immediate, | | |
| 1763 | ), | | |
| 1764 | inst, | | |
| 1765 | ); | | |
| 1766 | const ta = if (addend) |a| target_addr + a else target_addr; | | |
| 1767 | const narrowed = @truncate(u12, ta); | | |
| 1768 | parsed.imm12 = narrowed; | | |
| 1769 | } else { | | |
| 1770 | log.debug(" | detected LDR/STR opcode", .{}); | | |
| 1771 | // ldr/str | | |
| 1772 | var parsed = mem.bytesAsValue( | | |
| 1773 | meta.TagPayload( | | |
| 1774 | aarch64.Instruction, | | |
| 1775 | aarch64.Instruction.load_store_register, | | |
| 1776 | ), | | |
| 1777 | inst, | | |
| 1778 | ); | | |
| 1779 | | | |
| 1780 | const ta = if (addend) |a| target_addr + a else target_addr; | | |
| 1781 | const narrowed = @truncate(u12, ta); | | |
| 1782 | log.debug(" | narrowed 0x{x}", .{narrowed}); | | |
| 1783 | log.debug(" | parsed.size 0x{x}", .{parsed.size}); | | |
| 1784 | | | |
| 1785 | if (rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12) blk: { | | |
| 1786 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | | |
| 1787 | const got = data_const_seg.sections.items[self.got_section_index.?]; | | |
| 1788 | if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk; | | |
| 1789 | | | |
| 1790 | log.debug(" | rewriting to add", .{}); | | |
| 1791 | mem.writeIntLittle(u32, inst, aarch64.Instruction.add( | | |
| 1792 | @intToEnum(aarch64.Register, parsed.rt), | | |
| 1793 | @intToEnum(aarch64.Register, parsed.rn), | | |
| 1794 | narrowed, | | |
| 1795 | false, | | |
| 1796 | ).toU32()); | | |
| 1797 | addend = null; | | |
| 1798 | continue; | | |
| 1799 | } | | |
| 1800 | | | |
| 1801 | const offset: u12 = blk: { | | |
| 1802 | if (parsed.size == 0) { | | |
| 1803 | if (parsed.v == 1) { | | |
| 1804 | // 128-bit SIMD is scaled by 16. | | |
| 1805 | break :blk try math.divExact(u12, narrowed, 16); | | |
| 1806 | } | | |
| 1807 | // Otherwise, 8-bit SIMD or ldrb. | | |
| 1808 | break :blk narrowed; | | |
| 1809 | } else { | | |
| 1810 | const denom: u4 = try math.powi(u4, 2, parsed.size); | | |
| 1811 | break :blk try math.divExact(u12, narrowed, denom); | | |
| 1812 | } | | |
| 1813 | }; | | |
| 1814 | parsed.offset = offset; | | |
| 1815 | } | | |
| 1816 | addend = null; | | |
| 1817 | }, | | |
| 1818 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { | | |
| 1819 | const RegInfo = struct { | | |
| 1820 | rd: u5, | | |
| 1821 | rn: u5, | | |
| 1822 | size: u1, | | |
| 1823 | }; | | |
| 1824 | const inst = code[off..][0..4]; | | |
| 1825 | const parsed: RegInfo = blk: { | | |
| 1826 | if (aarch64IsArithmetic(inst)) { | | |
| 1827 | const curr = mem.bytesAsValue( | | |
| 1828 | meta.TagPayload( | | |
| 1829 | aarch64.Instruction, | | |
| 1830 | aarch64.Instruction.add_subtract_immediate, | | |
| 1831 | ), | | |
| 1832 | inst, | | |
| 1833 | ); | | |
| 1834 | break :blk .{ .rd = curr.rd, .rn = curr.rn, .size = curr.sf }; | | |
| 1835 | } else { | | |
| 1836 | const curr = mem.bytesAsValue( | | |
| 1837 | meta.TagPayload( | | |
| 1838 | aarch64.Instruction, | | |
| 1839 | aarch64.Instruction.load_store_register, | | |
| 1840 | ), | | |
| 1841 | inst, | | |
| 1842 | ); | | |
| 1843 | break :blk .{ .rd = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; | | |
| 1844 | } | | |
| 1845 | }; | | |
| 1846 | const ta = if (addend) |a| target_addr + a else target_addr; | | |
| 1847 | const narrowed = @truncate(u12, ta); | | |
| 1848 | log.debug(" | rewriting TLV access to ADD opcode", .{}); | | |
| 1849 | // For TLV, we always generate an add instruction. | | |
| 1850 | mem.writeIntLittle(u32, inst, aarch64.Instruction.add( | | |
| 1851 | @intToEnum(aarch64.Register, parsed.rd), | | |
| 1852 | @intToEnum(aarch64.Register, parsed.rn), | | |
| 1853 | narrowed, | | |
| 1854 | false, | | |
| 1855 | ).toU32()); | | |
| 1856 | }, | | |
| 1857 | .ARM64_RELOC_SUBTRACTOR => { | | |
| 1858 | sub = @intCast(i64, target_addr); | | |
| 1859 | }, | | |
| 1860 | .ARM64_RELOC_UNSIGNED => { | | |
| 1861 | switch (rel.r_length) { | | |
| 1862 | 3 => { | | |
| 1863 | const inst = code[off..][0..8]; | | |
| 1864 | const offset = mem.readIntLittle(i64, inst); | | |
| 1865 | log.debug(" | calculated addend 0x{x}", .{offset}); | | |
| 1866 | const result = if (sub) |s| | | |
| 1867 | @intCast(i64, target_addr) - s + offset | | |
| 1868 | else | | |
| 1869 | @intCast(i64, target_addr) + offset; | | |
| 1870 | mem.writeIntLittle(u64, inst, @bitCast(u64, result)); | | |
| 1871 | sub = null; | | |
| 1872 | | | |
| 1873 | rebases: { | | |
| 1874 | var hit: bool = false; | | |
| 1875 | if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { | | |
| 1876 | if (self.data_section_index) |index| { | | |
| 1877 | if (index == target_mapping.target_sect_id) hit = true; | | |
| 1878 | } | | |
| 1879 | } | | |
| 1880 | if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) { | | |
| 1881 | if (self.data_const_section_index) |index| { | | |
| 1882 | if (index == target_mapping.target_sect_id) hit = true; | | |
| 1883 | } | | |
| 1884 | } | | |
| 1885 | | | |
| 1886 | if (!hit) break :rebases; | | |
| 1887 | | | |
| 1888 | try self.local_rebases.append(self.allocator, .{ | | |
| 1889 | .offset = this_addr - target_seg.inner.vmaddr, | | |
| 1890 | .segment_id = target_mapping.target_seg_id, | | |
| 1891 | }); | | |
| 1892 | } | | |
| 1893 | // TLV is handled via a separate offset mechanism. | | |
| 1894 | // Calculate the offset to the initializer. | | |
| 1895 | if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { | | |
| 1896 | assert(rel.r_extern == 1); | | |
| 1897 | const sym = object.symtab.items[rel.r_symbolnum]; | | |
| 1898 | if (isImport(&sym)) break :tlv; | | |
| 1899 | | | |
| 1900 | const base_addr = blk: { | | |
| 1901 | if (self.tlv_data_section_index) |index| { | | |
| 1902 | const tlv_data = target_seg.sections.items[index]; | | |
| 1903 | break :blk tlv_data.addr; | | |
| 1904 | } else { | | |
| 1905 | const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?]; | | |
| 1906 | break :blk tlv_bss.addr; | | |
| 1907 | } | | |
| 1908 | }; | | |
| 1909 | // Since we require TLV data to always preceed TLV bss section, we calculate | | |
| 1910 | // offsets wrt to the former if it is defined; otherwise, wrt to the latter. | | |
| 1911 | try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr); | | |
| 1912 | } | | |
| 1913 | }, | | |
| 1914 | 2 => { | | |
| 1915 | const inst = code[off..][0..4]; | | |
| 1916 | const offset = mem.readIntLittle(i32, inst); | | |
| 1917 | log.debug(" | calculated addend 0x{x}", .{offset}); | | |
| 1918 | const result = if (sub) |s| | | |
| 1919 | @intCast(i64, target_addr) - s + offset | | |
| 1920 | else | | |
| 1921 | @intCast(i64, target_addr) + offset; | | |
| 1922 | mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result))); | | |
| 1923 | sub = null; | | |
| 1924 | }, | | |
| 1925 | else => |len| { | | |
| 1926 | log.err("unexpected relocation length 0x{x}", .{len}); | | |
| 1927 | return error.UnexpectedRelocationLength; | | |
| 1928 | }, | | |
| 1929 | } | | |
| 1930 | }, | | |
| 1931 | .ARM64_RELOC_POINTER_TO_GOT => return error.TODOArm64RelocPointerToGot, | | |
| 1932 | else => unreachable, | | |
| 1933 | } | 1440 | } |
| 1934 | }, | 1441 | |
| 1935 | else => unreachable, | 1442 | if (!hit) break :rebases; |
| | 1443 | |
| | 1444 | try self.local_rebases.append(self.allocator, .{ |
| | 1445 | .offset = source_addr - target_seg.inner.vmaddr, |
| | 1446 | .segment_id = target_mapping.target_seg_id, |
| | 1447 | }); |
| | 1448 | } |
| | 1449 | // TLV is handled via a separate offset mechanism. |
| | 1450 | // Calculate the offset to the initializer. |
| | 1451 | if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { |
| | 1452 | const sym = object.symtab.items[reloc.target.symbol]; |
| | 1453 | const sym_name = object.getString(sym.inner.n_strx); |
| | 1454 | |
| | 1455 | // TODO we don't want to save offset to tlv_bootstrap |
| | 1456 | if (mem.eql(u8, sym_name, "__tlv_boostrap")) break :tlv; |
| | 1457 | |
| | 1458 | const base_addr = blk: { |
| | 1459 | if (self.tlv_data_section_index) |index| { |
| | 1460 | const tlv_data = target_seg.sections.items[index]; |
| | 1461 | break :blk tlv_data.addr; |
| | 1462 | } else { |
| | 1463 | const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?]; |
| | 1464 | break :blk tlv_bss.addr; |
| | 1465 | } |
| | 1466 | }; |
| | 1467 | // Since we require TLV data to always preceed TLV bss section, we calculate |
| | 1468 | // offsets wrt to the former if it is defined; otherwise, wrt to the latter. |
| | 1469 | try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr); |
| | 1470 | } |
| | 1471 | } else if (reloc.cast(Relocation.GotPageOff)) |page_off| { |
| | 1472 | // TODO here we need to work out the indirection to GOT. |
| | 1473 | } else { |
| | 1474 | // TODO resolve target addr. |
| 1936 | } | 1475 | } |
| | 1476 | |
| | 1477 | log.debug("{s}", .{reloc.@"type"}); |
| | 1478 | log.debug(" | offset 0x{x}", .{reloc.offset}); |
| | 1479 | log.debug(" | source address 0x{x}", .{args.source_addr}); |
| | 1480 | log.debug(" | target address 0x{x}", .{args.target_addr}); |
| | 1481 | |
| | 1482 | try reloc.resolve(args); |
| 1937 | } | 1483 | } |
| 1938 | | 1484 | |
| 1939 | log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{ | 1485 | log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{ |
| ... | @@ -1941,7 +1487,7 @@ fn doRelocs(self: *Zld) !void { | ... | @@ -1941,7 +1487,7 @@ fn doRelocs(self: *Zld) !void { |
| 1941 | sectname, | 1487 | sectname, |
| 1942 | object.name, | 1488 | object.name, |
| 1943 | target_sect_off, | 1489 | target_sect_off, |
| 1944 | target_sect_off + code.len, | 1490 | target_sect_off + sect.code.len, |
| 1945 | }); | 1491 | }); |
| 1946 | | 1492 | |
| 1947 | if (target_sect.flags == macho.S_ZEROFILL or | 1493 | if (target_sect.flags == macho.S_ZEROFILL or |
| ... | @@ -1952,15 +1498,15 @@ fn doRelocs(self: *Zld) !void { | ... | @@ -1952,15 +1498,15 @@ fn doRelocs(self: *Zld) !void { |
| 1952 | parseName(&target_sect.segname), | 1498 | parseName(&target_sect.segname), |
| 1953 | parseName(&target_sect.sectname), | 1499 | parseName(&target_sect.sectname), |
| 1954 | target_sect_off, | 1500 | target_sect_off, |
| 1955 | target_sect_off + code.len, | 1501 | target_sect_off + sect.code.len, |
| 1956 | }); | 1502 | }); |
| 1957 | // Zero-out the space | 1503 | // Zero-out the space |
| 1958 | var zeroes = try self.allocator.alloc(u8, code.len); | 1504 | var zeroes = try self.allocator.alloc(u8, sect.code.len); |
| 1959 | defer self.allocator.free(zeroes); | 1505 | defer self.allocator.free(zeroes); |
| 1960 | mem.set(u8, zeroes, 0); | 1506 | mem.set(u8, zeroes, 0); |
| 1961 | try self.file.?.pwriteAll(zeroes, target_sect_off); | 1507 | try self.file.?.pwriteAll(zeroes, target_sect_off); |
| 1962 | } else { | 1508 | } else { |
| 1963 | try self.file.?.pwriteAll(code, target_sect_off); | 1509 | try self.file.?.pwriteAll(sect.code, target_sect_off); |
| 1964 | } | 1510 | } |
| 1965 | } | 1511 | } |
| 1966 | } | 1512 | } |