| ... | ... | @@ -252,9 +252,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| 252 | 252 | try self.populateMetadata(); |
| 253 | 253 | try self.parseInputFiles(files); |
| 254 | 254 | try self.resolveImports(); |
| 255 | | self.allocateTextSegment(); |
| 256 | | self.allocateDataSegment(); |
| 257 | | self.allocateLinkeditSegment(); |
| 255 | try self.allocateTextSegment(); |
| 256 | try self.allocateDataSegment(); |
| 257 | try self.allocateLinkeditSegment(); |
| 258 | 258 | try self.writeStubHelperCommon(); |
| 259 | 259 | try self.resolveSymbols(); |
| 260 | 260 | try self.doRelocs(); |
| ... | ... | @@ -317,13 +317,20 @@ fn parseObjectFile(self: *Zld, object: *const Object) !void { |
| 317 | 317 | if (mem.eql(u8, sectname, "__thread_vars")) { |
| 318 | 318 | self.tlv_section_index = sect_index; |
| 319 | 319 | } |
| 320 | log.warn("{s} align 0x{x}", .{ sectname, sect.@"align" }); |
| 321 | const alignment = switch (sect.flags) { |
| 322 | macho.S_4BYTE_LITERALS => 2, |
| 323 | macho.S_8BYTE_LITERALS => 3, |
| 324 | macho.S_16BYTE_LITERALS => 4, |
| 325 | else => sect.@"align", |
| 326 | }; |
| 320 | 327 | try seg.append(self.allocator, .{ |
| 321 | 328 | .sectname = makeStaticString(&sect.sectname), |
| 322 | 329 | .segname = makeStaticString(&sect.segname), |
| 323 | 330 | .addr = 0, |
| 324 | 331 | .size = 0, |
| 325 | 332 | .offset = 0, |
| 326 | | .@"align" = sect.@"align", |
| 333 | .@"align" = alignment, |
| 327 | 334 | .reloff = 0, |
| 328 | 335 | .nreloc = 0, |
| 329 | 336 | .flags = sect.flags, |
| ... | ... | @@ -429,7 +436,7 @@ fn resolveImports(self: *Zld) !void { |
| 429 | 436 | }); |
| 430 | 437 | } |
| 431 | 438 | |
| 432 | | fn allocateTextSegment(self: *Zld) void { |
| 439 | fn allocateTextSegment(self: *Zld) !void { |
| 433 | 440 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 434 | 441 | const nexterns = @intCast(u32, self.lazy_imports.items().len); |
| 435 | 442 | |
| ... | ... | @@ -450,10 +457,16 @@ fn allocateTextSegment(self: *Zld) void { |
| 450 | 457 | sizeofcmds += lc.cmdsize(); |
| 451 | 458 | } |
| 452 | 459 | |
| 453 | | self.allocateSegment(self.text_segment_cmd_index.?, 0, sizeofcmds, true); |
| 460 | try self.allocateSegment( |
| 461 | self.text_segment_cmd_index.?, |
| 462 | 0, |
| 463 | // sizeofcmds + 10 * 4 * @sizeOf(u32), |
| 464 | 3140, |
| 465 | true, |
| 466 | ); |
| 454 | 467 | } |
| 455 | 468 | |
| 456 | | fn allocateDataSegment(self: *Zld) void { |
| 469 | fn allocateDataSegment(self: *Zld) !void { |
| 457 | 470 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 458 | 471 | const nonlazy = @intCast(u32, self.nonlazy_imports.items().len); |
| 459 | 472 | const lazy = @intCast(u32, self.lazy_imports.items().len); |
| ... | ... | @@ -470,16 +483,16 @@ fn allocateDataSegment(self: *Zld) void { |
| 470 | 483 | |
| 471 | 484 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 472 | 485 | const offset = text_seg.inner.fileoff + text_seg.inner.filesize; |
| 473 | | self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false); |
| 486 | try self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false); |
| 474 | 487 | } |
| 475 | 488 | |
| 476 | | fn allocateLinkeditSegment(self: *Zld) void { |
| 489 | fn allocateLinkeditSegment(self: *Zld) !void { |
| 477 | 490 | const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 478 | 491 | const offset = data_seg.inner.fileoff + data_seg.inner.filesize; |
| 479 | | self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false); |
| 492 | try self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false); |
| 480 | 493 | } |
| 481 | 494 | |
| 482 | | fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) void { |
| 495 | fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) !void { |
| 483 | 496 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; |
| 484 | 497 | const seg = &self.load_commands.items[index].Segment; |
| 485 | 498 | |
| ... | ... | @@ -495,23 +508,27 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: boo |
| 495 | 508 | seg.inner.filesize = aligned_size; |
| 496 | 509 | |
| 497 | 510 | // Allocate section offsets |
| 498 | | if (reverse) { |
| 499 | | var end_off: u64 = seg.inner.fileoff + seg.inner.filesize; |
| 500 | | var count: usize = seg.sections.items.len; |
| 501 | | while (count > 0) : (count -= 1) { |
| 502 | | const sec = &seg.sections.items[count - 1]; |
| 503 | | end_off -= mem.alignForwardGeneric(u64, sec.size, @sizeOf(u32)); // TODO Should we always align to 4? |
| 504 | | sec.offset = @intCast(u32, end_off); |
| 505 | | sec.addr = base_vmaddr + end_off; |
| 506 | | } |
| 507 | | } else { |
| 508 | | var next_off: u64 = seg.inner.fileoff; |
| 509 | | for (seg.sections.items) |*sect| { |
| 510 | | sect.offset = @intCast(u32, next_off); |
| 511 | | sect.addr = base_vmaddr + next_off; |
| 512 | | next_off += mem.alignForwardGeneric(u64, sect.size, @sizeOf(u32)); // TODO Should we always align to 4? |
| 513 | | } |
| 514 | | } |
| 511 | // if (reverse) { |
| 512 | // var end_off: u64 = seg.inner.fileoff + seg.inner.filesize; |
| 513 | // var count: usize = seg.sections.items.len; |
| 514 | // while (count > 0) : (count -= 1) { |
| 515 | // const sec = &seg.sections.items[count - 1]; |
| 516 | // const alignment = math.max(@alignOf(u32), try std.math.powi(u32, 2, sec.@"align")); |
| 517 | // log.warn("{s} 0x{x} alignment = 0x{x}", .{ parseName(&sec.sectname), sec.@"align", alignment }); |
| 518 | // end_off -= mem.alignForwardGeneric(u64, sec.size, alignment); |
| 519 | // sec.offset = @intCast(u32, end_off); |
| 520 | // sec.addr = base_vmaddr + end_off; |
| 521 | // } |
| 522 | // } else { |
| 523 | var next_off: u64 = seg.inner.fileoff + start; |
| 524 | for (seg.sections.items) |*sect| { |
| 525 | const alignment = math.max(@alignOf(u32), try std.math.powi(u32, 2, sect.@"align")); |
| 526 | log.warn("{s} 0x{x} alignment = 0x{x}", .{ parseName(&sect.sectname), sect.@"align", alignment }); |
| 527 | sect.offset = @intCast(u32, next_off); |
| 528 | sect.addr = base_vmaddr + next_off; |
| 529 | next_off += mem.alignForwardGeneric(u64, sect.size, alignment); |
| 530 | } |
| 531 | // } |
| 515 | 532 | } |
| 516 | 533 | |
| 517 | 534 | fn writeStubHelperCommon(self: *Zld) !void { |
| ... | ... | @@ -552,33 +569,48 @@ fn writeStubHelperCommon(self: *Zld) !void { |
| 552 | 569 | break :blk stub_helper.offset + code_size; |
| 553 | 570 | }, |
| 554 | 571 | .aarch64 => { |
| 555 | | var code: [4 * @sizeOf(u32)]u8 = undefined; |
| 572 | var code: [6 * @sizeOf(u32)]u8 = undefined; |
| 556 | 573 | { |
| 557 | 574 | const target_addr = data.addr + data.size - @sizeOf(u64); |
| 558 | 575 | const displacement = @bitCast(u21, try math.cast(i21, target_addr - stub_helper.addr)); |
| 559 | 576 | // adr x17, disp |
| 560 | 577 | mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, displacement).toU32()); |
| 578 | // TODO check if adr is enough and expand into adrp + add if not. |
| 579 | // nop in case we need to expand adr for adrp followed by add. |
| 580 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); |
| 561 | 581 | } |
| 562 | 582 | // stp x16, x17, [sp, #-16]! |
| 563 | | code[4] = 0xf0; |
| 564 | | code[5] = 0x47; |
| 565 | | code[6] = 0xbf; |
| 566 | | code[7] = 0xa9; |
| 567 | | { |
| 583 | code[8] = 0xf0; |
| 584 | code[9] = 0x47; |
| 585 | code[10] = 0xbf; |
| 586 | code[11] = 0xa9; |
| 587 | binder: { |
| 568 | 588 | const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?; |
| 569 | 589 | const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64)); |
| 570 | | const displacement = try math.divExact(u64, addr - stub_helper.addr - 2 * @sizeOf(u32), 4); |
| 571 | | const literal = try math.cast(u19, displacement); |
| 590 | const displacement = math.divExact(u64, addr - stub_helper.addr - 3 * @sizeOf(u32), 4) catch |_| { |
| 591 | log.warn("0x{x}", .{addr - stub_helper.addr - 3 * @sizeOf(u32)}); |
| 592 | // Pad with nop to please division. |
| 593 | // nop |
| 594 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); |
| 595 | // ldr x16, label |
| 596 | const disp = try math.divExact(u64, addr - stub_helper.addr - 4 * @sizeOf(u32), 4); |
| 597 | const literal = try math.cast(u19, disp); // TODO use adrp + add if we exceed the range. |
| 598 | mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32()); |
| 599 | break :binder; |
| 600 | }; |
| 601 | const literal = try math.cast(u19, displacement); // TODO use adrp + add if we exceed the range. |
| 572 | 602 | // ldr x16, label |
| 573 | | mem.writeIntLittle(u32, code[8..12], Arm64.ldr(16, literal, 1).toU32()); |
| 603 | mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32()); |
| 604 | // nop |
| 605 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); |
| 574 | 606 | } |
| 575 | 607 | // br x16 |
| 576 | | code[12] = 0x00; |
| 577 | | code[13] = 0x02; |
| 578 | | code[14] = 0x1f; |
| 579 | | code[15] = 0xd6; |
| 608 | code[20] = 0x00; |
| 609 | code[21] = 0x02; |
| 610 | code[22] = 0x1f; |
| 611 | code[23] = 0xd6; |
| 580 | 612 | try self.file.?.pwriteAll(&code, stub_helper.offset); |
| 581 | | break :blk stub_helper.offset + 4 * @sizeOf(u32); |
| 613 | break :blk stub_helper.offset + 6 * @sizeOf(u32); |
| 582 | 614 | }, |
| 583 | 615 | else => unreachable, |
| 584 | 616 | } |
| ... | ... | @@ -635,12 +667,14 @@ fn writeStub(self: *Zld, index: u32) !void { |
| 635 | 667 | }, |
| 636 | 668 | .aarch64 => { |
| 637 | 669 | assert(la_ptr_addr >= stub_addr); |
| 638 | | const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4); |
| 670 | const displacement = try math.divExact(u64, la_ptr_addr - stub_addr - @sizeOf(u32), 4); |
| 639 | 671 | const literal = try math.cast(u19, displacement); |
| 672 | // nop |
| 673 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); |
| 640 | 674 | // ldr x16, literal |
| 641 | | mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 1).toU32()); |
| 675 | mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32()); |
| 642 | 676 | // br x16 |
| 643 | | mem.writeIntLittle(u32, code[4..8], Arm64.br(16).toU32()); |
| 677 | mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32()); |
| 644 | 678 | }, |
| 645 | 679 | else => unreachable, |
| 646 | 680 | } |
| ... | ... | @@ -722,7 +756,8 @@ fn resolveSymbols(self: *Zld) !void { |
| 722 | 756 | const sym_name = object.getString(sym.n_strx); |
| 723 | 757 | |
| 724 | 758 | if (isLocal(&sym) and self.locals.get(sym_name) != null) { |
| 725 | | log.warn("symbol '{s}' already exists; skipping", .{sym_name}); |
| 759 | log.warn("local symbol '{s}' defined multiple times; removing", .{sym_name}); |
| 760 | self.locals.swapRemoveAssertDiscard(sym_name); |
| 726 | 761 | continue; |
| 727 | 762 | } |
| 728 | 763 | |
| ... | ... | @@ -978,12 +1013,6 @@ fn doRelocs(self: *Zld) !void { |
| 978 | 1013 | const this_page = @intCast(i32, this_addr >> 12); |
| 979 | 1014 | const target_page = @intCast(i32, ta >> 12); |
| 980 | 1015 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); |
| 981 | | if (pages == 0) { |
| 982 | | // No need to execute adrp. Instead, replace with a nop. |
| 983 | | log.warn(" | replacing ADRP with NOP", .{}); |
| 984 | | mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32()); |
| 985 | | continue; |
| 986 | | } |
| 987 | 1016 | log.warn(" | moving by {} pages", .{pages}); |
| 988 | 1017 | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Address), inst); |
| 989 | 1018 | parsed.immhi = @truncate(u19, pages >> 2); |
| ... | ... | @@ -1000,12 +1029,6 @@ fn doRelocs(self: *Zld) !void { |
| 1000 | 1029 | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); |
| 1001 | 1030 | const ta = if (addend) |a| target_addr + a else target_addr; |
| 1002 | 1031 | const narrowed = @truncate(u12, ta); |
| 1003 | | if (narrowed == 0) { |
| 1004 | | // No need to execute add. Instead, replace with a nop. |
| 1005 | | log.warn(" | replacing ADD with NOP", .{}); |
| 1006 | | mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32()); |
| 1007 | | continue; |
| 1008 | | } |
| 1009 | 1032 | parsed.offset = narrowed; |
| 1010 | 1033 | } else { |
| 1011 | 1034 | log.warn(" | detected LDR/STR opcode", .{}); |
| ... | ... | @@ -1013,20 +1036,18 @@ fn doRelocs(self: *Zld) !void { |
| 1013 | 1036 | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); |
| 1014 | 1037 | const ta = if (addend) |a| target_addr + a else target_addr; |
| 1015 | 1038 | const narrowed = @truncate(u12, ta); |
| 1016 | | if (narrowed == 0) { |
| 1017 | | // No need to execute ldr/str. Instead, replace with a nop. |
| 1018 | | log.warn(" | replacing LDR/STR with NOP", .{}); |
| 1019 | | mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32()); |
| 1020 | | continue; |
| 1021 | | } |
| 1022 | | const denom: u12 = if (parsed.size == 1) 8 else 4; |
| 1023 | | const offset = math.divExact(u12, narrowed, denom) catch |_| { |
| 1024 | | // If we are here, then this means we are not able to divide the offset |
| 1025 | | // exactly by the required denominator. Therefore, we will use add instead of |
| 1026 | | // ldr as we expect ldr to follow this instruction nonetheless. |
| 1027 | | // TODO I believe ldr/str can only occur for GOT_LOAD_PAGEOFF12. |
| 1028 | | mem.writeIntLittle(u32, inst, Arm64.add(parsed.rn, parsed.rn, narrowed, parsed.size).toU32()); |
| 1029 | | continue; |
| 1039 | const offset: u12 = blk: { |
| 1040 | if (parsed.size == 0) { |
| 1041 | break :blk narrowed; |
| 1042 | } else { |
| 1043 | const denom: u4 = try math.powi(u4, 2, parsed.size); |
| 1044 | const offf = math.divExact(u12, narrowed, denom) catch |_| { |
| 1045 | log.warn(" | narrowed 0x{x}", .{narrowed}); |
| 1046 | log.warn(" | denom 0x{x}", .{denom}); |
| 1047 | continue; |
| 1048 | }; |
| 1049 | break :blk offf; |
| 1050 | } |
| 1030 | 1051 | }; |
| 1031 | 1052 | parsed.offset = offset; |
| 1032 | 1053 | } |
| ... | ... | @@ -1046,7 +1067,7 @@ fn doRelocs(self: *Zld) !void { |
| 1046 | 1067 | break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size }; |
| 1047 | 1068 | } else { |
| 1048 | 1069 | const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); |
| 1049 | | break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size }; |
| 1070 | break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; |
| 1050 | 1071 | } |
| 1051 | 1072 | }; |
| 1052 | 1073 | const ta = if (addend) |a| target_addr + a else target_addr; |
| ... | ... | @@ -1145,6 +1166,7 @@ fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_ |
| 1145 | 1166 | .segname = source_sect.segname, |
| 1146 | 1167 | .sectname = source_sect.sectname, |
| 1147 | 1168 | }).?; |
| 1169 | log.warn(" | symbol local to object", .{}); |
| 1148 | 1170 | break :blk target_space.address + sym.n_value - source_sect.addr; |
| 1149 | 1171 | } else if (isImport(&sym)) { |
| 1150 | 1172 | // Relocate to either the artifact's local symbol, or an import from |
| ... | ... | @@ -1267,7 +1289,7 @@ fn populateMetadata(self: *Zld) !void { |
| 1267 | 1289 | }; |
| 1268 | 1290 | const stub_size: u4 = switch (self.arch.?) { |
| 1269 | 1291 | .x86_64 => 6, |
| 1270 | | .aarch64 => 2 * @sizeOf(u32), |
| 1292 | .aarch64 => 3 * @sizeOf(u32), |
| 1271 | 1293 | else => unreachable, // unhandled architecture type |
| 1272 | 1294 | }; |
| 1273 | 1295 | try text_seg.append(self.allocator, .{ |
| ... | ... | @@ -1298,7 +1320,7 @@ fn populateMetadata(self: *Zld) !void { |
| 1298 | 1320 | .aarch64 => 2, |
| 1299 | 1321 | else => unreachable, // unhandled architecture type |
| 1300 | 1322 | }; |
| 1301 | | const stub_helper_size: u5 = switch (self.arch.?) { |
| 1323 | const stub_helper_size: u6 = switch (self.arch.?) { |
| 1302 | 1324 | .x86_64 => 15, |
| 1303 | 1325 | .aarch64 => 6 * @sizeOf(u32), |
| 1304 | 1326 | else => unreachable, |