authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-18 19:14:17+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-18 19:14:17+01:00
log17c066e9257d539cc1779eaaa9ad2679ec6b8881
tree032197b5d21cc22b1ac4ebc28c65fb5cdf592c12
parentb54514d9dd15225ef2578b33c4c384db4680b90b
parentf3b4f79c7fb2aafd2812c077469121f27d9f0018
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8282 from kubkon/zld

macho: upstream zld linker

10 files changed, 3794 insertions(+), 306 deletions(-)

CMakeLists.txt+7
...@@ -564,7 +564,14 @@ set(ZIG_STAGE2_SOURCES...@@ -564,7 +564,14 @@ set(ZIG_STAGE2_SOURCES
564 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"564 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
565 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"565 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
566 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"566 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
567 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
568 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
569 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
570 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
567 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"571 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
572 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
573 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
568 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"575 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
569 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"576 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
570 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"577 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
lib/std/debug.zig-18
...@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
250 resetSegfaultHandler();250 resetSegfaultHandler();
251 }251 }
252252
253 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64)
254 nosuspend {
255 // As a workaround for not having threadlocal variable support in LLD for this target,
256 // we have a simpler panic implementation that does not use threadlocal variables.
257 // TODO https://github.com/ziglang/zig/issues/7527
258 const stderr = io.getStdErr().writer();
259 if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) {
260 stderr.print("panic: " ++ format ++ "\n", args) catch os.abort();
261 if (trace) |t| {
262 dumpStackTrace(t.*);
263 }
264 dumpCurrentStackTrace(first_trace_addr);
265 } else {
266 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
267 }
268 os.abort();
269 };
270
271 nosuspend switch (panic_stage) {253 nosuspend switch (panic_stage) {
272 0 => {254 0 => {
273 panic_stage = 1;255 panic_stage = 1;
lib/std/macho.zig+32
...@@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;...@@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;
1227/// section has local relocation entries1227/// section has local relocation entries
1228pub const S_ATTR_LOC_RELOC = 0x100;1228pub const S_ATTR_LOC_RELOC = 0x100;
12291229
1230/// template of initial values for TLVs
1231pub const S_THREAD_LOCAL_REGULAR = 0x11;
1232
1233/// template of initial values for TLVs
1234pub const S_THREAD_LOCAL_ZEROFILL = 0x12;
1235
1236/// TLV descriptors
1237pub const S_THREAD_LOCAL_VARIABLES = 0x13;
1238
1239/// pointers to TLV descriptors
1240pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
1241
1242/// functions to call to initialize TLV values
1243pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
1244
1245/// 32-bit offsets to initializers
1246pub const S_INIT_FUNC_OFFSETS = 0x16;
1247
1230pub const cpu_type_t = integer_t;1248pub const cpu_type_t = integer_t;
1231pub const cpu_subtype_t = integer_t;1249pub const cpu_subtype_t = integer_t;
1232pub const integer_t = c_int;1250pub const integer_t = c_int;
...@@ -1597,3 +1615,17 @@ pub const GenericBlob = extern struct {...@@ -1597,3 +1615,17 @@ pub const GenericBlob = extern struct {
1597 /// Total length of blob1615 /// Total length of blob
1598 length: u32,1616 length: u32,
1599};1617};
1618
1619/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
1620/// to point to an array of data_in_code_entry entries. Each entry
1621/// describes a range of data in a code section.
1622pub const data_in_code_entry = extern struct {
1623 /// From mach_header to start of data range.
1624 offset: u32,
1625
1626 /// Number of bytes in data range.
1627 length: u16,
1628
1629 /// A DICE_KIND value.
1630 kind: u16,
1631};
src/codegen/aarch64.zig+4-1
...@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {...@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {
221 offset: u12,221 offset: u12,
222 opc: u2,222 opc: u2,
223 op1: u2,223 op1: u2,
224 fixed: u4 = 0b111_0,224 v: u1,
225 fixed: u3 = 0b111,
225 size: u2,226 size: u2,
226 },227 },
227 LoadStorePairOfRegisters: packed struct {228 LoadStorePairOfRegisters: packed struct {
...@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {...@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {
505 .offset = offset.toU12(),506 .offset = offset.toU12(),
506 .opc = opc,507 .opc = opc,
507 .op1 = op1,508 .op1 = op1,
509 .v = 0,
508 .size = 0b10,510 .size = 0b10,
509 },511 },
510 };512 };
...@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {...@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {
517 .offset = offset.toU12(),519 .offset = offset.toU12(),
518 .opc = opc,520 .opc = opc,
519 .op1 = op1,521 .op1 = op1,
522 .v = 0,
520 .size = 0b11,523 .size = 0b11,
521 },524 },
522 };525 };
src/link/MachO.zig+69-285
...@@ -26,6 +26,7 @@ const target_util = @import("../target.zig");...@@ -26,6 +26,7 @@ const target_util = @import("../target.zig");
26const DebugSymbols = @import("MachO/DebugSymbols.zig");26const DebugSymbols = @import("MachO/DebugSymbols.zig");
27const Trie = @import("MachO/Trie.zig");27const Trie = @import("MachO/Trie.zig");
28const CodeSignature = @import("MachO/CodeSignature.zig");28const CodeSignature = @import("MachO/CodeSignature.zig");
29const Zld = @import("MachO/Zld.zig");
2930
30usingnamespace @import("MachO/commands.zig");31usingnamespace @import("MachO/commands.zig");
3132
...@@ -632,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -632,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
632 if (!mem.eql(u8, the_object_path, full_out_path)) {633 if (!mem.eql(u8, the_object_path, full_out_path)) {
633 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});634 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
634 }635 }
635 } else {636 } else outer: {
637 const use_zld = blk: {
638 if (self.base.options.is_native_os and self.base.options.system_linker_hack) {
639 // If the user forces the use of ld64, make sure we are running native!
640 break :blk false;
641 }
642
643 if (self.base.options.target.cpu.arch == .aarch64) {
644 // On aarch64, always use zld.
645 break :blk true;
646 }
647
648 if (self.base.options.link_libcpp or
649 self.base.options.output_mode == .Lib or
650 self.base.options.linker_script != null)
651 {
652 // Fallback to LLD in this handful of cases on x86_64 only.
653 break :blk false;
654 }
655
656 break :blk true;
657 };
658
659 if (use_zld) {
660 var zld = Zld.init(self.base.allocator);
661 defer zld.deinit();
662 zld.arch = target.cpu.arch;
663
664 var input_files = std.ArrayList([]const u8).init(self.base.allocator);
665 defer input_files.deinit();
666 // Positional arguments to the linker such as object files.
667 try input_files.appendSlice(self.base.options.objects);
668 for (comp.c_object_table.items()) |entry| {
669 try input_files.append(entry.key.status.success.object_path);
670 }
671 if (module_obj_path) |p| {
672 try input_files.append(p);
673 }
674 try input_files.append(comp.compiler_rt_static_lib.?.full_object_path);
675 // libc++ dep
676 if (self.base.options.link_libcpp) {
677 try input_files.append(comp.libcxxabi_static_lib.?.full_object_path);
678 try input_files.append(comp.libcxx_static_lib.?.full_object_path);
679 }
680
681 if (self.base.options.verbose_link) {
682 var argv = std.ArrayList([]const u8).init(self.base.allocator);
683 defer argv.deinit();
684
685 try argv.append("zig");
686 try argv.append("ld");
687
688 try argv.ensureCapacity(input_files.items.len);
689 for (input_files.items) |f| {
690 argv.appendAssumeCapacity(f);
691 }
692
693 try argv.append("-o");
694 try argv.append(full_out_path);
695
696 Compilation.dump_argv(argv.items);
697 }
698
699 try zld.link(input_files.items, full_out_path);
700
701 break :outer;
702 }
703
636 // Create an LLD command line and invoke it.704 // Create an LLD command line and invoke it.
637 var argv = std.ArrayList([]const u8).init(self.base.allocator);705 var argv = std.ArrayList([]const u8).init(self.base.allocator);
638 defer argv.deinit();706 defer argv.deinit();
...@@ -903,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -903,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
903 log.warn("unexpected LLD stderr:\n{s}", .{stderr});971 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
904 }972 }
905 }973 }
906
907 // At this stage, LLD has done its job. It is time to patch the resultant
908 // binaries up!
909 const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true });
910 try self.parseFromFile(out_file);
911
912 if (self.libsystem_cmd_index == null and self.header.?.filetype == macho.MH_EXECUTE) {
913 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
914 const text_section = text_segment.sections.items[self.text_section_index.?];
915 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
916 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
917
918 if (needed_size + after_last_cmd_offset > text_section.offset) {
919 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
920 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
921 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
922 return error.NotEnoughPadding;
923 }
924
925 // Calculate next available dylib ordinal.
926 const next_ordinal = blk: {
927 var ordinal: u32 = 1;
928 for (self.load_commands.items) |cmd| {
929 switch (cmd) {
930 .Dylib => ordinal += 1,
931 else => {},
932 }
933 }
934 break :blk ordinal;
935 };
936
937 // Add load dylib load command
938 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
939 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
940 u64,
941 @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
942 @sizeOf(u64),
943 ));
944 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
945 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
946 const min_version = 0x0;
947 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
948 .cmd = macho.LC_LOAD_DYLIB,
949 .cmdsize = cmdsize,
950 .dylib = .{
951 .name = @sizeOf(macho.dylib_command),
952 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
953 .current_version = min_version,
954 .compatibility_version = min_version,
955 },
956 });
957 dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
958 mem.set(u8, dylib_cmd.data, 0);
959 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
960 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
961 self.header_dirty = true;
962 self.load_commands_dirty = true;
963
964 if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {
965 log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
966 log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
967 return error.NoSymbolTableFound;
968 }
969
970 // Patch dyld info
971 try self.fixupBindInfo(next_ordinal);
972 try self.fixupLazyBindInfo(next_ordinal);
973
974 // Write updated load commands and the header
975 try self.writeLoadCommands();
976 try self.writeHeader();
977
978 assert(!self.header_dirty);
979 assert(!self.load_commands_dirty);
980 }
981 if (self.code_signature_cmd_index == null) outer: {
982 if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets.
983 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
984 const text_section = text_segment.sections.items[self.text_section_index.?];
985 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
986 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
987
988 if (needed_size + after_last_cmd_offset > text_section.offset) {
989 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
990 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
991 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
992 return error.NotEnoughPadding;
993 }
994
995 // Add code signature load command
996 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
997 try self.load_commands.append(self.base.allocator, .{
998 .LinkeditData = .{
999 .cmd = macho.LC_CODE_SIGNATURE,
1000 .cmdsize = @sizeOf(macho.linkedit_data_command),
1001 .dataoff = 0,
1002 .datasize = 0,
1003 },
1004 });
1005 self.header_dirty = true;
1006 self.load_commands_dirty = true;
1007
1008 // Pad out space for code signature
1009 try self.writeCodeSignaturePadding();
1010 // Write updated load commands and the header
1011 try self.writeLoadCommands();
1012 try self.writeHeader();
1013 // Generate adhoc code signature
1014 try self.writeCodeSignature();
1015
1016 assert(!self.header_dirty);
1017 assert(!self.load_commands_dirty);
1018 }
1019 }974 }
1020 }975 }
1021976
...@@ -3331,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {...@@ -3331,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {
3331 self.header_dirty = false;3286 self.header_dirty = false;
3332}3287}
33333288
3334/// Parse MachO contents from existing binary file.
3335fn parseFromFile(self: *MachO, file: fs.File) !void {
3336 self.base.file = file;
3337 var reader = file.reader();
3338 const header = try reader.readStruct(macho.mach_header_64);
3339 try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds);
3340 var i: u16 = 0;
3341 while (i < header.ncmds) : (i += 1) {
3342 const cmd = try LoadCommand.read(self.base.allocator, reader);
3343 switch (cmd.cmd()) {
3344 macho.LC_SEGMENT_64 => {
3345 const x = cmd.Segment;
3346 if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) {
3347 self.pagezero_segment_cmd_index = i;
3348 } else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) {
3349 self.linkedit_segment_cmd_index = i;
3350 } else if (parseAndCmpName(&x.inner.segname, "__TEXT")) {
3351 self.text_segment_cmd_index = i;
3352 for (x.sections.items) |sect, j| {
3353 if (parseAndCmpName(&sect.sectname, "__text")) {
3354 self.text_section_index = @intCast(u16, j);
3355 }
3356 }
3357 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
3358 self.data_segment_cmd_index = i;
3359 } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) {
3360 self.data_const_segment_cmd_index = i;
3361 }
3362 },
3363 macho.LC_DYLD_INFO_ONLY => {
3364 self.dyld_info_cmd_index = i;
3365 },
3366 macho.LC_SYMTAB => {
3367 self.symtab_cmd_index = i;
3368 },
3369 macho.LC_DYSYMTAB => {
3370 self.dysymtab_cmd_index = i;
3371 },
3372 macho.LC_LOAD_DYLINKER => {
3373 self.dylinker_cmd_index = i;
3374 },
3375 macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => {
3376 self.version_min_cmd_index = i;
3377 },
3378 macho.LC_SOURCE_VERSION => {
3379 self.source_version_cmd_index = i;
3380 },
3381 macho.LC_UUID => {
3382 self.uuid_cmd_index = i;
3383 },
3384 macho.LC_MAIN => {
3385 self.main_cmd_index = i;
3386 },
3387 macho.LC_LOAD_DYLIB => {
3388 const x = cmd.Dylib;
3389 if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) {
3390 self.libsystem_cmd_index = i;
3391 }
3392 },
3393 macho.LC_FUNCTION_STARTS => {
3394 self.function_starts_cmd_index = i;
3395 },
3396 macho.LC_DATA_IN_CODE => {
3397 self.data_in_code_cmd_index = i;
3398 },
3399 macho.LC_CODE_SIGNATURE => {
3400 self.code_signature_cmd_index = i;
3401 },
3402 else => {
3403 log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
3404 },
3405 }
3406 self.load_commands.appendAssumeCapacity(cmd);
3407 }
3408 self.header = header;
3409}
3410
3411fn parseAndCmpName(name: []const u8, needle: []const u8) bool {
3412 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3413 return mem.eql(u8, name[0..len], needle);
3414}
3415
3416fn parseSymbolTable(self: *MachO) !void {
3417 const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3418 const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
3419
3420 var buffer = try self.base.allocator.alloc(macho.nlist_64, symtab.nsyms);
3421 defer self.base.allocator.free(buffer);
3422 const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);
3423 assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);
3424
3425 try self.locals.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
3426 try self.globals.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
3427 try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);
3428
3429 self.locals.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
3430 self.globals.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
3431 self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);
3432}
3433
3434fn parseStringTable(self: *MachO) !void {
3435 const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3436
3437 var buffer = try self.base.allocator.alloc(u8, symtab.strsize);
3438 defer self.base.allocator.free(buffer);
3439 const nread = try self.base.file.?.preadAll(buffer, symtab.stroff);
3440 assert(nread == buffer.len);
3441
3442 try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize);
3443 self.string_table.appendSliceAssumeCapacity(buffer);
3444}
3445
3446fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void {
3447 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3448 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
3449 defer self.base.allocator.free(buffer);
3450 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
3451 assert(nread == buffer.len);
3452 try self.fixupInfoCommon(buffer, dylib_ordinal);
3453 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
3454}
3455
3456fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void {
3457 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3458 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
3459 defer self.base.allocator.free(buffer);
3460 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
3461 assert(nread == buffer.len);
3462 try self.fixupInfoCommon(buffer, dylib_ordinal);
3463 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
3464}
3465
3466fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {
3467 var stream = std.io.fixedBufferStream(buffer);
3468 var reader = stream.reader();
3469
3470 while (true) {
3471 const inst = reader.readByte() catch |err| switch (err) {
3472 error.EndOfStream => break,
3473 else => return err,
3474 };
3475 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
3476 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
3477
3478 switch (opcode) {
3479 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
3480 var next = try reader.readByte();
3481 while (next != @as(u8, 0)) {
3482 next = try reader.readByte();
3483 }
3484 },
3485 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
3486 _ = try std.leb.readULEB128(u64, reader);
3487 },
3488 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
3489 // Perform the fixup.
3490 try stream.seekBy(-1);
3491 var writer = stream.writer();
3492 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal));
3493 },
3494 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
3495 _ = try std.leb.readULEB128(u64, reader);
3496 },
3497 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
3498 _ = try std.leb.readILEB128(i64, reader);
3499 },
3500 else => {},
3501 }
3502 }
3503}
3504
3505pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {3289pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3506 // TODO https://github.com/ziglang/zig/issues/12843290 // TODO https://github.com/ziglang/zig/issues/1284
3507 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch3291 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
src/link/MachO/Archive.zig created+256
...@@ -0,0 +1,256 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.archive);
7const macho = std.macho;
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11const Object = @import("Object.zig");
12const parseName = @import("Zld.zig").parseName;
13
14usingnamespace @import("commands.zig");
15
16allocator: *Allocator,
17file: fs.File,
18header: ar_hdr,
19name: []u8,
20
21objects: std.ArrayListUnmanaged(Object) = .{},
22
23// Archive files start with the ARMAG identifying string. Then follows a
24// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
25// member indicates, for each member file.
26/// String that begins an archive file.
27const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
28/// Size of that string.
29const SARMAG: u4 = 8;
30
31/// String in ar_fmag at the end of each header.
32const ARFMAG: *const [2:0]u8 = "`\n";
33
34const ar_hdr = extern struct {
35 /// Member file name, sometimes / terminated.
36 ar_name: [16]u8,
37
38 /// File date, decimal seconds since Epoch.
39 ar_date: [12]u8,
40
41 /// User ID, in ASCII format.
42 ar_uid: [6]u8,
43
44 /// Group ID, in ASCII format.
45 ar_gid: [6]u8,
46
47 /// File mode, in ASCII octal.
48 ar_mode: [8]u8,
49
50 /// File size, in ASCII decimal.
51 ar_size: [10]u8,
52
53 /// Always contains ARFMAG.
54 ar_fmag: [2]u8,
55
56 const NameOrLength = union(enum) {
57 Name: []const u8,
58 Length: u64,
59 };
60 pub fn nameOrLength(self: ar_hdr) !NameOrLength {
61 const value = getValue(&self.ar_name);
62 const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive;
63 const len = value.len;
64 if (slash_index == len - 1) {
65 // Name stored directly
66 return NameOrLength{ .Name = value };
67 } else {
68 // Name follows the header directly and its length is encoded in
69 // the name field.
70 const length = try std.fmt.parseInt(u64, value[slash_index + 1 ..], 10);
71 return NameOrLength{ .Length = length };
72 }
73 }
74
75 pub fn size(self: ar_hdr) !u64 {
76 const value = getValue(&self.ar_size);
77 return std.fmt.parseInt(u64, value, 10);
78 }
79
80 fn getValue(raw: []const u8) []const u8 {
81 return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)});
82 }
83};
84
85pub fn deinit(self: *Archive) void {
86 self.allocator.free(self.name);
87 for (self.objects.items) |*object| {
88 object.deinit();
89 }
90 self.objects.deinit(self.allocator);
91 self.file.close();
92}
93
94/// Caller owns the returned Archive instance and is responsible for calling
95/// `deinit` to free allocated memory.
96pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: []const u8, file: fs.File) !Archive {
97 var reader = file.reader();
98 var magic = try readMagic(allocator, reader);
99 defer allocator.free(magic);
100
101 if (!mem.eql(u8, magic, ARMAG)) {
102 // Reset file cursor.
103 try file.seekTo(0);
104 return error.NotArchive;
105 }
106
107 const header = try reader.readStruct(ar_hdr);
108
109 if (!mem.eql(u8, &header.ar_fmag, ARFMAG))
110 return error.MalformedArchive;
111
112 var embedded_name = try getName(allocator, header, reader);
113 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, ar_name });
114 defer allocator.free(embedded_name);
115
116 var name = try allocator.dupe(u8, ar_name);
117 var self = Archive{
118 .allocator = allocator,
119 .file = file,
120 .header = header,
121 .name = name,
122 };
123
124 var object_offsets = try self.readTableOfContents(reader);
125 defer self.allocator.free(object_offsets);
126
127 var i: usize = 1;
128 while (i < object_offsets.len) : (i += 1) {
129 const offset = object_offsets[i];
130 try reader.context.seekTo(offset);
131 try self.readObject(arch, ar_name, reader);
132 }
133
134 return self;
135}
136
137fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
138 const symtab_size = try reader.readIntLittle(u32);
139 var symtab = try self.allocator.alloc(u8, symtab_size);
140 defer self.allocator.free(symtab);
141 try reader.readNoEof(symtab);
142
143 const strtab_size = try reader.readIntLittle(u32);
144 var strtab = try self.allocator.alloc(u8, strtab_size);
145 defer self.allocator.free(strtab);
146 try reader.readNoEof(strtab);
147
148 var symtab_stream = std.io.fixedBufferStream(symtab);
149 var symtab_reader = symtab_stream.reader();
150
151 var object_offsets = std.ArrayList(u32).init(self.allocator);
152 try object_offsets.append(0);
153 var last: usize = 0;
154
155 while (true) {
156 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
157 error.EndOfStream => break,
158 else => |e| return e,
159 };
160 const object_offset = try symtab_reader.readIntLittle(u32);
161
162 // TODO Store the table of contents for later reuse.
163
164 // Here, we assume that symbols are NOT sorted in any way, and
165 // they point to objects in sequence.
166 if (object_offsets.items[last] != object_offset) {
167 try object_offsets.append(object_offset);
168 last += 1;
169 }
170 }
171
172 return object_offsets.toOwnedSlice();
173}
174
175fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {
176 const object_header = try reader.readStruct(ar_hdr);
177
178 if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG))
179 return error.MalformedArchive;
180
181 var object_name = try getName(self.allocator, object_header, reader);
182 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
183
184 const offset = @intCast(u32, try reader.context.getPos());
185 const header = try reader.readStruct(macho.mach_header_64);
186
187 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
188 macho.CPU_TYPE_ARM64 => .aarch64,
189 macho.CPU_TYPE_X86_64 => .x86_64,
190 else => |value| {
191 log.err("unsupported cpu architecture 0x{x}", .{value});
192 return error.UnsupportedCpuArchitecture;
193 },
194 };
195 if (this_arch != arch) {
196 log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
197 return error.MismatchedCpuArchitecture;
198 }
199
200 // TODO Implement std.fs.File.clone() or similar.
201 var new_file = try fs.cwd().openFile(ar_name, .{});
202 var object = Object{
203 .allocator = self.allocator,
204 .name = object_name,
205 .ar_name = try mem.dupe(self.allocator, u8, ar_name),
206 .file = new_file,
207 .header = header,
208 };
209
210 try object.readLoadCommands(reader, .{ .offset = offset });
211
212 if (object.symtab_cmd_index != null) {
213 try object.readSymtab();
214 try object.readStrtab();
215 }
216
217 if (object.data_in_code_cmd_index != null) try object.readDataInCode();
218
219 log.debug("\n\n", .{});
220 log.debug("{s} defines symbols", .{object.name});
221 for (object.symtab.items) |sym| {
222 const symname = object.getString(sym.n_strx);
223 log.debug("'{s}': {}", .{ symname, sym });
224 }
225
226 try self.objects.append(self.allocator, object);
227}
228
229fn readMagic(allocator: *Allocator, reader: anytype) ![]u8 {
230 var magic = std.ArrayList(u8).init(allocator);
231 try magic.ensureCapacity(SARMAG);
232 var i: usize = 0;
233 while (i < SARMAG) : (i += 1) {
234 const next = try reader.readByte();
235 magic.appendAssumeCapacity(next);
236 }
237 return magic.toOwnedSlice();
238}
239
240fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
241 const name_or_length = try header.nameOrLength();
242 var name: []u8 = undefined;
243 switch (name_or_length) {
244 .Name => |n| {
245 name = try allocator.dupe(u8, n);
246 },
247 .Length => |len| {
248 var n = try allocator.alloc(u8, len);
249 defer allocator.free(n);
250 try reader.readNoEof(n);
251 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0));
252 name = try allocator.dupe(u8, n[0..actual_len.?]);
253 },
254 }
255 return name;
256}
src/link/MachO/Object.zig created+228
...@@ -0,0 +1,228 @@
1const Object = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const io = std.io;
7const log = std.log.scoped(.object);
8const macho = std.macho;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12const parseName = @import("Zld.zig").parseName;
13
14usingnamespace @import("commands.zig");
15
16allocator: *Allocator,
17file: fs.File,
18name: []u8,
19ar_name: ?[]u8 = null,
20
21header: macho.mach_header_64,
22
23load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
24
25segment_cmd_index: ?u16 = null,
26symtab_cmd_index: ?u16 = null,
27dysymtab_cmd_index: ?u16 = null,
28build_version_cmd_index: ?u16 = null,
29data_in_code_cmd_index: ?u16 = null,
30text_section_index: ?u16 = null,
31
32// __DWARF segment sections
33dwarf_debug_info_index: ?u16 = null,
34dwarf_debug_abbrev_index: ?u16 = null,
35dwarf_debug_str_index: ?u16 = null,
36dwarf_debug_line_index: ?u16 = null,
37dwarf_debug_ranges_index: ?u16 = null,
38
39symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
40strtab: std.ArrayListUnmanaged(u8) = .{},
41
42data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
43
44pub fn deinit(self: *Object) void {
45 for (self.load_commands.items) |*lc| {
46 lc.deinit(self.allocator);
47 }
48 self.load_commands.deinit(self.allocator);
49 self.symtab.deinit(self.allocator);
50 self.strtab.deinit(self.allocator);
51 self.data_in_code_entries.deinit(self.allocator);
52 self.allocator.free(self.name);
53 if (self.ar_name) |v| {
54 self.allocator.free(v);
55 }
56 self.file.close();
57}
58
59/// Caller owns the returned Object instance and is responsible for calling
60/// `deinit` to free allocated memory.
61pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []const u8, file: fs.File) !Object {
62 var reader = file.reader();
63 const header = try reader.readStruct(macho.mach_header_64);
64
65 if (header.filetype != macho.MH_OBJECT) {
66 // Reset file cursor.
67 try file.seekTo(0);
68 return error.NotObject;
69 }
70
71 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
72 macho.CPU_TYPE_ARM64 => .aarch64,
73 macho.CPU_TYPE_X86_64 => .x86_64,
74 else => |value| {
75 log.err("unsupported cpu architecture 0x{x}", .{value});
76 return error.UnsupportedCpuArchitecture;
77 },
78 };
79 if (this_arch != arch) {
80 log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
81 return error.MismatchedCpuArchitecture;
82 }
83
84 var self = Object{
85 .allocator = allocator,
86 .name = try allocator.dupe(u8, name),
87 .file = file,
88 .header = header,
89 };
90
91 try self.readLoadCommands(reader, .{});
92
93 if (self.symtab_cmd_index != null) {
94 try self.readSymtab();
95 try self.readStrtab();
96 }
97
98 if (self.data_in_code_cmd_index != null) try self.readDataInCode();
99
100 log.debug("\n\n", .{});
101 log.debug("{s} defines symbols", .{self.name});
102 for (self.symtab.items) |sym| {
103 const symname = self.getString(sym.n_strx);
104 log.debug("'{s}': {}", .{ symname, sym });
105 }
106
107 return self;
108}
109
110pub const ReadOffset = struct {
111 offset: ?u32 = null,
112};
113
114pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !void {
115 const offset_mod = offset.offset orelse 0;
116 try self.load_commands.ensureCapacity(self.allocator, self.header.ncmds);
117
118 var i: u16 = 0;
119 while (i < self.header.ncmds) : (i += 1) {
120 var cmd = try LoadCommand.read(self.allocator, reader);
121 switch (cmd.cmd()) {
122 macho.LC_SEGMENT_64 => {
123 self.segment_cmd_index = i;
124 var seg = cmd.Segment;
125 for (seg.sections.items) |*sect, j| {
126 const index = @intCast(u16, j);
127 const segname = parseName(&sect.segname);
128 const sectname = parseName(&sect.sectname);
129 if (mem.eql(u8, segname, "__DWARF")) {
130 if (mem.eql(u8, sectname, "__debug_info")) {
131 self.dwarf_debug_info_index = index;
132 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
133 self.dwarf_debug_abbrev_index = index;
134 } else if (mem.eql(u8, sectname, "__debug_str")) {
135 self.dwarf_debug_str_index = index;
136 } else if (mem.eql(u8, sectname, "__debug_line")) {
137 self.dwarf_debug_line_index = index;
138 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
139 self.dwarf_debug_ranges_index = index;
140 }
141 } else if (mem.eql(u8, segname, "__TEXT")) {
142 if (mem.eql(u8, sectname, "__text")) {
143 self.text_section_index = index;
144 }
145 }
146
147 sect.offset += offset_mod;
148 if (sect.reloff > 0)
149 sect.reloff += offset_mod;
150 }
151
152 seg.inner.fileoff += offset_mod;
153 },
154 macho.LC_SYMTAB => {
155 self.symtab_cmd_index = i;
156 cmd.Symtab.symoff += offset_mod;
157 cmd.Symtab.stroff += offset_mod;
158 },
159 macho.LC_DYSYMTAB => {
160 self.dysymtab_cmd_index = i;
161 },
162 macho.LC_BUILD_VERSION => {
163 self.build_version_cmd_index = i;
164 },
165 macho.LC_DATA_IN_CODE => {
166 self.data_in_code_cmd_index = i;
167 },
168 else => {
169 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
170 },
171 }
172 self.load_commands.appendAssumeCapacity(cmd);
173 }
174}
175
176pub fn readSymtab(self: *Object) !void {
177 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
178 var buffer = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
179 defer self.allocator.free(buffer);
180 _ = try self.file.preadAll(buffer, symtab_cmd.symoff);
181 try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms);
182 // TODO this align case should not be needed.
183 // Probably a bug in stage1.
184 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, buffer));
185 self.symtab.appendSliceAssumeCapacity(slice);
186}
187
188pub fn readStrtab(self: *Object) !void {
189 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
190 var buffer = try self.allocator.alloc(u8, symtab_cmd.strsize);
191 defer self.allocator.free(buffer);
192 _ = try self.file.preadAll(buffer, symtab_cmd.stroff);
193 try self.strtab.ensureCapacity(self.allocator, symtab_cmd.strsize);
194 self.strtab.appendSliceAssumeCapacity(buffer);
195}
196
197pub fn getString(self: *const Object, str_off: u32) []const u8 {
198 assert(str_off < self.strtab.items.len);
199 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
200}
201
202pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
203 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
204 const sect = seg.sections.items[index];
205 var buffer = try allocator.alloc(u8, sect.size);
206 _ = try self.file.preadAll(buffer, sect.offset);
207 return buffer;
208}
209
210pub fn readDataInCode(self: *Object) !void {
211 const index = self.data_in_code_cmd_index orelse return;
212 const data_in_code = self.load_commands.items[index].LinkeditData;
213
214 var buffer = try self.allocator.alloc(u8, data_in_code.datasize);
215 defer self.allocator.free(buffer);
216
217 _ = try self.file.preadAll(buffer, data_in_code.dataoff);
218
219 var stream = io.fixedBufferStream(buffer);
220 var reader = stream.reader();
221 while (true) {
222 const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) {
223 error.EndOfStream => break,
224 else => |e| return e,
225 };
226 try self.data_in_code_entries.append(self.allocator, dice);
227 }
228}
src/link/MachO/Zld.zig created+3192
...@@ -0,0 +1,3192 @@
1const Zld = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const dwarf = std.dwarf;
6const leb = std.leb;
7const mem = std.mem;
8const meta = std.meta;
9const fs = std.fs;
10const macho = std.macho;
11const math = std.math;
12const log = std.log.scoped(.zld);
13const aarch64 = @import("../../codegen/aarch64.zig");
14
15const Allocator = mem.Allocator;
16const CodeSignature = @import("CodeSignature.zig");
17const Archive = @import("Archive.zig");
18const Object = @import("Object.zig");
19const Trie = @import("Trie.zig");
20
21usingnamespace @import("commands.zig");
22usingnamespace @import("bind.zig");
23
24allocator: *Allocator,
25
26arch: ?std.Target.Cpu.Arch = null,
27page_size: ?u16 = null,
28file: ?fs.File = null,
29out_path: ?[]const u8 = null,
30
31// TODO Eventually, we will want to keep track of the archives themselves to be able to exclude objects
32// contained within from landing in the final artifact. For now however, since we don't optimise the binary
33// at all, we just move all objects from the archives into the final artifact.
34objects: std.ArrayListUnmanaged(Object) = .{},
35
36load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
37
38pagezero_segment_cmd_index: ?u16 = null,
39text_segment_cmd_index: ?u16 = null,
40data_const_segment_cmd_index: ?u16 = null,
41data_segment_cmd_index: ?u16 = null,
42linkedit_segment_cmd_index: ?u16 = null,
43dyld_info_cmd_index: ?u16 = null,
44symtab_cmd_index: ?u16 = null,
45dysymtab_cmd_index: ?u16 = null,
46dylinker_cmd_index: ?u16 = null,
47libsystem_cmd_index: ?u16 = null,
48data_in_code_cmd_index: ?u16 = null,
49function_starts_cmd_index: ?u16 = null,
50main_cmd_index: ?u16 = null,
51version_min_cmd_index: ?u16 = null,
52source_version_cmd_index: ?u16 = null,
53uuid_cmd_index: ?u16 = null,
54code_signature_cmd_index: ?u16 = null,
55
56// __TEXT segment sections
57text_section_index: ?u16 = null,
58stubs_section_index: ?u16 = null,
59stub_helper_section_index: ?u16 = null,
60text_const_section_index: ?u16 = null,
61cstring_section_index: ?u16 = null,
62
63// __DATA segment sections
64got_section_index: ?u16 = null,
65tlv_section_index: ?u16 = null,
66tlv_data_section_index: ?u16 = null,
67tlv_bss_section_index: ?u16 = null,
68la_symbol_ptr_section_index: ?u16 = null,
69data_const_section_index: ?u16 = null,
70data_section_index: ?u16 = null,
71bss_section_index: ?u16 = null,
72
73locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},
74exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},
75nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
76lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
77tlv_bootstrap: ?Import = null,
78threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
79local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
80nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
81
82strtab: std.ArrayListUnmanaged(u8) = .{},
83
84stub_helper_stubs_start_off: ?u64 = null,
85
86mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
87unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
88
89// TODO this will require scanning the relocations at least one to work out
90// the exact amount of local GOT indirections. For the time being, set some
91// default value.
92const max_local_got_indirections: u16 = 1000;
93
94const GotEntry = struct {
95 index: u32,
96 target_addr: u64,
97};
98
99const MappingKey = struct {
100 object_id: u16,
101 source_sect_id: u16,
102};
103
104const SectionMapping = struct {
105 source_sect_id: u16,
106 target_seg_id: u16,
107 target_sect_id: u16,
108 offset: u32,
109};
110
111const Symbol = struct {
112 inner: macho.nlist_64,
113 tt: Type,
114 object_id: u16,
115
116 const Type = enum {
117 Local,
118 WeakGlobal,
119 Global,
120 };
121};
122
123const DebugInfo = struct {
124 inner: dwarf.DwarfInfo,
125 debug_info: []u8,
126 debug_abbrev: []u8,
127 debug_str: []u8,
128 debug_line: []u8,
129 debug_ranges: []u8,
130
131 pub fn parseFromObject(allocator: *Allocator, object: Object) !?DebugInfo {
132 var debug_info = blk: {
133 const index = object.dwarf_debug_info_index orelse return null;
134 break :blk try object.readSection(allocator, index);
135 };
136 var debug_abbrev = blk: {
137 const index = object.dwarf_debug_abbrev_index orelse return null;
138 break :blk try object.readSection(allocator, index);
139 };
140 var debug_str = blk: {
141 const index = object.dwarf_debug_str_index orelse return null;
142 break :blk try object.readSection(allocator, index);
143 };
144 var debug_line = blk: {
145 const index = object.dwarf_debug_line_index orelse return null;
146 break :blk try object.readSection(allocator, index);
147 };
148 var debug_ranges = blk: {
149 if (object.dwarf_debug_ranges_index) |ind| {
150 break :blk try object.readSection(allocator, ind);
151 }
152 break :blk try allocator.alloc(u8, 0);
153 };
154
155 var inner: dwarf.DwarfInfo = .{
156 .endian = .Little,
157 .debug_info = debug_info,
158 .debug_abbrev = debug_abbrev,
159 .debug_str = debug_str,
160 .debug_line = debug_line,
161 .debug_ranges = debug_ranges,
162 };
163 try dwarf.openDwarfDebugInfo(&inner, allocator);
164
165 return DebugInfo{
166 .inner = inner,
167 .debug_info = debug_info,
168 .debug_abbrev = debug_abbrev,
169 .debug_str = debug_str,
170 .debug_line = debug_line,
171 .debug_ranges = debug_ranges,
172 };
173 }
174
175 pub fn deinit(self: *DebugInfo, allocator: *Allocator) void {
176 allocator.free(self.debug_info);
177 allocator.free(self.debug_abbrev);
178 allocator.free(self.debug_str);
179 allocator.free(self.debug_line);
180 allocator.free(self.debug_ranges);
181 self.inner.abbrev_table_list.deinit();
182 self.inner.compile_unit_list.deinit();
183 self.inner.func_list.deinit();
184 }
185};
186
187pub const Import = struct {
188 /// MachO symbol table entry.
189 symbol: macho.nlist_64,
190
191 /// Id of the dynamic library where the specified entries can be found.
192 dylib_ordinal: i64,
193
194 /// Index of this import within the import list.
195 index: u32,
196};
197
198/// Default path to dyld
199/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
200/// instead but this will do for now.
201const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
202
203/// Default lib search path
204/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
205/// instead but this will do for now.
206const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
207
208const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
209/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
210const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
211
212pub fn init(allocator: *Allocator) Zld {
213 return .{ .allocator = allocator };
214}
215
216pub fn deinit(self: *Zld) void {
217 self.threadlocal_offsets.deinit(self.allocator);
218 self.strtab.deinit(self.allocator);
219 self.local_rebases.deinit(self.allocator);
220 for (self.lazy_imports.items()) |*entry| {
221 self.allocator.free(entry.key);
222 }
223 self.lazy_imports.deinit(self.allocator);
224 for (self.nonlazy_imports.items()) |*entry| {
225 self.allocator.free(entry.key);
226 }
227 self.nonlazy_imports.deinit(self.allocator);
228 for (self.nonlazy_pointers.items()) |*entry| {
229 self.allocator.free(entry.key);
230 }
231 self.nonlazy_pointers.deinit(self.allocator);
232 for (self.exports.items()) |*entry| {
233 self.allocator.free(entry.key);
234 }
235 self.exports.deinit(self.allocator);
236 for (self.locals.items()) |*entry| {
237 self.allocator.free(entry.key);
238 entry.value.deinit(self.allocator);
239 }
240 self.locals.deinit(self.allocator);
241 for (self.objects.items) |*object| {
242 object.deinit();
243 }
244 self.objects.deinit(self.allocator);
245 for (self.load_commands.items) |*lc| {
246 lc.deinit(self.allocator);
247 }
248 self.load_commands.deinit(self.allocator);
249 self.mappings.deinit(self.allocator);
250 self.unhandled_sections.deinit(self.allocator);
251 if (self.file) |*f| f.close();
252}
253
254pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
255 if (files.len == 0) return error.NoInputFiles;
256 if (out_path.len == 0) return error.EmptyOutputPath;
257
258 if (self.arch == null) {
259 // Try inferring the arch from the object files.
260 self.arch = blk: {
261 const file = try fs.cwd().openFile(files[0], .{});
262 defer file.close();
263 var reader = file.reader();
264 const header = try reader.readStruct(macho.mach_header_64);
265 const arch: std.Target.Cpu.Arch = switch (header.cputype) {
266 macho.CPU_TYPE_X86_64 => .x86_64,
267 macho.CPU_TYPE_ARM64 => .aarch64,
268 else => |value| {
269 log.err("unsupported cpu architecture 0x{x}", .{value});
270 return error.UnsupportedCpuArchitecture;
271 },
272 };
273 break :blk arch;
274 };
275 }
276
277 self.page_size = switch (self.arch.?) {
278 .aarch64 => 0x4000,
279 .x86_64 => 0x1000,
280 else => unreachable,
281 };
282 self.out_path = out_path;
283 self.file = try fs.cwd().createFile(out_path, .{
284 .truncate = true,
285 .read = true,
286 .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
287 });
288
289 try self.populateMetadata();
290 try self.parseInputFiles(files);
291 try self.sortSections();
292 try self.resolveImports();
293 try self.allocateTextSegment();
294 try self.allocateDataConstSegment();
295 try self.allocateDataSegment();
296 self.allocateLinkeditSegment();
297 try self.writeStubHelperCommon();
298 try self.resolveSymbols();
299 try self.doRelocs();
300 try self.flush();
301}
302
303fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
304 for (files) |file_name| {
305 const file = try fs.cwd().openFile(file_name, .{});
306
307 try_object: {
308 var object = Object.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) {
309 error.NotObject => break :try_object,
310 else => |e| return e,
311 };
312 const index = @intCast(u16, self.objects.items.len);
313 try self.objects.append(self.allocator, object);
314 try self.updateMetadata(index);
315 continue;
316 }
317
318 try_archive: {
319 var archive = Archive.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) {
320 error.NotArchive => break :try_archive,
321 else => |e| return e,
322 };
323 defer archive.deinit();
324 while (archive.objects.popOrNull()) |object| {
325 const index = @intCast(u16, self.objects.items.len);
326 try self.objects.append(self.allocator, object);
327 try self.updateMetadata(index);
328 }
329 continue;
330 }
331
332 log.err("unexpected file type: expected object '.o' or archive '.a': {s}", .{file_name});
333 return error.UnexpectedInputFileType;
334 }
335}
336
337fn mapAndUpdateSections(
338 self: *Zld,
339 object_id: u16,
340 source_sect_id: u16,
341 target_seg_id: u16,
342 target_sect_id: u16,
343) !void {
344 const object = self.objects.items[object_id];
345 const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
346 const source_sect = source_seg.sections.items[source_sect_id];
347 const target_seg = &self.load_commands.items[target_seg_id].Segment;
348 const target_sect = &target_seg.sections.items[target_sect_id];
349
350 const alignment = try math.powi(u32, 2, target_sect.@"align");
351 const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment);
352 const size = mem.alignForwardGeneric(u64, source_sect.size, alignment);
353 const key = MappingKey{
354 .object_id = object_id,
355 .source_sect_id = source_sect_id,
356 };
357 try self.mappings.putNoClobber(self.allocator, key, .{
358 .source_sect_id = source_sect_id,
359 .target_seg_id = target_seg_id,
360 .target_sect_id = target_sect_id,
361 .offset = @intCast(u32, offset),
362 });
363 log.debug("{s}: {s},{s} mapped to {s},{s} from 0x{x} to 0x{x}", .{
364 object.name,
365 parseName(&source_sect.segname),
366 parseName(&source_sect.sectname),
367 parseName(&target_sect.segname),
368 parseName(&target_sect.sectname),
369 offset,
370 offset + size,
371 });
372
373 target_sect.size = offset + size;
374}
375
376fn updateMetadata(self: *Zld, object_id: u16) !void {
377 const object = self.objects.items[object_id];
378 const object_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
379 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
380 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
381 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
382
383 // Create missing metadata
384 for (object_seg.sections.items) |source_sect, id| {
385 if (id == object.text_section_index.?) continue;
386 const segname = parseName(&source_sect.segname);
387 const sectname = parseName(&source_sect.sectname);
388 const flags = source_sect.flags;
389
390 switch (flags) {
391 macho.S_REGULAR, macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
392 if (mem.eql(u8, segname, "__TEXT")) {
393 if (self.text_const_section_index != null) continue;
394
395 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
396 try text_seg.addSection(self.allocator, .{
397 .sectname = makeStaticString("__const"),
398 .segname = makeStaticString("__TEXT"),
399 .addr = 0,
400 .size = 0,
401 .offset = 0,
402 .@"align" = 0,
403 .reloff = 0,
404 .nreloc = 0,
405 .flags = macho.S_REGULAR,
406 .reserved1 = 0,
407 .reserved2 = 0,
408 .reserved3 = 0,
409 });
410 } else if (mem.eql(u8, segname, "__DATA")) {
411 if (!mem.eql(u8, sectname, "__const")) continue;
412 if (self.data_const_section_index != null) continue;
413
414 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
415 try data_const_seg.addSection(self.allocator, .{
416 .sectname = makeStaticString("__const"),
417 .segname = makeStaticString("__DATA_CONST"),
418 .addr = 0,
419 .size = 0,
420 .offset = 0,
421 .@"align" = 0,
422 .reloff = 0,
423 .nreloc = 0,
424 .flags = macho.S_REGULAR,
425 .reserved1 = 0,
426 .reserved2 = 0,
427 .reserved3 = 0,
428 });
429 }
430 },
431 macho.S_CSTRING_LITERALS => {
432 if (!mem.eql(u8, segname, "__TEXT")) continue;
433 if (self.cstring_section_index != null) continue;
434
435 self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
436 try text_seg.addSection(self.allocator, .{
437 .sectname = makeStaticString("__cstring"),
438 .segname = makeStaticString("__TEXT"),
439 .addr = 0,
440 .size = 0,
441 .offset = 0,
442 .@"align" = 0,
443 .reloff = 0,
444 .nreloc = 0,
445 .flags = macho.S_CSTRING_LITERALS,
446 .reserved1 = 0,
447 .reserved2 = 0,
448 .reserved3 = 0,
449 });
450 },
451 macho.S_ZEROFILL => {
452 if (!mem.eql(u8, segname, "__DATA")) continue;
453 if (self.bss_section_index != null) continue;
454
455 self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
456 try data_seg.addSection(self.allocator, .{
457 .sectname = makeStaticString("__bss"),
458 .segname = makeStaticString("__DATA"),
459 .addr = 0,
460 .size = 0,
461 .offset = 0,
462 .@"align" = 0,
463 .reloff = 0,
464 .nreloc = 0,
465 .flags = macho.S_ZEROFILL,
466 .reserved1 = 0,
467 .reserved2 = 0,
468 .reserved3 = 0,
469 });
470 },
471 macho.S_THREAD_LOCAL_VARIABLES => {
472 if (!mem.eql(u8, segname, "__DATA")) continue;
473 if (self.tlv_section_index != null) continue;
474
475 self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
476 try data_seg.addSection(self.allocator, .{
477 .sectname = makeStaticString("__thread_vars"),
478 .segname = makeStaticString("__DATA"),
479 .addr = 0,
480 .size = 0,
481 .offset = 0,
482 .@"align" = 0,
483 .reloff = 0,
484 .nreloc = 0,
485 .flags = macho.S_THREAD_LOCAL_VARIABLES,
486 .reserved1 = 0,
487 .reserved2 = 0,
488 .reserved3 = 0,
489 });
490 },
491 macho.S_THREAD_LOCAL_REGULAR => {
492 if (!mem.eql(u8, segname, "__DATA")) continue;
493 if (self.tlv_data_section_index != null) continue;
494
495 self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
496 try data_seg.addSection(self.allocator, .{
497 .sectname = makeStaticString("__thread_data"),
498 .segname = makeStaticString("__DATA"),
499 .addr = 0,
500 .size = 0,
501 .offset = 0,
502 .@"align" = 0,
503 .reloff = 0,
504 .nreloc = 0,
505 .flags = macho.S_THREAD_LOCAL_REGULAR,
506 .reserved1 = 0,
507 .reserved2 = 0,
508 .reserved3 = 0,
509 });
510 },
511 macho.S_THREAD_LOCAL_ZEROFILL => {
512 if (!mem.eql(u8, segname, "__DATA")) continue;
513 if (self.tlv_bss_section_index != null) continue;
514
515 self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
516 try data_seg.addSection(self.allocator, .{
517 .sectname = makeStaticString("__thread_bss"),
518 .segname = makeStaticString("__DATA"),
519 .addr = 0,
520 .size = 0,
521 .offset = 0,
522 .@"align" = 0,
523 .reloff = 0,
524 .nreloc = 0,
525 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
526 .reserved1 = 0,
527 .reserved2 = 0,
528 .reserved3 = 0,
529 });
530 },
531 else => {
532 log.debug("unhandled section type 0x{x} for '{s}/{s}'", .{ flags, segname, sectname });
533 },
534 }
535 }
536
537 // Find ideal section alignment.
538 for (object_seg.sections.items) |source_sect, id| {
539 if (self.getMatchingSection(source_sect)) |res| {
540 const target_seg = &self.load_commands.items[res.seg].Segment;
541 const target_sect = &target_seg.sections.items[res.sect];
542 target_sect.@"align" = math.max(target_sect.@"align", source_sect.@"align");
543 }
544 }
545
546 // Update section mappings
547 for (object_seg.sections.items) |source_sect, id| {
548 const source_sect_id = @intCast(u16, id);
549 if (self.getMatchingSection(source_sect)) |res| {
550 try self.mapAndUpdateSections(object_id, source_sect_id, res.seg, res.sect);
551 continue;
552 }
553
554 const segname = parseName(&source_sect.segname);
555 const sectname = parseName(&source_sect.sectname);
556 log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname });
557 try self.unhandled_sections.putNoClobber(self.allocator, .{
558 .object_id = object_id,
559 .source_sect_id = source_sect_id,
560 }, 0);
561 }
562}
563
564const MatchingSection = struct {
565 seg: u16,
566 sect: u16,
567};
568
569fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
570 const segname = parseName(&section.segname);
571 const sectname = parseName(&section.sectname);
572 const res: ?MatchingSection = blk: {
573 switch (section.flags) {
574 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
575 break :blk .{
576 .seg = self.text_segment_cmd_index.?,
577 .sect = self.text_const_section_index.?,
578 };
579 },
580 macho.S_CSTRING_LITERALS => {
581 break :blk .{
582 .seg = self.text_segment_cmd_index.?,
583 .sect = self.cstring_section_index.?,
584 };
585 },
586 macho.S_ZEROFILL => {
587 break :blk .{
588 .seg = self.data_segment_cmd_index.?,
589 .sect = self.bss_section_index.?,
590 };
591 },
592 macho.S_THREAD_LOCAL_VARIABLES => {
593 break :blk .{
594 .seg = self.data_segment_cmd_index.?,
595 .sect = self.tlv_section_index.?,
596 };
597 },
598 macho.S_THREAD_LOCAL_REGULAR => {
599 break :blk .{
600 .seg = self.data_segment_cmd_index.?,
601 .sect = self.tlv_data_section_index.?,
602 };
603 },
604 macho.S_THREAD_LOCAL_ZEROFILL => {
605 break :blk .{
606 .seg = self.data_segment_cmd_index.?,
607 .sect = self.tlv_bss_section_index.?,
608 };
609 },
610 macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS => {
611 break :blk .{
612 .seg = self.text_segment_cmd_index.?,
613 .sect = self.text_section_index.?,
614 };
615 },
616 macho.S_REGULAR => {
617 if (mem.eql(u8, segname, "__TEXT")) {
618 break :blk .{
619 .seg = self.text_segment_cmd_index.?,
620 .sect = self.text_const_section_index.?,
621 };
622 } else if (mem.eql(u8, segname, "__DATA")) {
623 if (mem.eql(u8, sectname, "__data")) {
624 break :blk .{
625 .seg = self.data_segment_cmd_index.?,
626 .sect = self.data_section_index.?,
627 };
628 } else if (mem.eql(u8, sectname, "__const")) {
629 break :blk .{
630 .seg = self.data_const_segment_cmd_index.?,
631 .sect = self.data_const_section_index.?,
632 };
633 }
634 }
635 break :blk null;
636 },
637 else => {
638 break :blk null;
639 },
640 }
641 };
642 return res;
643}
644
645fn sortSections(self: *Zld) !void {
646 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
647 defer text_index_mapping.deinit();
648 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
649 defer data_const_index_mapping.deinit();
650 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
651 defer data_index_mapping.deinit();
652
653 {
654 // __TEXT segment
655 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
656 var sections = seg.sections.toOwnedSlice(self.allocator);
657 defer self.allocator.free(sections);
658 try seg.sections.ensureCapacity(self.allocator, sections.len);
659
660 const indices = &[_]*?u16{
661 &self.text_section_index,
662 &self.stubs_section_index,
663 &self.stub_helper_section_index,
664 &self.text_const_section_index,
665 &self.cstring_section_index,
666 };
667 for (indices) |maybe_index| {
668 const new_index: u16 = if (maybe_index.*) |index| blk: {
669 const idx = @intCast(u16, seg.sections.items.len);
670 seg.sections.appendAssumeCapacity(sections[index]);
671 try text_index_mapping.putNoClobber(index, idx);
672 break :blk idx;
673 } else continue;
674 maybe_index.* = new_index;
675 }
676 }
677
678 {
679 // __DATA_CONST segment
680 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
681 var sections = seg.sections.toOwnedSlice(self.allocator);
682 defer self.allocator.free(sections);
683 try seg.sections.ensureCapacity(self.allocator, sections.len);
684
685 const indices = &[_]*?u16{
686 &self.got_section_index,
687 &self.data_const_section_index,
688 };
689 for (indices) |maybe_index| {
690 const new_index: u16 = if (maybe_index.*) |index| blk: {
691 const idx = @intCast(u16, seg.sections.items.len);
692 seg.sections.appendAssumeCapacity(sections[index]);
693 try data_const_index_mapping.putNoClobber(index, idx);
694 break :blk idx;
695 } else continue;
696 maybe_index.* = new_index;
697 }
698 }
699
700 {
701 // __DATA segment
702 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
703 var sections = seg.sections.toOwnedSlice(self.allocator);
704 defer self.allocator.free(sections);
705 try seg.sections.ensureCapacity(self.allocator, sections.len);
706
707 // __DATA segment
708 const indices = &[_]*?u16{
709 &self.la_symbol_ptr_section_index,
710 &self.tlv_section_index,
711 &self.data_section_index,
712 &self.tlv_data_section_index,
713 &self.tlv_bss_section_index,
714 &self.bss_section_index,
715 };
716 for (indices) |maybe_index| {
717 const new_index: u16 = if (maybe_index.*) |index| blk: {
718 const idx = @intCast(u16, seg.sections.items.len);
719 seg.sections.appendAssumeCapacity(sections[index]);
720 try data_index_mapping.putNoClobber(index, idx);
721 break :blk idx;
722 } else continue;
723 maybe_index.* = new_index;
724 }
725 }
726
727 var it = self.mappings.iterator();
728 while (it.next()) |entry| {
729 const mapping = &entry.value;
730 if (self.text_segment_cmd_index.? == mapping.target_seg_id) {
731 const new_index = text_index_mapping.get(mapping.target_sect_id) orelse unreachable;
732 mapping.target_sect_id = new_index;
733 } else if (self.data_const_segment_cmd_index.? == mapping.target_seg_id) {
734 const new_index = data_const_index_mapping.get(mapping.target_sect_id) orelse unreachable;
735 mapping.target_sect_id = new_index;
736 } else if (self.data_segment_cmd_index.? == mapping.target_seg_id) {
737 const new_index = data_index_mapping.get(mapping.target_sect_id) orelse unreachable;
738 mapping.target_sect_id = new_index;
739 } else unreachable;
740 }
741}
742
743fn resolveImports(self: *Zld) !void {
744 var imports = std.StringArrayHashMap(bool).init(self.allocator);
745 defer imports.deinit();
746
747 for (self.objects.items) |object| {
748 for (object.symtab.items) |sym| {
749 if (isLocal(&sym)) continue;
750
751 const name = object.getString(sym.n_strx);
752 const res = try imports.getOrPut(name);
753 if (isExport(&sym)) {
754 res.entry.value = false;
755 continue;
756 }
757 if (res.found_existing and !res.entry.value)
758 continue;
759 res.entry.value = true;
760 }
761 }
762
763 for (imports.items()) |entry| {
764 if (!entry.value) continue;
765
766 const sym_name = entry.key;
767 const n_strx = try self.makeString(sym_name);
768 var new_sym: macho.nlist_64 = .{
769 .n_strx = n_strx,
770 .n_type = macho.N_UNDF | macho.N_EXT,
771 .n_value = 0,
772 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
773 .n_sect = 0,
774 };
775 var key = try self.allocator.dupe(u8, sym_name);
776 // TODO handle symbol resolution from non-libc dylibs.
777 const dylib_ordinal = 1;
778
779 // TODO need to rework this. Perhaps should create a set of all possible libc
780 // symbols which are expected to be nonlazy?
781 if (mem.eql(u8, sym_name, "___stdoutp") or
782 mem.eql(u8, sym_name, "___stderrp") or
783 mem.eql(u8, sym_name, "___stdinp") or
784 mem.eql(u8, sym_name, "___stack_chk_guard") or
785 mem.eql(u8, sym_name, "_environ") or
786 mem.eql(u8, sym_name, "__DefaultRuneLocale") or
787 mem.eql(u8, sym_name, "_mach_task_self_"))
788 {
789 log.debug("writing nonlazy symbol '{s}'", .{sym_name});
790 const index = @intCast(u32, self.nonlazy_imports.items().len);
791 try self.nonlazy_imports.putNoClobber(self.allocator, key, .{
792 .symbol = new_sym,
793 .dylib_ordinal = dylib_ordinal,
794 .index = index,
795 });
796 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
797 log.debug("writing threadlocal symbol '{s}'", .{sym_name});
798 self.tlv_bootstrap = .{
799 .symbol = new_sym,
800 .dylib_ordinal = dylib_ordinal,
801 .index = 0,
802 };
803 } else {
804 log.debug("writing lazy symbol '{s}'", .{sym_name});
805 const index = @intCast(u32, self.lazy_imports.items().len);
806 try self.lazy_imports.putNoClobber(self.allocator, key, .{
807 .symbol = new_sym,
808 .dylib_ordinal = dylib_ordinal,
809 .index = index,
810 });
811 }
812 }
813
814 const n_strx = try self.makeString("dyld_stub_binder");
815 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
816 log.debug("writing nonlazy symbol 'dyld_stub_binder'", .{});
817 const index = @intCast(u32, self.nonlazy_imports.items().len);
818 try self.nonlazy_imports.putNoClobber(self.allocator, name, .{
819 .symbol = .{
820 .n_strx = n_strx,
821 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
822 .n_sect = 0,
823 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
824 .n_value = 0,
825 },
826 .dylib_ordinal = 1,
827 .index = index,
828 });
829}
830
831fn allocateTextSegment(self: *Zld) !void {
832 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
833 const nexterns = @intCast(u32, self.lazy_imports.items().len);
834
835 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
836 seg.inner.fileoff = 0;
837 seg.inner.vmaddr = base_vmaddr;
838
839 // Set stubs and stub_helper sizes
840 const stubs = &seg.sections.items[self.stubs_section_index.?];
841 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
842 stubs.size += nexterns * stubs.reserved2;
843
844 const stub_size: u4 = switch (self.arch.?) {
845 .x86_64 => 10,
846 .aarch64 => 3 * @sizeOf(u32),
847 else => unreachable,
848 };
849 stub_helper.size += nexterns * stub_size;
850
851 var sizeofcmds: u64 = 0;
852 for (self.load_commands.items) |lc| {
853 sizeofcmds += lc.cmdsize();
854 }
855
856 try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);
857
858 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
859 var min_alignment: u32 = 0;
860 for (seg.sections.items) |sect| {
861 const alignment = try math.powi(u32, 2, sect.@"align");
862 min_alignment = math.max(min_alignment, alignment);
863 }
864
865 assert(min_alignment > 0);
866 const last_sect_idx = seg.sections.items.len - 1;
867 const last_sect = seg.sections.items[last_sect_idx];
868 const shift: u32 = blk: {
869 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
870 const factor = @divTrunc(diff, min_alignment);
871 break :blk @intCast(u32, factor * min_alignment);
872 };
873
874 if (shift > 0) {
875 for (seg.sections.items) |*sect| {
876 sect.offset += shift;
877 sect.addr += shift;
878 }
879 }
880}
881
882fn allocateDataConstSegment(self: *Zld) !void {
883 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
884 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);
885
886 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
887 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
888 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
889
890 // Set got size
891 const got = &seg.sections.items[self.got_section_index.?];
892 // TODO this will require scanning the relocations at least one to work out
893 // the exact amount of local GOT indirections. For the time being, set some
894 // default value.
895 got.size += (max_local_got_indirections + nonlazy) * @sizeOf(u64);
896
897 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
898}
899
900fn allocateDataSegment(self: *Zld) !void {
901 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
902 const lazy = @intCast(u32, self.lazy_imports.items().len);
903
904 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
905 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
906 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
907
908 // Set la_symbol_ptr and data size
909 const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?];
910 const data = &seg.sections.items[self.data_section_index.?];
911 la_symbol_ptr.size += lazy * @sizeOf(u64);
912 data.size += @sizeOf(u64); // TODO when do we need more?
913
914 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
915}
916
917fn allocateLinkeditSegment(self: *Zld) void {
918 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
919 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
920 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
921 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
922}
923
924fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
925 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
926 const seg = &self.load_commands.items[index].Segment;
927
928 // Allocate the sections according to their alignment at the beginning of the segment.
929 var start: u64 = offset;
930 for (seg.sections.items) |*sect| {
931 const alignment = try math.powi(u32, 2, sect.@"align");
932 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
933 const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
934 sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
935 sect.addr = seg.inner.vmaddr + start_aligned;
936 start = end_aligned;
937 }
938
939 const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?);
940 seg.inner.filesize = seg_size_aligned;
941 seg.inner.vmsize = seg_size_aligned;
942}
943
944fn writeStubHelperCommon(self: *Zld) !void {
945 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
946 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
947 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
948 const got = &data_const_segment.sections.items[self.got_section_index.?];
949 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
950 const data = &data_segment.sections.items[self.data_section_index.?];
951 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
952
953 self.stub_helper_stubs_start_off = blk: {
954 switch (self.arch.?) {
955 .x86_64 => {
956 const code_size = 15;
957 var code: [code_size]u8 = undefined;
958 // lea %r11, [rip + disp]
959 code[0] = 0x4c;
960 code[1] = 0x8d;
961 code[2] = 0x1d;
962 {
963 const target_addr = data.addr + data.size - @sizeOf(u64);
964 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
965 mem.writeIntLittle(u32, code[3..7], displacement);
966 }
967 // push %r11
968 code[7] = 0x41;
969 code[8] = 0x53;
970 // jmp [rip + disp]
971 code[9] = 0xff;
972 code[10] = 0x25;
973 {
974 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
975 const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
976 const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
977 mem.writeIntLittle(u32, code[11..], displacement);
978 }
979 try self.file.?.pwriteAll(&code, stub_helper.offset);
980 break :blk stub_helper.offset + code_size;
981 },
982 .aarch64 => {
983 var code: [6 * @sizeOf(u32)]u8 = undefined;
984 data_blk_outer: {
985 const this_addr = stub_helper.addr;
986 const target_addr = data.addr + data.size - @sizeOf(u64);
987 data_blk: {
988 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
989 // adr x17, disp
990 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
991 // nop
992 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
993 break :data_blk_outer;
994 }
995 data_blk: {
996 const new_this_addr = this_addr + @sizeOf(u32);
997 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
998 // nop
999 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1000 // adr x17, disp
1001 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
1002 break :data_blk_outer;
1003 }
1004 // Jump is too big, replace adr with adrp and add.
1005 const this_page = @intCast(i32, this_addr >> 12);
1006 const target_page = @intCast(i32, target_addr >> 12);
1007 const pages = @intCast(i21, target_page - this_page);
1008 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
1009 const narrowed = @truncate(u12, target_addr);
1010 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
1011 }
1012 // stp x16, x17, [sp, #-16]!
1013 code[8] = 0xf0;
1014 code[9] = 0x47;
1015 code[10] = 0xbf;
1016 code[11] = 0xa9;
1017 binder_blk_outer: {
1018 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
1019 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
1020 const target_addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
1021 binder_blk: {
1022 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
1023 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
1024 // ldr x16, label
1025 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
1026 .literal = literal,
1027 }).toU32());
1028 // nop
1029 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
1030 break :binder_blk_outer;
1031 }
1032 binder_blk: {
1033 const new_this_addr = this_addr + @sizeOf(u32);
1034 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
1035 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
1036 log.debug("2: disp=0x{x}, literal=0x{x}", .{ displacement, literal });
1037 // Pad with nop to please division.
1038 // nop
1039 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
1040 // ldr x16, label
1041 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
1042 .literal = literal,
1043 }).toU32());
1044 break :binder_blk_outer;
1045 }
1046 // Use adrp followed by ldr(immediate).
1047 const this_page = @intCast(i32, this_addr >> 12);
1048 const target_page = @intCast(i32, target_addr >> 12);
1049 const pages = @intCast(i21, target_page - this_page);
1050 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
1051 const narrowed = @truncate(u12, target_addr);
1052 const offset = try math.divExact(u12, narrowed, 8);
1053 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
1054 .register = .{
1055 .rn = .x16,
1056 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
1057 },
1058 }).toU32());
1059 }
1060 // br x16
1061 code[20] = 0x00;
1062 code[21] = 0x02;
1063 code[22] = 0x1f;
1064 code[23] = 0xd6;
1065 try self.file.?.pwriteAll(&code, stub_helper.offset);
1066 break :blk stub_helper.offset + 6 * @sizeOf(u32);
1067 },
1068 else => unreachable,
1069 }
1070 };
1071
1072 for (self.lazy_imports.items()) |_, i| {
1073 const index = @intCast(u32, i);
1074 try self.writeLazySymbolPointer(index);
1075 try self.writeStub(index);
1076 try self.writeStubInStubHelper(index);
1077 }
1078}
1079
1080fn writeLazySymbolPointer(self: *Zld, index: u32) !void {
1081 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1082 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
1083 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1084 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1085
1086 const stub_size: u4 = switch (self.arch.?) {
1087 .x86_64 => 10,
1088 .aarch64 => 3 * @sizeOf(u32),
1089 else => unreachable,
1090 };
1091 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
1092 const end = stub_helper.addr + stub_off - stub_helper.offset;
1093 var buf: [@sizeOf(u64)]u8 = undefined;
1094 mem.writeIntLittle(u64, &buf, end);
1095 const off = la_symbol_ptr.offset + index * @sizeOf(u64);
1096 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
1097 try self.file.?.pwriteAll(&buf, off);
1098}
1099
1100fn writeStub(self: *Zld, index: u32) !void {
1101 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1102 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1103 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1104 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1105
1106 const stub_off = stubs.offset + index * stubs.reserved2;
1107 const stub_addr = stubs.addr + index * stubs.reserved2;
1108 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
1109 log.debug("writing stub at 0x{x}", .{stub_off});
1110 var code = try self.allocator.alloc(u8, stubs.reserved2);
1111 defer self.allocator.free(code);
1112 switch (self.arch.?) {
1113 .x86_64 => {
1114 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
1115 const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
1116 // jmp
1117 code[0] = 0xff;
1118 code[1] = 0x25;
1119 mem.writeIntLittle(u32, code[2..][0..4], displacement);
1120 },
1121 .aarch64 => {
1122 assert(la_ptr_addr >= stub_addr);
1123 outer: {
1124 const this_addr = stub_addr;
1125 const target_addr = la_ptr_addr;
1126 inner: {
1127 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
1128 const literal = math.cast(u18, displacement) catch |_| break :inner;
1129 // ldr x16, literal
1130 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
1131 .literal = literal,
1132 }).toU32());
1133 // nop
1134 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
1135 break :outer;
1136 }
1137 inner: {
1138 const new_this_addr = this_addr + @sizeOf(u32);
1139 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
1140 const literal = math.cast(u18, displacement) catch |_| break :inner;
1141 // nop
1142 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1143 // ldr x16, literal
1144 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
1145 .literal = literal,
1146 }).toU32());
1147 break :outer;
1148 }
1149 // Use adrp followed by ldr(immediate).
1150 const this_page = @intCast(i32, this_addr >> 12);
1151 const target_page = @intCast(i32, target_addr >> 12);
1152 const pages = @intCast(i21, target_page - this_page);
1153 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
1154 const narrowed = @truncate(u12, target_addr);
1155 const offset = try math.divExact(u12, narrowed, 8);
1156 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
1157 .register = .{
1158 .rn = .x16,
1159 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
1160 },
1161 }).toU32());
1162 }
1163 // br x16
1164 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1165 },
1166 else => unreachable,
1167 }
1168 try self.file.?.pwriteAll(code, stub_off);
1169}
1170
1171fn writeStubInStubHelper(self: *Zld, index: u32) !void {
1172 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1173 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
1174
1175 const stub_size: u4 = switch (self.arch.?) {
1176 .x86_64 => 10,
1177 .aarch64 => 3 * @sizeOf(u32),
1178 else => unreachable,
1179 };
1180 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
1181 var code = try self.allocator.alloc(u8, stub_size);
1182 defer self.allocator.free(code);
1183 switch (self.arch.?) {
1184 .x86_64 => {
1185 const displacement = try math.cast(
1186 i32,
1187 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
1188 );
1189 // pushq
1190 code[0] = 0x68;
1191 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1192 // jmpq
1193 code[5] = 0xe9;
1194 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
1195 },
1196 .aarch64 => {
1197 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
1198 const literal = @divExact(stub_size - @sizeOf(u32), 4);
1199 // ldr w16, literal
1200 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
1201 .literal = literal,
1202 }).toU32());
1203 // b disp
1204 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
1205 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1206 },
1207 else => unreachable,
1208 }
1209 try self.file.?.pwriteAll(code, stub_off);
1210}
1211
1212fn resolveSymbols(self: *Zld) !void {
1213 for (self.objects.items) |object, object_id| {
1214 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1215 log.debug("\n\n", .{});
1216 log.debug("resolving symbols in {s}", .{object.name});
1217
1218 for (object.symtab.items) |sym| {
1219 if (isImport(&sym)) continue;
1220
1221 const sym_name = object.getString(sym.n_strx);
1222 const out_name = try self.allocator.dupe(u8, sym_name);
1223 const locs = try self.locals.getOrPut(self.allocator, out_name);
1224 defer {
1225 if (locs.found_existing) self.allocator.free(out_name);
1226 }
1227
1228 if (!locs.found_existing) {
1229 locs.entry.value = .{};
1230 }
1231
1232 const tt: Symbol.Type = blk: {
1233 if (isLocal(&sym)) {
1234 break :blk .Local;
1235 } else if (isWeakDef(&sym)) {
1236 break :blk .WeakGlobal;
1237 } else {
1238 break :blk .Global;
1239 }
1240 };
1241 if (tt == .Global) {
1242 for (locs.entry.value.items) |ss| {
1243 if (ss.tt == .Global) {
1244 log.debug("symbol already defined '{s}'", .{sym_name});
1245 continue;
1246 // log.err("symbol '{s}' defined multiple times: {}", .{ sym_name, sym });
1247 // return error.MultipleSymbolDefinitions;
1248 }
1249 }
1250 }
1251
1252 const source_sect_id = sym.n_sect - 1;
1253 const target_mapping = self.mappings.get(.{
1254 .object_id = @intCast(u16, object_id),
1255 .source_sect_id = source_sect_id,
1256 }) orelse {
1257 if (self.unhandled_sections.get(.{
1258 .object_id = @intCast(u16, object_id),
1259 .source_sect_id = source_sect_id,
1260 }) != null) continue;
1261
1262 log.err("section not mapped for symbol '{s}': {}", .{ sym_name, sym });
1263 return error.SectionNotMappedForSymbol;
1264 };
1265 const source_sect = seg.sections.items[source_sect_id];
1266 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1267 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1268 const target_addr = target_sect.addr + target_mapping.offset;
1269 const n_value = sym.n_value - source_sect.addr + target_addr;
1270
1271 log.debug("resolving '{s}':{} as {s} symbol at 0x{x}", .{ sym_name, sym, tt, n_value });
1272
1273 // TODO there might be a more generic way of doing this.
1274 var n_sect: u16 = 0;
1275 for (self.load_commands.items) |cmd, cmd_id| {
1276 if (cmd != .Segment) break;
1277 if (cmd_id == target_mapping.target_seg_id) {
1278 n_sect += target_mapping.target_sect_id + 1;
1279 break;
1280 }
1281 n_sect += @intCast(u16, cmd.Segment.sections.items.len);
1282 }
1283
1284 const n_strx = try self.makeString(sym_name);
1285 try locs.entry.value.append(self.allocator, .{
1286 .inner = .{
1287 .n_strx = n_strx,
1288 .n_value = n_value,
1289 .n_type = macho.N_SECT,
1290 .n_desc = sym.n_desc,
1291 .n_sect = @intCast(u8, n_sect),
1292 },
1293 .tt = tt,
1294 .object_id = @intCast(u16, object_id),
1295 });
1296 }
1297 }
1298}
1299
1300fn doRelocs(self: *Zld) !void {
1301 for (self.objects.items) |object, object_id| {
1302 log.debug("\n\n", .{});
1303 log.debug("relocating object {s}", .{object.name});
1304
1305 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1306
1307 for (seg.sections.items) |sect, source_sect_id| {
1308 const segname = parseName(&sect.segname);
1309 const sectname = parseName(&sect.sectname);
1310
1311 var code = try self.allocator.alloc(u8, sect.size);
1312 _ = try object.file.preadAll(code, sect.offset);
1313 defer self.allocator.free(code);
1314
1315 // Parse relocs (if any)
1316 var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
1317 defer self.allocator.free(raw_relocs);
1318 _ = try object.file.preadAll(raw_relocs, sect.reloff);
1319 const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);
1320
1321 // Get mapping
1322 const target_mapping = self.mappings.get(.{
1323 .object_id = @intCast(u16, object_id),
1324 .source_sect_id = @intCast(u16, source_sect_id),
1325 }) orelse {
1326 log.debug("no mapping for {s},{s}; skipping", .{ segname, sectname });
1327 continue;
1328 };
1329 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1330 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1331 const target_sect_addr = target_sect.addr + target_mapping.offset;
1332 const target_sect_off = target_sect.offset + target_mapping.offset;
1333
1334 var addend: ?u64 = null;
1335 var sub: ?i64 = null;
1336
1337 for (relocs) |rel| {
1338 const off = @intCast(u32, rel.r_address);
1339 const this_addr = target_sect_addr + off;
1340
1341 switch (self.arch.?) {
1342 .aarch64 => {
1343 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1344 log.debug("{s}", .{rel_type});
1345 log.debug(" | source address 0x{x}", .{this_addr});
1346 log.debug(" | offset 0x{x}", .{off});
1347
1348 if (rel_type == .ARM64_RELOC_ADDEND) {
1349 addend = rel.r_symbolnum;
1350 log.debug(" | calculated addend = 0x{x}", .{addend});
1351 // TODO followed by either PAGE21 or PAGEOFF12 only.
1352 continue;
1353 }
1354 },
1355 .x86_64 => {
1356 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1357 log.debug("{s}", .{rel_type});
1358 log.debug(" | source address 0x{x}", .{this_addr});
1359 log.debug(" | offset 0x{x}", .{off});
1360 },
1361 else => {},
1362 }
1363
1364 const target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel);
1365 log.debug(" | target address 0x{x}", .{target_addr});
1366 if (rel.r_extern == 1) {
1367 const target_symname = object.getString(object.symtab.items[rel.r_symbolnum].n_strx);
1368 log.debug(" | target symbol '{s}'", .{target_symname});
1369 } else {
1370 const target_sectname = seg.sections.items[rel.r_symbolnum - 1].sectname;
1371 log.debug(" | target section '{s}'", .{parseName(&target_sectname)});
1372 }
1373
1374 switch (self.arch.?) {
1375 .x86_64 => {
1376 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1377
1378 switch (rel_type) {
1379 .X86_64_RELOC_BRANCH => {
1380 assert(rel.r_length == 2);
1381 const inst = code[off..][0..4];
1382 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1383 mem.writeIntLittle(u32, inst, displacement);
1384 },
1385 .X86_64_RELOC_GOT_LOAD => {
1386 assert(rel.r_length == 2);
1387 const inst = code[off..][0..4];
1388 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1389
1390 blk: {
1391 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1392 const got = data_const_seg.sections.items[self.got_section_index.?];
1393 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1394 log.debug(" | rewriting to leaq", .{});
1395 code[off - 2] = 0x8d;
1396 }
1397
1398 mem.writeIntLittle(u32, inst, displacement);
1399 },
1400 .X86_64_RELOC_GOT => {
1401 assert(rel.r_length == 2);
1402 // TODO Instead of referring to the target symbol directly, we refer to it
1403 // indirectly via GOT. Getting actual target address should be done in the
1404 // helper relocTargetAddr function rather than here.
1405 const sym = object.symtab.items[rel.r_symbolnum];
1406 const sym_name = try self.allocator.dupe(u8, object.getString(sym.n_strx));
1407 const res = try self.nonlazy_pointers.getOrPut(self.allocator, sym_name);
1408 defer if (res.found_existing) self.allocator.free(sym_name);
1409
1410 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1411 const got = data_const_seg.sections.items[self.got_section_index.?];
1412
1413 if (!res.found_existing) {
1414 const index = @intCast(u32, self.nonlazy_pointers.items().len) - 1;
1415 assert(index < max_local_got_indirections); // TODO This is just a temp solution.
1416 res.entry.value = .{
1417 .index = index,
1418 .target_addr = target_addr,
1419 };
1420 var buf: [@sizeOf(u64)]u8 = undefined;
1421 mem.writeIntLittle(u64, &buf, target_addr);
1422 const got_offset = got.offset + (index + self.nonlazy_imports.items().len) * @sizeOf(u64);
1423
1424 log.debug(" | GOT off 0x{x}", .{got.offset});
1425 log.debug(" | writing GOT entry 0x{x} at 0x{x}", .{ target_addr, got_offset });
1426
1427 try self.file.?.pwriteAll(&buf, got_offset);
1428 }
1429
1430 const index = res.entry.value.index + self.nonlazy_imports.items().len;
1431 const actual_target_addr = got.addr + index * @sizeOf(u64);
1432
1433 log.debug(" | GOT addr 0x{x}", .{got.addr});
1434 log.debug(" | actual target address in GOT 0x{x}", .{actual_target_addr});
1435
1436 const inst = code[off..][0..4];
1437 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, actual_target_addr) - @intCast(i64, this_addr) - 4));
1438 mem.writeIntLittle(u32, inst, displacement);
1439 },
1440 .X86_64_RELOC_TLV => {
1441 assert(rel.r_length == 2);
1442 // We need to rewrite the opcode from movq to leaq.
1443 code[off - 2] = 0x8d;
1444 // Add displacement.
1445 const inst = code[off..][0..4];
1446 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1447 mem.writeIntLittle(u32, inst, displacement);
1448 },
1449 .X86_64_RELOC_SIGNED,
1450 .X86_64_RELOC_SIGNED_1,
1451 .X86_64_RELOC_SIGNED_2,
1452 .X86_64_RELOC_SIGNED_4,
1453 => {
1454 assert(rel.r_length == 2);
1455 const inst = code[off..][0..4];
1456 const offset = @intCast(i64, mem.readIntLittle(i32, inst));
1457 log.debug(" | calculated addend 0x{x}", .{offset});
1458 const actual_target_addr = blk: {
1459 if (rel.r_extern == 1) {
1460 break :blk @intCast(i64, target_addr) + offset;
1461 } else {
1462 const correction: i4 = switch (rel_type) {
1463 .X86_64_RELOC_SIGNED => 0,
1464 .X86_64_RELOC_SIGNED_1 => 1,
1465 .X86_64_RELOC_SIGNED_2 => 2,
1466 .X86_64_RELOC_SIGNED_4 => 4,
1467 else => unreachable,
1468 };
1469 log.debug(" | calculated correction 0x{x}", .{correction});
1470
1471 // The value encoded in the instruction is a displacement - 4 - correction.
1472 // To obtain the adjusted target address in the final binary, we need
1473 // calculate the original target address within the object file, establish
1474 // what the offset from the original target section was, and apply this
1475 // offset to the resultant target section with this relocated binary.
1476 const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1);
1477 const target_map = self.mappings.get(.{
1478 .object_id = @intCast(u16, object_id),
1479 .source_sect_id = orig_sect_id,
1480 }) orelse unreachable;
1481 const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1482 const orig_sect = orig_seg.sections.items[orig_sect_id];
1483 const orig_offset = off + offset + 4 + correction - @intCast(i64, orig_sect.addr);
1484 log.debug(" | original offset 0x{x}", .{orig_offset});
1485 const adjusted = @intCast(i64, target_addr) + orig_offset;
1486 log.debug(" | adjusted target address 0x{x}", .{adjusted});
1487 break :blk adjusted - correction;
1488 }
1489 };
1490 const result = actual_target_addr - @intCast(i64, this_addr) - 4;
1491 const displacement = @bitCast(u32, @intCast(i32, result));
1492 mem.writeIntLittle(u32, inst, displacement);
1493 },
1494 .X86_64_RELOC_SUBTRACTOR => {
1495 sub = @intCast(i64, target_addr);
1496 },
1497 .X86_64_RELOC_UNSIGNED => {
1498 switch (rel.r_length) {
1499 3 => {
1500 const inst = code[off..][0..8];
1501 const offset = mem.readIntLittle(i64, inst);
1502
1503 const result = outer: {
1504 if (rel.r_extern == 1) {
1505 log.debug(" | calculated addend 0x{x}", .{offset});
1506 if (sub) |s| {
1507 break :outer @intCast(i64, target_addr) - s + offset;
1508 } else {
1509 break :outer @intCast(i64, target_addr) + offset;
1510 }
1511 } else {
1512 // The value encoded in the instruction is an absolute offset
1513 // from the start of MachO header to the target address in the
1514 // object file. To extract the address, we calculate the offset from
1515 // the beginning of the source section to the address, and apply it to
1516 // the target address value.
1517 const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1);
1518 const target_map = self.mappings.get(.{
1519 .object_id = @intCast(u16, object_id),
1520 .source_sect_id = orig_sect_id,
1521 }) orelse unreachable;
1522 const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1523 const orig_sect = orig_seg.sections.items[orig_sect_id];
1524 const orig_offset = offset - @intCast(i64, orig_sect.addr);
1525 const actual_target_addr = inner: {
1526 if (sub) |s| {
1527 break :inner @intCast(i64, target_addr) - s + orig_offset;
1528 } else {
1529 break :inner @intCast(i64, target_addr) + orig_offset;
1530 }
1531 };
1532 log.debug(" | adjusted target address 0x{x}", .{actual_target_addr});
1533 break :outer actual_target_addr;
1534 }
1535 };
1536 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1537 sub = null;
1538
1539 rebases: {
1540 var hit: bool = false;
1541 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1542 if (self.data_section_index) |index| {
1543 if (index == target_mapping.target_sect_id) hit = true;
1544 }
1545 }
1546 if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
1547 if (self.data_const_section_index) |index| {
1548 if (index == target_mapping.target_sect_id) hit = true;
1549 }
1550 }
1551
1552 if (!hit) break :rebases;
1553
1554 try self.local_rebases.append(self.allocator, .{
1555 .offset = this_addr - target_seg.inner.vmaddr,
1556 .segment_id = target_mapping.target_seg_id,
1557 });
1558 }
1559 // TLV is handled via a separate offset mechanism.
1560 // Calculate the offset to the initializer.
1561 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1562 assert(rel.r_extern == 1);
1563 const sym = object.symtab.items[rel.r_symbolnum];
1564 if (isImport(&sym)) break :tlv;
1565
1566 const base_addr = blk: {
1567 if (self.tlv_data_section_index) |index| {
1568 const tlv_data = target_seg.sections.items[index];
1569 break :blk tlv_data.addr;
1570 } else {
1571 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1572 break :blk tlv_bss.addr;
1573 }
1574 };
1575 // Since we require TLV data to always preceed TLV bss section, we calculate
1576 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1577 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1578 }
1579 },
1580 2 => {
1581 const inst = code[off..][0..4];
1582 const offset = mem.readIntLittle(i32, inst);
1583 log.debug(" | calculated addend 0x{x}", .{offset});
1584 const result = if (sub) |s|
1585 @intCast(i64, target_addr) - s + offset
1586 else
1587 @intCast(i64, target_addr) + offset;
1588 mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result)));
1589 sub = null;
1590 },
1591 else => |len| {
1592 log.err("unexpected relocation length 0x{x}", .{len});
1593 return error.UnexpectedRelocationLength;
1594 },
1595 }
1596 },
1597 }
1598 },
1599 .aarch64 => {
1600 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1601
1602 switch (rel_type) {
1603 .ARM64_RELOC_BRANCH26 => {
1604 assert(rel.r_length == 2);
1605 const inst = code[off..][0..4];
1606 const displacement = @intCast(
1607 i28,
1608 @intCast(i64, target_addr) - @intCast(i64, this_addr),
1609 );
1610 var parsed = mem.bytesAsValue(
1611 meta.TagPayload(
1612 aarch64.Instruction,
1613 aarch64.Instruction.UnconditionalBranchImmediate,
1614 ),
1615 inst,
1616 );
1617 parsed.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2);
1618 },
1619 .ARM64_RELOC_PAGE21,
1620 .ARM64_RELOC_GOT_LOAD_PAGE21,
1621 .ARM64_RELOC_TLVP_LOAD_PAGE21,
1622 => {
1623 assert(rel.r_length == 2);
1624 const inst = code[off..][0..4];
1625 const ta = if (addend) |a| target_addr + a else target_addr;
1626 const this_page = @intCast(i32, this_addr >> 12);
1627 const target_page = @intCast(i32, ta >> 12);
1628 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1629 log.debug(" | moving by {} pages", .{pages});
1630 var parsed = mem.bytesAsValue(
1631 meta.TagPayload(
1632 aarch64.Instruction,
1633 aarch64.Instruction.PCRelativeAddress,
1634 ),
1635 inst,
1636 );
1637 parsed.immhi = @truncate(u19, pages >> 2);
1638 parsed.immlo = @truncate(u2, pages);
1639 addend = null;
1640 },
1641 .ARM64_RELOC_PAGEOFF12,
1642 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1643 => {
1644 const inst = code[off..][0..4];
1645 if (aarch64IsArithmetic(inst)) {
1646 log.debug(" | detected ADD opcode", .{});
1647 // add
1648 var parsed = mem.bytesAsValue(
1649 meta.TagPayload(
1650 aarch64.Instruction,
1651 aarch64.Instruction.AddSubtractImmediate,
1652 ),
1653 inst,
1654 );
1655 const ta = if (addend) |a| target_addr + a else target_addr;
1656 const narrowed = @truncate(u12, ta);
1657 parsed.imm12 = narrowed;
1658 } else {
1659 log.debug(" | detected LDR/STR opcode", .{});
1660 // ldr/str
1661 var parsed = mem.bytesAsValue(
1662 meta.TagPayload(
1663 aarch64.Instruction,
1664 aarch64.Instruction.LoadStoreRegister,
1665 ),
1666 inst,
1667 );
1668
1669 const ta = if (addend) |a| target_addr + a else target_addr;
1670 const narrowed = @truncate(u12, ta);
1671 log.debug(" | narrowed 0x{x}", .{narrowed});
1672 log.debug(" | parsed.size 0x{x}", .{parsed.size});
1673
1674 if (rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12) blk: {
1675 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1676 const got = data_const_seg.sections.items[self.got_section_index.?];
1677 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1678
1679 log.debug(" | rewriting to add", .{});
1680 mem.writeIntLittle(u32, inst, aarch64.Instruction.add(
1681 @intToEnum(aarch64.Register, parsed.rt),
1682 @intToEnum(aarch64.Register, parsed.rn),
1683 narrowed,
1684 false,
1685 ).toU32());
1686 addend = null;
1687 continue;
1688 }
1689
1690 const offset: u12 = blk: {
1691 if (parsed.size == 0) {
1692 if (parsed.v == 1) {
1693 // 128-bit SIMD is scaled by 16.
1694 break :blk try math.divExact(u12, narrowed, 16);
1695 }
1696 // Otherwise, 8-bit SIMD or ldrb.
1697 break :blk narrowed;
1698 } else {
1699 const denom: u4 = try math.powi(u4, 2, parsed.size);
1700 break :blk try math.divExact(u12, narrowed, denom);
1701 }
1702 };
1703 parsed.offset = offset;
1704 }
1705 addend = null;
1706 },
1707 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
1708 const RegInfo = struct {
1709 rd: u5,
1710 rn: u5,
1711 size: u1,
1712 };
1713 const inst = code[off..][0..4];
1714 const parsed: RegInfo = blk: {
1715 if (aarch64IsArithmetic(inst)) {
1716 const curr = mem.bytesAsValue(
1717 meta.TagPayload(
1718 aarch64.Instruction,
1719 aarch64.Instruction.AddSubtractImmediate,
1720 ),
1721 inst,
1722 );
1723 break :blk .{ .rd = curr.rd, .rn = curr.rn, .size = curr.sf };
1724 } else {
1725 const curr = mem.bytesAsValue(
1726 meta.TagPayload(
1727 aarch64.Instruction,
1728 aarch64.Instruction.LoadStoreRegister,
1729 ),
1730 inst,
1731 );
1732 break :blk .{ .rd = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) };
1733 }
1734 };
1735 const ta = if (addend) |a| target_addr + a else target_addr;
1736 const narrowed = @truncate(u12, ta);
1737 log.debug(" | rewriting TLV access to ADD opcode", .{});
1738 // For TLV, we always generate an add instruction.
1739 mem.writeIntLittle(u32, inst, aarch64.Instruction.add(
1740 @intToEnum(aarch64.Register, parsed.rd),
1741 @intToEnum(aarch64.Register, parsed.rn),
1742 narrowed,
1743 false,
1744 ).toU32());
1745 },
1746 .ARM64_RELOC_SUBTRACTOR => {
1747 sub = @intCast(i64, target_addr);
1748 },
1749 .ARM64_RELOC_UNSIGNED => {
1750 switch (rel.r_length) {
1751 3 => {
1752 const inst = code[off..][0..8];
1753 const offset = mem.readIntLittle(i64, inst);
1754 log.debug(" | calculated addend 0x{x}", .{offset});
1755 const result = if (sub) |s|
1756 @intCast(i64, target_addr) - s + offset
1757 else
1758 @intCast(i64, target_addr) + offset;
1759 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1760 sub = null;
1761
1762 rebases: {
1763 var hit: bool = false;
1764 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1765 if (self.data_section_index) |index| {
1766 if (index == target_mapping.target_sect_id) hit = true;
1767 }
1768 }
1769 if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
1770 if (self.data_const_section_index) |index| {
1771 if (index == target_mapping.target_sect_id) hit = true;
1772 }
1773 }
1774
1775 if (!hit) break :rebases;
1776
1777 try self.local_rebases.append(self.allocator, .{
1778 .offset = this_addr - target_seg.inner.vmaddr,
1779 .segment_id = target_mapping.target_seg_id,
1780 });
1781 }
1782 // TLV is handled via a separate offset mechanism.
1783 // Calculate the offset to the initializer.
1784 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1785 assert(rel.r_extern == 1);
1786 const sym = object.symtab.items[rel.r_symbolnum];
1787 if (isImport(&sym)) break :tlv;
1788
1789 const base_addr = blk: {
1790 if (self.tlv_data_section_index) |index| {
1791 const tlv_data = target_seg.sections.items[index];
1792 break :blk tlv_data.addr;
1793 } else {
1794 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1795 break :blk tlv_bss.addr;
1796 }
1797 };
1798 // Since we require TLV data to always preceed TLV bss section, we calculate
1799 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1800 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1801 }
1802 },
1803 2 => {
1804 const inst = code[off..][0..4];
1805 const offset = mem.readIntLittle(i32, inst);
1806 log.debug(" | calculated addend 0x{x}", .{offset});
1807 const result = if (sub) |s|
1808 @intCast(i64, target_addr) - s + offset
1809 else
1810 @intCast(i64, target_addr) + offset;
1811 mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result)));
1812 sub = null;
1813 },
1814 else => |len| {
1815 log.err("unexpected relocation length 0x{x}", .{len});
1816 return error.UnexpectedRelocationLength;
1817 },
1818 }
1819 },
1820 .ARM64_RELOC_POINTER_TO_GOT => return error.TODOArm64RelocPointerToGot,
1821 else => unreachable,
1822 }
1823 },
1824 else => unreachable,
1825 }
1826 }
1827
1828 log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
1829 segname,
1830 sectname,
1831 object.name,
1832 target_sect_off,
1833 target_sect_off + code.len,
1834 });
1835
1836 if (target_sect.flags == macho.S_ZEROFILL or
1837 target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or
1838 target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES)
1839 {
1840 log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
1841 parseName(&target_sect.segname),
1842 parseName(&target_sect.sectname),
1843 target_sect_off,
1844 target_sect_off + code.len,
1845 });
1846 // Zero-out the space
1847 var zeroes = try self.allocator.alloc(u8, code.len);
1848 defer self.allocator.free(zeroes);
1849 mem.set(u8, zeroes, 0);
1850 try self.file.?.pwriteAll(zeroes, target_sect_off);
1851 } else {
1852 try self.file.?.pwriteAll(code, target_sect_off);
1853 }
1854 }
1855 }
1856}
1857
1858fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64 {
1859 const object = self.objects.items[object_id];
1860 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1861 const target_addr = blk: {
1862 if (rel.r_extern == 1) {
1863 const sym = object.symtab.items[rel.r_symbolnum];
1864 if (isLocal(&sym) or isExport(&sym)) {
1865 // Relocate using section offsets only.
1866 const target_mapping = self.mappings.get(.{
1867 .object_id = object_id,
1868 .source_sect_id = sym.n_sect - 1,
1869 }) orelse unreachable;
1870 const source_sect = seg.sections.items[target_mapping.source_sect_id];
1871 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1872 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1873 const target_sect_addr = target_sect.addr + target_mapping.offset;
1874 log.debug(" | symbol local to object", .{});
1875 break :blk target_sect_addr + sym.n_value - source_sect.addr;
1876 } else if (isImport(&sym)) {
1877 // Relocate to either the artifact's local symbol, or an import from
1878 // shared library.
1879 const sym_name = object.getString(sym.n_strx);
1880 if (self.locals.get(sym_name)) |locs| {
1881 var n_value: ?u64 = null;
1882 for (locs.items) |loc| {
1883 switch (loc.tt) {
1884 .Global => {
1885 n_value = loc.inner.n_value;
1886 break;
1887 },
1888 .WeakGlobal => {
1889 n_value = loc.inner.n_value;
1890 },
1891 .Local => {},
1892 }
1893 }
1894 if (n_value) |v| {
1895 break :blk v;
1896 }
1897 log.err("local symbol export '{s}' not found", .{sym_name});
1898 return error.LocalSymbolExportNotFound;
1899 } else if (self.lazy_imports.get(sym_name)) |ext| {
1900 const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1901 const stubs = segment.sections.items[self.stubs_section_index.?];
1902 break :blk stubs.addr + ext.index * stubs.reserved2;
1903 } else if (self.nonlazy_imports.get(sym_name)) |ext| {
1904 const segment = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1905 const got = segment.sections.items[self.got_section_index.?];
1906 break :blk got.addr + ext.index * @sizeOf(u64);
1907 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
1908 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1909 const tlv = segment.sections.items[self.tlv_section_index.?];
1910 break :blk tlv.addr + self.tlv_bootstrap.?.index * @sizeOf(u64);
1911 } else {
1912 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
1913 return error.FailedToResolveRelocationTarget;
1914 }
1915 } else {
1916 log.err("unexpected symbol {}, {s}", .{ sym, object.getString(sym.n_strx) });
1917 return error.UnexpectedSymbolWhenRelocating;
1918 }
1919 } else {
1920 // TODO I think we need to reparse the relocation_info as scattered_relocation_info
1921 // here to get the actual section plus offset into that section of the relocated
1922 // symbol. Unless the fine-grained location is encoded within the cell in the code
1923 // buffer?
1924 const target_mapping = self.mappings.get(.{
1925 .object_id = object_id,
1926 .source_sect_id = @intCast(u16, rel.r_symbolnum - 1),
1927 }) orelse unreachable;
1928 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1929 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1930 break :blk target_sect.addr + target_mapping.offset;
1931 }
1932 };
1933 return target_addr;
1934}
1935
1936fn populateMetadata(self: *Zld) !void {
1937 if (self.pagezero_segment_cmd_index == null) {
1938 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1939 try self.load_commands.append(self.allocator, .{
1940 .Segment = SegmentCommand.empty(.{
1941 .cmd = macho.LC_SEGMENT_64,
1942 .cmdsize = @sizeOf(macho.segment_command_64),
1943 .segname = makeStaticString("__PAGEZERO"),
1944 .vmaddr = 0,
1945 .vmsize = 0x100000000, // size always set to 4GB
1946 .fileoff = 0,
1947 .filesize = 0,
1948 .maxprot = 0,
1949 .initprot = 0,
1950 .nsects = 0,
1951 .flags = 0,
1952 }),
1953 });
1954 }
1955
1956 if (self.text_segment_cmd_index == null) {
1957 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1958 try self.load_commands.append(self.allocator, .{
1959 .Segment = SegmentCommand.empty(.{
1960 .cmd = macho.LC_SEGMENT_64,
1961 .cmdsize = @sizeOf(macho.segment_command_64),
1962 .segname = makeStaticString("__TEXT"),
1963 .vmaddr = 0x100000000, // always starts at 4GB
1964 .vmsize = 0,
1965 .fileoff = 0,
1966 .filesize = 0,
1967 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
1968 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
1969 .nsects = 0,
1970 .flags = 0,
1971 }),
1972 });
1973 }
1974
1975 if (self.text_section_index == null) {
1976 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1977 self.text_section_index = @intCast(u16, text_seg.sections.items.len);
1978 const alignment: u2 = switch (self.arch.?) {
1979 .x86_64 => 0,
1980 .aarch64 => 2,
1981 else => unreachable, // unhandled architecture type
1982 };
1983 try text_seg.addSection(self.allocator, .{
1984 .sectname = makeStaticString("__text"),
1985 .segname = makeStaticString("__TEXT"),
1986 .addr = 0,
1987 .size = 0,
1988 .offset = 0,
1989 .@"align" = alignment,
1990 .reloff = 0,
1991 .nreloc = 0,
1992 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
1993 .reserved1 = 0,
1994 .reserved2 = 0,
1995 .reserved3 = 0,
1996 });
1997 }
1998
1999 if (self.stubs_section_index == null) {
2000 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2001 self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);
2002 const alignment: u2 = switch (self.arch.?) {
2003 .x86_64 => 0,
2004 .aarch64 => 2,
2005 else => unreachable, // unhandled architecture type
2006 };
2007 const stub_size: u4 = switch (self.arch.?) {
2008 .x86_64 => 6,
2009 .aarch64 => 3 * @sizeOf(u32),
2010 else => unreachable, // unhandled architecture type
2011 };
2012 try text_seg.addSection(self.allocator, .{
2013 .sectname = makeStaticString("__stubs"),
2014 .segname = makeStaticString("__TEXT"),
2015 .addr = 0,
2016 .size = 0,
2017 .offset = 0,
2018 .@"align" = alignment,
2019 .reloff = 0,
2020 .nreloc = 0,
2021 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2022 .reserved1 = 0,
2023 .reserved2 = stub_size,
2024 .reserved3 = 0,
2025 });
2026 }
2027
2028 if (self.stub_helper_section_index == null) {
2029 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2030 self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);
2031 const alignment: u2 = switch (self.arch.?) {
2032 .x86_64 => 0,
2033 .aarch64 => 2,
2034 else => unreachable, // unhandled architecture type
2035 };
2036 const stub_helper_size: u6 = switch (self.arch.?) {
2037 .x86_64 => 15,
2038 .aarch64 => 6 * @sizeOf(u32),
2039 else => unreachable,
2040 };
2041 try text_seg.addSection(self.allocator, .{
2042 .sectname = makeStaticString("__stub_helper"),
2043 .segname = makeStaticString("__TEXT"),
2044 .addr = 0,
2045 .size = stub_helper_size,
2046 .offset = 0,
2047 .@"align" = alignment,
2048 .reloff = 0,
2049 .nreloc = 0,
2050 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2051 .reserved1 = 0,
2052 .reserved2 = 0,
2053 .reserved3 = 0,
2054 });
2055 }
2056
2057 if (self.data_const_segment_cmd_index == null) {
2058 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2059 try self.load_commands.append(self.allocator, .{
2060 .Segment = SegmentCommand.empty(.{
2061 .cmd = macho.LC_SEGMENT_64,
2062 .cmdsize = @sizeOf(macho.segment_command_64),
2063 .segname = makeStaticString("__DATA_CONST"),
2064 .vmaddr = 0,
2065 .vmsize = 0,
2066 .fileoff = 0,
2067 .filesize = 0,
2068 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2069 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2070 .nsects = 0,
2071 .flags = 0,
2072 }),
2073 });
2074 }
2075
2076 if (self.got_section_index == null) {
2077 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2078 self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);
2079 try data_const_seg.addSection(self.allocator, .{
2080 .sectname = makeStaticString("__got"),
2081 .segname = makeStaticString("__DATA_CONST"),
2082 .addr = 0,
2083 .size = 0,
2084 .offset = 0,
2085 .@"align" = 3, // 2^3 = @sizeOf(u64)
2086 .reloff = 0,
2087 .nreloc = 0,
2088 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2089 .reserved1 = 0,
2090 .reserved2 = 0,
2091 .reserved3 = 0,
2092 });
2093 }
2094
2095 if (self.data_segment_cmd_index == null) {
2096 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2097 try self.load_commands.append(self.allocator, .{
2098 .Segment = SegmentCommand.empty(.{
2099 .cmd = macho.LC_SEGMENT_64,
2100 .cmdsize = @sizeOf(macho.segment_command_64),
2101 .segname = makeStaticString("__DATA"),
2102 .vmaddr = 0,
2103 .vmsize = 0,
2104 .fileoff = 0,
2105 .filesize = 0,
2106 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2107 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2108 .nsects = 0,
2109 .flags = 0,
2110 }),
2111 });
2112 }
2113
2114 if (self.la_symbol_ptr_section_index == null) {
2115 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2116 self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);
2117 try data_seg.addSection(self.allocator, .{
2118 .sectname = makeStaticString("__la_symbol_ptr"),
2119 .segname = makeStaticString("__DATA"),
2120 .addr = 0,
2121 .size = 0,
2122 .offset = 0,
2123 .@"align" = 3, // 2^3 = @sizeOf(u64)
2124 .reloff = 0,
2125 .nreloc = 0,
2126 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2127 .reserved1 = 0,
2128 .reserved2 = 0,
2129 .reserved3 = 0,
2130 });
2131 }
2132
2133 if (self.data_section_index == null) {
2134 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2135 self.data_section_index = @intCast(u16, data_seg.sections.items.len);
2136 try data_seg.addSection(self.allocator, .{
2137 .sectname = makeStaticString("__data"),
2138 .segname = makeStaticString("__DATA"),
2139 .addr = 0,
2140 .size = 0,
2141 .offset = 0,
2142 .@"align" = 3, // 2^3 = @sizeOf(u64)
2143 .reloff = 0,
2144 .nreloc = 0,
2145 .flags = macho.S_REGULAR,
2146 .reserved1 = 0,
2147 .reserved2 = 0,
2148 .reserved3 = 0,
2149 });
2150 }
2151
2152 if (self.linkedit_segment_cmd_index == null) {
2153 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2154 try self.load_commands.append(self.allocator, .{
2155 .Segment = SegmentCommand.empty(.{
2156 .cmd = macho.LC_SEGMENT_64,
2157 .cmdsize = @sizeOf(macho.segment_command_64),
2158 .segname = makeStaticString("__LINKEDIT"),
2159 .vmaddr = 0,
2160 .vmsize = 0,
2161 .fileoff = 0,
2162 .filesize = 0,
2163 .maxprot = macho.VM_PROT_READ,
2164 .initprot = macho.VM_PROT_READ,
2165 .nsects = 0,
2166 .flags = 0,
2167 }),
2168 });
2169 }
2170
2171 if (self.dyld_info_cmd_index == null) {
2172 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
2173 try self.load_commands.append(self.allocator, .{
2174 .DyldInfoOnly = .{
2175 .cmd = macho.LC_DYLD_INFO_ONLY,
2176 .cmdsize = @sizeOf(macho.dyld_info_command),
2177 .rebase_off = 0,
2178 .rebase_size = 0,
2179 .bind_off = 0,
2180 .bind_size = 0,
2181 .weak_bind_off = 0,
2182 .weak_bind_size = 0,
2183 .lazy_bind_off = 0,
2184 .lazy_bind_size = 0,
2185 .export_off = 0,
2186 .export_size = 0,
2187 },
2188 });
2189 }
2190
2191 if (self.symtab_cmd_index == null) {
2192 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2193 try self.load_commands.append(self.allocator, .{
2194 .Symtab = .{
2195 .cmd = macho.LC_SYMTAB,
2196 .cmdsize = @sizeOf(macho.symtab_command),
2197 .symoff = 0,
2198 .nsyms = 0,
2199 .stroff = 0,
2200 .strsize = 0,
2201 },
2202 });
2203 try self.strtab.append(self.allocator, 0);
2204 }
2205
2206 if (self.dysymtab_cmd_index == null) {
2207 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2208 try self.load_commands.append(self.allocator, .{
2209 .Dysymtab = .{
2210 .cmd = macho.LC_DYSYMTAB,
2211 .cmdsize = @sizeOf(macho.dysymtab_command),
2212 .ilocalsym = 0,
2213 .nlocalsym = 0,
2214 .iextdefsym = 0,
2215 .nextdefsym = 0,
2216 .iundefsym = 0,
2217 .nundefsym = 0,
2218 .tocoff = 0,
2219 .ntoc = 0,
2220 .modtaboff = 0,
2221 .nmodtab = 0,
2222 .extrefsymoff = 0,
2223 .nextrefsyms = 0,
2224 .indirectsymoff = 0,
2225 .nindirectsyms = 0,
2226 .extreloff = 0,
2227 .nextrel = 0,
2228 .locreloff = 0,
2229 .nlocrel = 0,
2230 },
2231 });
2232 }
2233
2234 if (self.dylinker_cmd_index == null) {
2235 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
2236 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2237 u64,
2238 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
2239 @sizeOf(u64),
2240 ));
2241 var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{
2242 .cmd = macho.LC_LOAD_DYLINKER,
2243 .cmdsize = cmdsize,
2244 .name = @sizeOf(macho.dylinker_command),
2245 });
2246 dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
2247 mem.set(u8, dylinker_cmd.data, 0);
2248 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
2249 try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd });
2250 }
2251
2252 if (self.libsystem_cmd_index == null) {
2253 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
2254 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2255 u64,
2256 @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
2257 @sizeOf(u64),
2258 ));
2259 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
2260 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
2261 const min_version = 0x0;
2262 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
2263 .cmd = macho.LC_LOAD_DYLIB,
2264 .cmdsize = cmdsize,
2265 .dylib = .{
2266 .name = @sizeOf(macho.dylib_command),
2267 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
2268 .current_version = min_version,
2269 .compatibility_version = min_version,
2270 },
2271 });
2272 dylib_cmd.data = try self.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2273 mem.set(u8, dylib_cmd.data, 0);
2274 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
2275 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
2276 }
2277
2278 if (self.main_cmd_index == null) {
2279 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
2280 try self.load_commands.append(self.allocator, .{
2281 .Main = .{
2282 .cmd = macho.LC_MAIN,
2283 .cmdsize = @sizeOf(macho.entry_point_command),
2284 .entryoff = 0x0,
2285 .stacksize = 0,
2286 },
2287 });
2288 }
2289
2290 if (self.source_version_cmd_index == null) {
2291 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
2292 try self.load_commands.append(self.allocator, .{
2293 .SourceVersion = .{
2294 .cmd = macho.LC_SOURCE_VERSION,
2295 .cmdsize = @sizeOf(macho.source_version_command),
2296 .version = 0x0,
2297 },
2298 });
2299 }
2300
2301 if (self.uuid_cmd_index == null) {
2302 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
2303 var uuid_cmd: macho.uuid_command = .{
2304 .cmd = macho.LC_UUID,
2305 .cmdsize = @sizeOf(macho.uuid_command),
2306 .uuid = undefined,
2307 };
2308 std.crypto.random.bytes(&uuid_cmd.uuid);
2309 try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd });
2310 }
2311
2312 if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
2313 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
2314 try self.load_commands.append(self.allocator, .{
2315 .LinkeditData = .{
2316 .cmd = macho.LC_CODE_SIGNATURE,
2317 .cmdsize = @sizeOf(macho.linkedit_data_command),
2318 .dataoff = 0,
2319 .datasize = 0,
2320 },
2321 });
2322 }
2323
2324 if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) {
2325 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
2326 try self.load_commands.append(self.allocator, .{
2327 .LinkeditData = .{
2328 .cmd = macho.LC_DATA_IN_CODE,
2329 .cmdsize = @sizeOf(macho.linkedit_data_command),
2330 .dataoff = 0,
2331 .datasize = 0,
2332 },
2333 });
2334 }
2335}
2336
2337fn flush(self: *Zld) !void {
2338 if (self.bss_section_index) |index| {
2339 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2340 const sect = &seg.sections.items[index];
2341 sect.offset = 0;
2342 }
2343
2344 if (self.tlv_bss_section_index) |index| {
2345 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2346 const sect = &seg.sections.items[index];
2347 sect.offset = 0;
2348 }
2349
2350 if (self.tlv_section_index) |index| {
2351 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2352 const sect = &seg.sections.items[index];
2353
2354 var buffer = try self.allocator.alloc(u8, sect.size);
2355 defer self.allocator.free(buffer);
2356 _ = try self.file.?.preadAll(buffer, sect.offset);
2357
2358 var stream = std.io.fixedBufferStream(buffer);
2359 var writer = stream.writer();
2360
2361 const seek_amt = 2 * @sizeOf(u64);
2362 while (self.threadlocal_offsets.popOrNull()) |offset| {
2363 try writer.context.seekBy(seek_amt);
2364 try writer.writeIntLittle(u64, offset);
2365 }
2366
2367 try self.file.?.pwriteAll(buffer, sect.offset);
2368 }
2369
2370 try self.setEntryPoint();
2371 try self.writeRebaseInfoTable();
2372 try self.writeBindInfoTable();
2373 try self.writeLazyBindInfoTable();
2374 try self.writeExportInfo();
2375 if (self.arch.? == .x86_64) {
2376 try self.writeDataInCode();
2377 }
2378
2379 {
2380 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2381 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2382 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2383 }
2384
2385 try self.writeDebugInfo();
2386 try self.writeSymbolTable();
2387 try self.writeDynamicSymbolTable();
2388 try self.writeStringTable();
2389
2390 {
2391 // Seal __LINKEDIT size
2392 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2393 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
2394 }
2395
2396 if (self.arch.? == .aarch64) {
2397 try self.writeCodeSignaturePadding();
2398 }
2399
2400 try self.writeLoadCommands();
2401 try self.writeHeader();
2402
2403 if (self.arch.? == .aarch64) {
2404 try self.writeCodeSignature();
2405 }
2406
2407 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
2408 try fs.cwd().copyFile(self.out_path.?, fs.cwd(), self.out_path.?, .{});
2409 }
2410}
2411
2412fn setEntryPoint(self: *Zld) !void {
2413 // TODO we should respect the -entry flag passed in by the user to set a custom
2414 // entrypoint. For now, assume default of `_main`.
2415 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2416 const text = seg.sections.items[self.text_section_index.?];
2417 const entry_syms = self.locals.get("_main") orelse return error.MissingMainEntrypoint;
2418
2419 var entry_sym: ?macho.nlist_64 = null;
2420 for (entry_syms.items) |es| {
2421 switch (es.tt) {
2422 .Global => {
2423 entry_sym = es.inner;
2424 break;
2425 },
2426 .WeakGlobal => {
2427 entry_sym = es.inner;
2428 },
2429 .Local => {},
2430 }
2431 }
2432 if (entry_sym == null) {
2433 log.err("no (weak) global definition of _main found", .{});
2434 return error.MissingMainEntrypoint;
2435 }
2436
2437 const name = try self.allocator.dupe(u8, "_main");
2438 try self.exports.putNoClobber(self.allocator, name, .{
2439 .n_strx = entry_sym.?.n_strx,
2440 .n_value = entry_sym.?.n_value,
2441 .n_type = macho.N_SECT | macho.N_EXT,
2442 .n_desc = entry_sym.?.n_desc,
2443 .n_sect = entry_sym.?.n_sect,
2444 });
2445
2446 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2447 ec.entryoff = @intCast(u32, entry_sym.?.n_value - seg.inner.vmaddr);
2448}
2449
2450fn writeRebaseInfoTable(self: *Zld) !void {
2451 var pointers = std.ArrayList(Pointer).init(self.allocator);
2452 defer pointers.deinit();
2453
2454 try pointers.ensureCapacity(pointers.items.len + self.local_rebases.items.len);
2455 pointers.appendSliceAssumeCapacity(self.local_rebases.items);
2456
2457 if (self.got_section_index) |idx| {
2458 // TODO this should be cleaned up!
2459 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_pointers.items().len);
2460 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2461 const sect = seg.sections.items[idx];
2462 const base_offset = sect.addr - seg.inner.vmaddr;
2463 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2464 const index_offset = @intCast(u32, self.nonlazy_imports.items().len);
2465 for (self.nonlazy_pointers.items()) |entry| {
2466 const index = index_offset + entry.value.index;
2467 pointers.appendAssumeCapacity(.{
2468 .offset = base_offset + index * @sizeOf(u64),
2469 .segment_id = segment_id,
2470 });
2471 }
2472 }
2473
2474 if (self.la_symbol_ptr_section_index) |idx| {
2475 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2476 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2477 const sect = seg.sections.items[idx];
2478 const base_offset = sect.addr - seg.inner.vmaddr;
2479 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2480 for (self.lazy_imports.items()) |entry| {
2481 pointers.appendAssumeCapacity(.{
2482 .offset = base_offset + entry.value.index * @sizeOf(u64),
2483 .segment_id = segment_id,
2484 });
2485 }
2486 }
2487
2488 std.sort.sort(Pointer, pointers.items, {}, pointerCmp);
2489
2490 const size = try rebaseInfoSize(pointers.items);
2491 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2492 defer self.allocator.free(buffer);
2493
2494 var stream = std.io.fixedBufferStream(buffer);
2495 try writeRebaseInfo(pointers.items, stream.writer());
2496
2497 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2498 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2499 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
2500 dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
2501 seg.inner.filesize += dyld_info.rebase_size;
2502
2503 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
2504
2505 try self.file.?.pwriteAll(buffer, dyld_info.rebase_off);
2506}
2507
2508fn writeBindInfoTable(self: *Zld) !void {
2509 var pointers = std.ArrayList(Pointer).init(self.allocator);
2510 defer pointers.deinit();
2511
2512 if (self.got_section_index) |idx| {
2513 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len);
2514 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2515 const sect = seg.sections.items[idx];
2516 const base_offset = sect.addr - seg.inner.vmaddr;
2517 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2518 for (self.nonlazy_imports.items()) |entry| {
2519 pointers.appendAssumeCapacity(.{
2520 .offset = base_offset + entry.value.index * @sizeOf(u64),
2521 .segment_id = segment_id,
2522 .dylib_ordinal = entry.value.dylib_ordinal,
2523 .name = entry.key,
2524 });
2525 }
2526 }
2527
2528 if (self.tlv_section_index) |idx| {
2529 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2530 const sect = seg.sections.items[idx];
2531 const base_offset = sect.addr - seg.inner.vmaddr;
2532 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2533 try pointers.append(.{
2534 .offset = base_offset + self.tlv_bootstrap.?.index * @sizeOf(u64),
2535 .segment_id = segment_id,
2536 .dylib_ordinal = self.tlv_bootstrap.?.dylib_ordinal,
2537 .name = "__tlv_bootstrap",
2538 });
2539 }
2540
2541 const size = try bindInfoSize(pointers.items);
2542 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2543 defer self.allocator.free(buffer);
2544
2545 var stream = std.io.fixedBufferStream(buffer);
2546 try writeBindInfo(pointers.items, stream.writer());
2547
2548 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2549 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2550 dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2551 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2552 seg.inner.filesize += dyld_info.bind_size;
2553
2554 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
2555
2556 try self.file.?.pwriteAll(buffer, dyld_info.bind_off);
2557}
2558
2559fn writeLazyBindInfoTable(self: *Zld) !void {
2560 var pointers = std.ArrayList(Pointer).init(self.allocator);
2561 defer pointers.deinit();
2562 try pointers.ensureCapacity(self.lazy_imports.items().len);
2563
2564 if (self.la_symbol_ptr_section_index) |idx| {
2565 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2566 const sect = seg.sections.items[idx];
2567 const base_offset = sect.addr - seg.inner.vmaddr;
2568 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2569 for (self.lazy_imports.items()) |entry| {
2570 pointers.appendAssumeCapacity(.{
2571 .offset = base_offset + entry.value.index * @sizeOf(u64),
2572 .segment_id = segment_id,
2573 .dylib_ordinal = entry.value.dylib_ordinal,
2574 .name = entry.key,
2575 });
2576 }
2577 }
2578
2579 const size = try lazyBindInfoSize(pointers.items);
2580 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2581 defer self.allocator.free(buffer);
2582
2583 var stream = std.io.fixedBufferStream(buffer);
2584 try writeLazyBindInfo(pointers.items, stream.writer());
2585
2586 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2587 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2588 dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2589 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2590 seg.inner.filesize += dyld_info.lazy_bind_size;
2591
2592 log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size });
2593
2594 try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2595 try self.populateLazyBindOffsetsInStubHelper(buffer);
2596}
2597
2598fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
2599 var stream = std.io.fixedBufferStream(buffer);
2600 var reader = stream.reader();
2601 var offsets = std.ArrayList(u32).init(self.allocator);
2602 try offsets.append(0);
2603 defer offsets.deinit();
2604 var valid_block = false;
2605
2606 while (true) {
2607 const inst = reader.readByte() catch |err| switch (err) {
2608 error.EndOfStream => break,
2609 else => return err,
2610 };
2611 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
2612 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2613
2614 switch (opcode) {
2615 macho.BIND_OPCODE_DO_BIND => {
2616 valid_block = true;
2617 },
2618 macho.BIND_OPCODE_DONE => {
2619 if (valid_block) {
2620 const offset = try stream.getPos();
2621 try offsets.append(@intCast(u32, offset));
2622 }
2623 valid_block = false;
2624 },
2625 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2626 var next = try reader.readByte();
2627 while (next != @as(u8, 0)) {
2628 next = try reader.readByte();
2629 }
2630 },
2631 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2632 _ = try leb.readULEB128(u64, reader);
2633 },
2634 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2635 _ = try leb.readULEB128(u64, reader);
2636 },
2637 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2638 _ = try leb.readILEB128(i64, reader);
2639 },
2640 else => {},
2641 }
2642 }
2643 assert(self.lazy_imports.items().len <= offsets.items.len);
2644
2645 const stub_size: u4 = switch (self.arch.?) {
2646 .x86_64 => 10,
2647 .aarch64 => 3 * @sizeOf(u32),
2648 else => unreachable,
2649 };
2650 const off: u4 = switch (self.arch.?) {
2651 .x86_64 => 1,
2652 .aarch64 => 2 * @sizeOf(u32),
2653 else => unreachable,
2654 };
2655 var buf: [@sizeOf(u32)]u8 = undefined;
2656 for (self.lazy_imports.items()) |entry| {
2657 const symbol = entry.value;
2658 const placeholder_off = self.stub_helper_stubs_start_off.? + symbol.index * stub_size + off;
2659 mem.writeIntLittle(u32, &buf, offsets.items[symbol.index]);
2660 try self.file.?.pwriteAll(&buf, placeholder_off);
2661 }
2662}
2663
2664fn writeExportInfo(self: *Zld) !void {
2665 var trie = Trie.init(self.allocator);
2666 defer trie.deinit();
2667
2668 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2669 for (self.exports.items()) |entry| {
2670 const name = entry.key;
2671 const symbol = entry.value;
2672 // TODO figure out if we should put all exports into the export trie
2673 assert(symbol.n_value >= text_segment.inner.vmaddr);
2674 try trie.put(.{
2675 .name = name,
2676 .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr,
2677 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2678 });
2679 }
2680
2681 try trie.finalize();
2682 var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size));
2683 defer self.allocator.free(buffer);
2684 var stream = std.io.fixedBufferStream(buffer);
2685 const nwritten = try trie.write(stream.writer());
2686 assert(nwritten == trie.size);
2687
2688 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2689 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2690 dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2691 dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2692 seg.inner.filesize += dyld_info.export_size;
2693
2694 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
2695
2696 try self.file.?.pwriteAll(buffer, dyld_info.export_off);
2697}
2698
2699fn writeDebugInfo(self: *Zld) !void {
2700 var stabs = std.ArrayList(macho.nlist_64).init(self.allocator);
2701 defer stabs.deinit();
2702
2703 for (self.objects.items) |object, object_id| {
2704 var debug_info = blk: {
2705 var di = try DebugInfo.parseFromObject(self.allocator, object);
2706 break :blk di orelse continue;
2707 };
2708 defer debug_info.deinit(self.allocator);
2709
2710 const compile_unit = try debug_info.inner.findCompileUnit(0x0); // We assume there is only one CU.
2711 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
2712 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
2713
2714 {
2715 const tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name });
2716 defer self.allocator.free(tu_path);
2717 const dirname = std.fs.path.dirname(tu_path) orelse "./";
2718 // Current dir
2719 try stabs.append(.{
2720 .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
2721 .n_type = macho.N_SO,
2722 .n_sect = 0,
2723 .n_desc = 0,
2724 .n_value = 0,
2725 });
2726 // Artifact name
2727 try stabs.append(.{
2728 .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
2729 .n_type = macho.N_SO,
2730 .n_sect = 0,
2731 .n_desc = 0,
2732 .n_value = 0,
2733 });
2734 // Path to object file with debug info
2735 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
2736 const full_path = blk: {
2737 if (object.ar_name) |prefix| {
2738 const path = try std.os.realpath(prefix, &buffer);
2739 break :blk try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object.name });
2740 } else {
2741 const path = try std.os.realpath(object.name, &buffer);
2742 break :blk try mem.dupe(self.allocator, u8, path);
2743 }
2744 };
2745 defer self.allocator.free(full_path);
2746 const stat = try object.file.stat();
2747 const mtime = @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
2748 try stabs.append(.{
2749 .n_strx = try self.makeString(full_path),
2750 .n_type = macho.N_OSO,
2751 .n_sect = 0,
2752 .n_desc = 1,
2753 .n_value = mtime,
2754 });
2755 }
2756 log.debug("analyzing debug info in '{s}'", .{object.name});
2757
2758 for (object.symtab.items) |source_sym| {
2759 const symname = object.getString(source_sym.n_strx);
2760 const source_addr = source_sym.n_value;
2761 const target_syms = self.locals.get(symname) orelse continue;
2762 const target_sym: Symbol = blk: {
2763 for (target_syms.items) |ts| {
2764 if (ts.object_id == @intCast(u16, object_id)) break :blk ts;
2765 } else continue;
2766 };
2767
2768 const maybe_size = blk: for (debug_info.inner.func_list.items) |func| {
2769 if (func.pc_range) |range| {
2770 if (source_addr >= range.start and source_addr < range.end) {
2771 break :blk range.end - range.start;
2772 }
2773 }
2774 } else null;
2775
2776 if (maybe_size) |size| {
2777 try stabs.append(.{
2778 .n_strx = 0,
2779 .n_type = macho.N_BNSYM,
2780 .n_sect = target_sym.inner.n_sect,
2781 .n_desc = 0,
2782 .n_value = target_sym.inner.n_value,
2783 });
2784 try stabs.append(.{
2785 .n_strx = target_sym.inner.n_strx,
2786 .n_type = macho.N_FUN,
2787 .n_sect = target_sym.inner.n_sect,
2788 .n_desc = 0,
2789 .n_value = target_sym.inner.n_value,
2790 });
2791 try stabs.append(.{
2792 .n_strx = 0,
2793 .n_type = macho.N_FUN,
2794 .n_sect = 0,
2795 .n_desc = 0,
2796 .n_value = size,
2797 });
2798 try stabs.append(.{
2799 .n_strx = 0,
2800 .n_type = macho.N_ENSYM,
2801 .n_sect = target_sym.inner.n_sect,
2802 .n_desc = 0,
2803 .n_value = size,
2804 });
2805 } else {
2806 // TODO need a way to differentiate symbols: global, static, local, etc.
2807 try stabs.append(.{
2808 .n_strx = target_sym.inner.n_strx,
2809 .n_type = macho.N_STSYM,
2810 .n_sect = target_sym.inner.n_sect,
2811 .n_desc = 0,
2812 .n_value = target_sym.inner.n_value,
2813 });
2814 }
2815 }
2816
2817 // Close the source file!
2818 try stabs.append(.{
2819 .n_strx = 0,
2820 .n_type = macho.N_SO,
2821 .n_sect = 0,
2822 .n_desc = 0,
2823 .n_value = 0,
2824 });
2825 }
2826
2827 if (stabs.items.len == 0) return;
2828
2829 // Write stabs into the symbol table
2830 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2831 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2832
2833 symtab.nsyms = @intCast(u32, stabs.items.len);
2834
2835 const stabs_off = symtab.symoff;
2836 const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64);
2837 log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off });
2838 try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off);
2839
2840 linkedit.inner.filesize += stabs_size;
2841
2842 // Update dynamic symbol table.
2843 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2844 dysymtab.nlocalsym = symtab.nsyms;
2845}
2846
2847fn writeSymbolTable(self: *Zld) !void {
2848 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2849 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2850
2851 var locals = std.ArrayList(macho.nlist_64).init(self.allocator);
2852 defer locals.deinit();
2853
2854 for (self.locals.items()) |entries| {
2855 log.debug("'{s}': {} entries", .{ entries.key, entries.value.items.len });
2856 // var symbol: ?macho.nlist_64 = null;
2857 for (entries.value.items) |entry| {
2858 log.debug(" | {}", .{entry.inner});
2859 log.debug(" | {}", .{entry.tt});
2860 log.debug(" | {s}", .{self.objects.items[entry.object_id].name});
2861 try locals.append(entry.inner);
2862 }
2863 }
2864 const nlocals = locals.items.len;
2865
2866 const nexports = self.exports.items().len;
2867 var exports = std.ArrayList(macho.nlist_64).init(self.allocator);
2868 defer exports.deinit();
2869
2870 try exports.ensureCapacity(nexports);
2871 for (self.exports.items()) |entry| {
2872 exports.appendAssumeCapacity(entry.value);
2873 }
2874
2875 const has_tlv: bool = self.tlv_bootstrap != null;
2876
2877 var nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2878 if (has_tlv) nundefs += 1;
2879
2880 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
2881 defer undefs.deinit();
2882
2883 try undefs.ensureCapacity(nundefs);
2884 for (self.lazy_imports.items()) |entry| {
2885 undefs.appendAssumeCapacity(entry.value.symbol);
2886 }
2887 for (self.nonlazy_imports.items()) |entry| {
2888 undefs.appendAssumeCapacity(entry.value.symbol);
2889 }
2890 if (has_tlv) {
2891 undefs.appendAssumeCapacity(self.tlv_bootstrap.?.symbol);
2892 }
2893
2894 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
2895 const locals_size = nlocals * @sizeOf(macho.nlist_64);
2896 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
2897 try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
2898
2899 const exports_off = locals_off + locals_size;
2900 const exports_size = nexports * @sizeOf(macho.nlist_64);
2901 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
2902 try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
2903
2904 const undefs_off = exports_off + exports_size;
2905 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
2906 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2907 try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
2908
2909 symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
2910 seg.inner.filesize += locals_size + exports_size + undefs_size;
2911
2912 // Update dynamic symbol table.
2913 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2914 dysymtab.nlocalsym += @intCast(u32, nlocals);
2915 dysymtab.iextdefsym = dysymtab.nlocalsym;
2916 dysymtab.nextdefsym = @intCast(u32, nexports);
2917 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
2918 dysymtab.nundefsym = @intCast(u32, nundefs);
2919}
2920
2921fn writeDynamicSymbolTable(self: *Zld) !void {
2922 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2923 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2924 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2925 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2926 const got = &data_const_segment.sections.items[self.got_section_index.?];
2927 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2928 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2929 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2930
2931 const lazy = self.lazy_imports.items();
2932 const nonlazy = self.nonlazy_imports.items();
2933 const got_locals = self.nonlazy_pointers.items();
2934 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2935 dysymtab.nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len + got_locals.len);
2936 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
2937 seg.inner.filesize += needed_size;
2938
2939 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
2940 dysymtab.indirectsymoff,
2941 dysymtab.indirectsymoff + needed_size,
2942 });
2943
2944 var buf = try self.allocator.alloc(u8, needed_size);
2945 defer self.allocator.free(buf);
2946 var stream = std.io.fixedBufferStream(buf);
2947 var writer = stream.writer();
2948
2949 stubs.reserved1 = 0;
2950 for (lazy) |_, i| {
2951 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2952 try writer.writeIntLittle(u32, symtab_idx);
2953 }
2954
2955 const base_id = @intCast(u32, lazy.len);
2956 got.reserved1 = base_id;
2957 for (nonlazy) |_, i| {
2958 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2959 try writer.writeIntLittle(u32, symtab_idx);
2960 }
2961 // TODO there should be one common set of GOT entries.
2962 for (got_locals) |_| {
2963 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2964 }
2965
2966 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len) + @intCast(u32, got_locals.len);
2967 for (lazy) |_, i| {
2968 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2969 try writer.writeIntLittle(u32, symtab_idx);
2970 }
2971
2972 try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
2973}
2974
2975fn writeStringTable(self: *Zld) !void {
2976 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2977 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2978 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2979 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
2980 seg.inner.filesize += symtab.strsize;
2981
2982 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
2983
2984 try self.file.?.pwriteAll(self.strtab.items, symtab.stroff);
2985
2986 if (symtab.strsize > self.strtab.items.len and self.arch.? == .x86_64) {
2987 // This is the last section, so we need to pad it out.
2988 try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
2989 }
2990}
2991
2992fn writeDataInCode(self: *Zld) !void {
2993 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2994 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
2995 const fileoff = seg.inner.fileoff + seg.inner.filesize;
2996
2997 var buf = std.ArrayList(u8).init(self.allocator);
2998 defer buf.deinit();
2999
3000 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3001 const text_sect = text_seg.sections.items[self.text_section_index.?];
3002 for (self.objects.items) |object, object_id| {
3003 const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
3004 const source_sect = source_seg.sections.items[object.text_section_index.?];
3005 const target_mapping = self.mappings.get(.{
3006 .object_id = @intCast(u16, object_id),
3007 .source_sect_id = object.text_section_index.?,
3008 }) orelse continue;
3009
3010 try buf.ensureCapacity(
3011 buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry),
3012 );
3013 for (object.data_in_code_entries.items) |dice| {
3014 const new_dice: macho.data_in_code_entry = .{
3015 .offset = text_sect.offset + target_mapping.offset + dice.offset,
3016 .length = dice.length,
3017 .kind = dice.kind,
3018 };
3019 buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice));
3020 }
3021 }
3022 const datasize = @intCast(u32, buf.items.len);
3023
3024 dice_cmd.dataoff = @intCast(u32, fileoff);
3025 dice_cmd.datasize = datasize;
3026 seg.inner.filesize += datasize;
3027
3028 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
3029
3030 try self.file.?.pwriteAll(buf.items, fileoff);
3031}
3032
3033fn writeCodeSignaturePadding(self: *Zld) !void {
3034 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3035 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3036 const fileoff = seg.inner.fileoff + seg.inner.filesize;
3037 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
3038 self.out_path.?,
3039 fileoff,
3040 self.page_size.?,
3041 );
3042 code_sig_cmd.dataoff = @intCast(u32, fileoff);
3043 code_sig_cmd.datasize = needed_size;
3044
3045 // Advance size of __LINKEDIT segment
3046 seg.inner.filesize += needed_size;
3047 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
3048
3049 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
3050
3051 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
3052 // except for code signature data.
3053 try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
3054}
3055
3056fn writeCodeSignature(self: *Zld) !void {
3057 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3058 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3059
3060 var code_sig = CodeSignature.init(self.allocator, self.page_size.?);
3061 defer code_sig.deinit();
3062 try code_sig.calcAdhocSignature(
3063 self.file.?,
3064 self.out_path.?,
3065 text_seg.inner,
3066 code_sig_cmd,
3067 .Exe,
3068 );
3069
3070 var buffer = try self.allocator.alloc(u8, code_sig.size());
3071 defer self.allocator.free(buffer);
3072 var stream = std.io.fixedBufferStream(buffer);
3073 try code_sig.write(stream.writer());
3074
3075 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
3076
3077 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
3078}
3079
3080fn writeLoadCommands(self: *Zld) !void {
3081 var sizeofcmds: u32 = 0;
3082 for (self.load_commands.items) |lc| {
3083 sizeofcmds += lc.cmdsize();
3084 }
3085
3086 var buffer = try self.allocator.alloc(u8, sizeofcmds);
3087 defer self.allocator.free(buffer);
3088 var writer = std.io.fixedBufferStream(buffer).writer();
3089 for (self.load_commands.items) |lc| {
3090 try lc.write(writer);
3091 }
3092
3093 const off = @sizeOf(macho.mach_header_64);
3094 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
3095 try self.file.?.pwriteAll(buffer, off);
3096}
3097
3098fn writeHeader(self: *Zld) !void {
3099 var header: macho.mach_header_64 = undefined;
3100 header.magic = macho.MH_MAGIC_64;
3101
3102 const CpuInfo = struct {
3103 cpu_type: macho.cpu_type_t,
3104 cpu_subtype: macho.cpu_subtype_t,
3105 };
3106
3107 const cpu_info: CpuInfo = switch (self.arch.?) {
3108 .aarch64 => .{
3109 .cpu_type = macho.CPU_TYPE_ARM64,
3110 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
3111 },
3112 .x86_64 => .{
3113 .cpu_type = macho.CPU_TYPE_X86_64,
3114 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
3115 },
3116 else => return error.UnsupportedCpuArchitecture,
3117 };
3118 header.cputype = cpu_info.cpu_type;
3119 header.cpusubtype = cpu_info.cpu_subtype;
3120 header.filetype = macho.MH_EXECUTE;
3121 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
3122 header.reserved = 0;
3123
3124 if (self.tlv_section_index) |_|
3125 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3126
3127 header.ncmds = @intCast(u32, self.load_commands.items.len);
3128 header.sizeofcmds = 0;
3129 for (self.load_commands.items) |cmd| {
3130 header.sizeofcmds += cmd.cmdsize();
3131 }
3132 log.debug("writing Mach-O header {}", .{header});
3133 try self.file.?.pwriteAll(mem.asBytes(&header), 0);
3134}
3135
3136pub fn makeStaticString(bytes: []const u8) [16]u8 {
3137 var buf = [_]u8{0} ** 16;
3138 assert(bytes.len <= buf.len);
3139 mem.copy(u8, &buf, bytes);
3140 return buf;
3141}
3142
3143fn makeString(self: *Zld, bytes: []const u8) !u32 {
3144 try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1);
3145 const offset = @intCast(u32, self.strtab.items.len);
3146 log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
3147 self.strtab.appendSliceAssumeCapacity(bytes);
3148 self.strtab.appendAssumeCapacity(0);
3149 return offset;
3150}
3151
3152fn getString(self: *const Zld, str_off: u32) []const u8 {
3153 assert(str_off < self.strtab.items.len);
3154 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
3155}
3156
3157pub fn parseName(name: *const [16]u8) []const u8 {
3158 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3159 return name[0..len];
3160}
3161
3162fn isLocal(sym: *const macho.nlist_64) callconv(.Inline) bool {
3163 if (isExtern(sym)) return false;
3164 const tt = macho.N_TYPE & sym.n_type;
3165 return tt == macho.N_SECT;
3166}
3167
3168fn isExport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3169 if (!isExtern(sym)) return false;
3170 const tt = macho.N_TYPE & sym.n_type;
3171 return tt == macho.N_SECT;
3172}
3173
3174fn isImport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3175 if (!isExtern(sym)) return false;
3176 const tt = macho.N_TYPE & sym.n_type;
3177 return tt == macho.N_UNDF;
3178}
3179
3180fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool {
3181 if ((sym.n_type & macho.N_EXT) == 0) return false;
3182 return (sym.n_type & macho.N_PEXT) == 0;
3183}
3184
3185fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool {
3186 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
3187}
3188
3189fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {
3190 const group_decode = @truncate(u5, inst[3]);
3191 return ((group_decode >> 2) == 4);
3192}
src/main.zig+2-1
...@@ -3281,7 +3281,8 @@ pub const ClangArgIterator = struct {...@@ -3281,7 +3281,8 @@ pub const ClangArgIterator = struct {
3281 self.zig_equivalent = clang_arg.zig_equivalent;3281 self.zig_equivalent = clang_arg.zig_equivalent;
3282 break :find_clang_arg;3282 break :find_clang_arg;
3283 },3283 },
3284 } else {3284 }
3285 else {
3285 fatal("Unknown Clang option: '{s}'", .{arg});3286 fatal("Unknown Clang option: '{s}'", .{arg});
3286 }3287 }
3287 }3288 }
test/standalone.zig+4-1
...@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
9 cases.add("test/standalone/main_return_error/error_u8.zig");9 cases.add("test/standalone/main_return_error/error_u8.zig");
10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
12 cases.addBuildFile("test/standalone/shared_library/build.zig");12 if (std.Target.current.os.tag != .macos) {
13 // TODO zld cannot link shared libraries yet.
14 cases.addBuildFile("test/standalone/shared_library/build.zig");
15 }
13 cases.addBuildFile("test/standalone/mix_o_files/build.zig");16 cases.addBuildFile("test/standalone/mix_o_files/build.zig");
14 cases.addBuildFile("test/standalone/global_linkage/build.zig");17 cases.addBuildFile("test/standalone/global_linkage/build.zig");
15 cases.addBuildFile("test/standalone/static_c_lib/build.zig");18 cases.addBuildFile("test/standalone/static_c_lib/build.zig");