| ... | ... | @@ -78,6 +78,7 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 78 | 78 | |
| 79 | 79 | threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, |
| 80 | 80 | rebases: std.ArrayListUnmanaged(Pointer) = .{}, |
| 81 | stubs: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 81 | 82 | got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, |
| 82 | 83 | |
| 83 | 84 | stub_helper_stubs_start_off: ?u64 = null, |
| ... | ... | @@ -85,11 +86,6 @@ stub_helper_stubs_start_off: ?u64 = null, |
| 85 | 86 | mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, |
| 86 | 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 | 89 | const GotEntry = struct { |
| 94 | 90 | index: u32, |
| 95 | 91 | target_addr: u64, |
| ... | ... | @@ -100,7 +96,7 @@ const MappingKey = struct { |
| 100 | 96 | source_sect_id: u16, |
| 101 | 97 | }; |
| 102 | 98 | |
| 103 | | const SectionMapping = struct { |
| 99 | pub const SectionMapping = struct { |
| 104 | 100 | source_sect_id: u16, |
| 105 | 101 | target_seg_id: u16, |
| 106 | 102 | target_sect_id: u16, |
| ... | ... | @@ -172,24 +168,30 @@ const DebugInfo = struct { |
| 172 | 168 | }; |
| 173 | 169 | |
| 174 | 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 | 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 | 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 |
| 186 | | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; |
| 174 | /// TODO this should be inferred from included libSystem.tbd or similar. |
| 175 | const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib"; |
| 187 | 176 | |
| 188 | 177 | pub fn init(allocator: *Allocator) Zld { |
| 189 | 178 | return .{ .allocator = allocator }; |
| 190 | 179 | } |
| 191 | 180 | |
| 192 | 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 | 195 | for (self.load_commands.items) |*lc| { |
| 194 | 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 | 265 | try self.populateMetadata(); |
| 264 | 266 | try self.parseInputFiles(files); |
| 265 | 267 | try self.resolveSymbols(); |
| 268 | try self.resolveStubsAndGotEntries(); |
| 266 | 269 | try self.updateMetadata(); |
| 267 | 270 | try self.sortSections(); |
| 268 | 271 | try self.allocateTextSegment(); |
| ... | ... | @@ -272,7 +275,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| 272 | 275 | try self.allocateSymbols(); |
| 273 | 276 | self.printSymtab(); |
| 274 | 277 | // try self.writeStubHelperCommon(); |
| 275 | | // try self.doRelocs(); |
| 278 | // try self.resolveRelocsAndWriteSections(); |
| 276 | 279 | // try self.flush(); |
| 277 | 280 | } |
| 278 | 281 | |
| ... | ... | @@ -814,14 +817,7 @@ fn sortSections(self: *Zld) !void { |
| 814 | 817 | |
| 815 | 818 | fn allocateTextSegment(self: *Zld) !void { |
| 816 | 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 |
| 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 | | } |
| 820 | const nstubs = @intCast(u32, self.stubs.items().len); |
| 825 | 821 | |
| 826 | 822 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; |
| 827 | 823 | seg.inner.fileoff = 0; |
| ... | ... | @@ -830,14 +826,14 @@ fn allocateTextSegment(self: *Zld) !void { |
| 830 | 826 | // Set stubs and stub_helper sizes |
| 831 | 827 | const stubs = &seg.sections.items[self.stubs_section_index.?]; |
| 832 | 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 | 831 | const stub_size: u4 = switch (self.arch.?) { |
| 836 | 832 | .x86_64 => 10, |
| 837 | 833 | .aarch64 => 3 * @sizeOf(u32), |
| 838 | 834 | else => unreachable, |
| 839 | 835 | }; |
| 840 | | stub_helper.size += nexterns * stub_size; |
| 836 | stub_helper.size += nstubs * stub_size; |
| 841 | 837 | |
| 842 | 838 | var sizeofcmds: u64 = 0; |
| 843 | 839 | for (self.load_commands.items) |lc| { |
| ... | ... | @@ -872,14 +868,7 @@ fn allocateTextSegment(self: *Zld) !void { |
| 872 | 868 | |
| 873 | 869 | fn allocateDataConstSegment(self: *Zld) !void { |
| 874 | 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 |
| 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 | | } |
| 871 | const nentries = @intCast(u32, self.got_entries.items().len); |
| 883 | 872 | |
| 884 | 873 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 885 | 874 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; |
| ... | ... | @@ -887,24 +876,14 @@ fn allocateDataConstSegment(self: *Zld) !void { |
| 887 | 876 | |
| 888 | 877 | // Set got size |
| 889 | 878 | const got = &seg.sections.items[self.got_section_index.?]; |
| 890 | | // TODO this will require scanning the relocations at least one to work out |
| 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); |
| 879 | got.size += nentries * @sizeOf(u64); |
| 894 | 880 | |
| 895 | 881 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); |
| 896 | 882 | } |
| 897 | 883 | |
| 898 | 884 | fn allocateDataSegment(self: *Zld) !void { |
| 899 | 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 |
| 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 | | } |
| 886 | const nstubs = @intCast(u32, self.stubs.items().len); |
| 908 | 887 | |
| 909 | 888 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 910 | 889 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; |
| ... | ... | @@ -913,7 +892,7 @@ fn allocateDataSegment(self: *Zld) !void { |
| 913 | 892 | // Set la_symbol_ptr and data size |
| 914 | 893 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; |
| 915 | 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 | 896 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder |
| 918 | 897 | |
| 919 | 898 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); |
| ... | ... | @@ -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 | 1393 | for (self.objects.items) |object, object_id| { |
| 1413 | 1394 | log.debug("\n\n", .{}); |
| 1414 | 1395 | log.debug("relocating object {s}", .{object.name}); |
| 1415 | 1396 | |
| 1416 | | const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; |
| 1417 | | |
| 1418 | | for (seg.sections.items) |sect, source_sect_id| { |
| 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); |
| 1397 | for (object.sections.items) |sect, source_sect_id| { |
| 1398 | const segname = parseName(&sect.inner.segname); |
| 1399 | const sectname = parseName(&sect.inner.sectname); |
| 1431 | 1400 | |
| 1432 | 1401 | // Get mapping |
| 1433 | 1402 | const target_mapping = self.mappings.get(.{ |
| ... | ... | @@ -1442,498 +1411,75 @@ fn doRelocs(self: *Zld) !void { |
| 1442 | 1411 | const target_sect_addr = target_sect.addr + target_mapping.offset; |
| 1443 | 1412 | const target_sect_off = target_sect.offset + target_mapping.offset; |
| 1444 | 1413 | |
| 1445 | | var addend: ?u64 = null; |
| 1446 | | var sub: ?i64 = null; |
| 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 | | } |
| 1414 | for (sect.relocs) |reloc| { |
| 1415 | const source_addr = target_sect_addr + reloc.offset; |
| 1474 | 1416 | |
| 1475 | | const target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel); |
| 1476 | | log.debug(" | target address 0x{x}", .{target_addr}); |
| 1477 | | if (rel.r_extern == 1) { |
| 1478 | | const target_symname = object.getString(object.symtab.items[rel.r_symbolnum].n_strx); |
| 1479 | | log.debug(" | target symbol '{s}'", .{target_symname}); |
| 1480 | | } else { |
| 1481 | | const target_sectname = seg.sections.items[rel.r_symbolnum - 1].sectname; |
| 1482 | | log.debug(" | target section '{s}'", .{parseName(&target_sectname)}); |
| 1483 | | } |
| 1417 | var args: Relocation.ResolveArgs = .{ |
| 1418 | .source_addr = source_addr, |
| 1419 | .target_addr = undefined, |
| 1420 | }; |
| 1421 | |
| 1422 | if (reloc.cast(Relocation.Unsigned)) |unsigned| { |
| 1423 | // TODO resolve target addr |
| 1484 | 1424 | |
| 1485 | | switch (self.arch.?) { |
| 1486 | | .x86_64 => { |
| 1487 | | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 1488 | | |
| 1489 | | switch (rel_type) { |
| 1490 | | .X86_64_RELOC_BRANCH => { |
| 1491 | | assert(rel.r_length == 2); |
| 1492 | | const inst = code[off..][0..4]; |
| 1493 | | const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4)); |
| 1494 | | mem.writeIntLittle(u32, inst, displacement); |
| 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 | | }, |
| 1425 | if (unsigned.subtractor) |subtractor| { |
| 1426 | args.subtractor = undefined; // TODO resolve |
| 1427 | } |
| 1428 | |
| 1429 | rebases: { |
| 1430 | var hit: bool = false; |
| 1431 | if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { |
| 1432 | if (self.data_section_index) |index| { |
| 1433 | if (index == target_mapping.target_sect_id) hit = true; |
| 1434 | } |
| 1708 | 1435 | } |
| 1709 | | }, |
| 1710 | | .aarch64 => { |
| 1711 | | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 1712 | | |
| 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, |
| 1436 | if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) { |
| 1437 | if (self.data_const_section_index) |index| { |
| 1438 | if (index == target_mapping.target_sect_id) hit = true; |
| 1439 | } |
| 1933 | 1440 | } |
| 1934 | | }, |
| 1935 | | else => unreachable, |
| 1441 | |
| 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 | 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 | 1487 | sectname, |
| 1942 | 1488 | object.name, |
| 1943 | 1489 | target_sect_off, |
| 1944 | | target_sect_off + code.len, |
| 1490 | target_sect_off + sect.code.len, |
| 1945 | 1491 | }); |
| 1946 | 1492 | |
| 1947 | 1493 | if (target_sect.flags == macho.S_ZEROFILL or |
| ... | ... | @@ -1952,15 +1498,15 @@ fn doRelocs(self: *Zld) !void { |
| 1952 | 1498 | parseName(&target_sect.segname), |
| 1953 | 1499 | parseName(&target_sect.sectname), |
| 1954 | 1500 | target_sect_off, |
| 1955 | | target_sect_off + code.len, |
| 1501 | target_sect_off + sect.code.len, |
| 1956 | 1502 | }); |
| 1957 | 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 | 1505 | defer self.allocator.free(zeroes); |
| 1960 | 1506 | mem.set(u8, zeroes, 0); |
| 1961 | 1507 | try self.file.?.pwriteAll(zeroes, target_sect_off); |
| 1962 | 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 | } |