authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-04 09:12:59+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-04 09:12:59+01:00
log9bf97b8494524074b1d3cfe71cd08aae335ba576
tree531e0ecdc83b766752696417bf35f8a411cb9f7b
parent5b803aecfb302718b67c465adfdaaef500ab8c68
parentca86dc61ddf1f2dd96f78e217ade71f04604d144
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18793 from ziglang/macho-zig-object

macho: emit relocatable with self-hosted x86_64 backend

11 files changed, 467 insertions(+), 184 deletions(-)

src/link/MachO.zig+224-68
...@@ -285,8 +285,7 @@ pub fn createEmpty(...@@ -285,8 +285,7 @@ pub fn createEmpty(
285 };285 };
286 try self.d_sym.?.initMetadata(self);286 try self.d_sym.?.initMetadata(self);
287 } else {287 } else {
288 try self.reportUnexpectedError("TODO: implement generating and emitting __DWARF in .o file", .{});288 @panic("TODO: implement generating and emitting __DWARF in .o file");
289 return error.Unexpected;
290 },289 },
291 .code_view => unreachable,290 .code_view => unreachable,
292 }291 }
...@@ -597,7 +596,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -597,7 +596,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
597596
598 try self.allocateSections();597 try self.allocateSections();
599 self.allocateSegments();598 self.allocateSegments();
600 self.allocateAtoms();
601 self.allocateSyntheticSymbols();599 self.allocateSyntheticSymbols();
602 try self.allocateLinkeditSegment();600 try self.allocateLinkeditSegment();
603601
...@@ -615,7 +613,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -615,7 +613,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
615 if (!atom.flags.alive) continue;613 if (!atom.flags.alive) continue;
616 const sect = &self.sections.items(.header)[atom.out_n_sect];614 const sect = &self.sections.items(.header)[atom.out_n_sect];
617 if (sect.isZerofill()) continue;615 if (sect.isZerofill()) continue;
618 if (mem.indexOf(u8, sect.segName(), "ZIG") == null) continue; // Non-Zig sections are handled separately616 if (!self.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
617 if (atom.getRelocs(self).len == 0) continue;
619 // TODO: we will resolve and write ZigObject's TLS data twice:618 // TODO: we will resolve and write ZigObject's TLS data twice:
620 // once here, and once in writeAtoms619 // once here, and once in writeAtoms
621 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;620 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
...@@ -636,7 +635,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -636,7 +635,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
636 return error.FlushFailure;635 return error.FlushFailure;
637 },636 },
638 };637 };
639 const file_offset = sect.offset + atom.value - sect.addr;638 const file_offset = sect.offset + atom.value;
640 atom.resolveRelocs(self, code) catch |err| switch (err) {639 atom.resolveRelocs(self, code) catch |err| switch (err) {
641 error.ResolveFailed => has_resolve_error = true,640 error.ResolveFailed => has_resolve_error = true,
642 else => |e| {641 else => |e| {
...@@ -2025,7 +2024,7 @@ pub fn sortSections(self: *MachO) !void {...@@ -2025,7 +2024,7 @@ pub fn sortSections(self: *MachO) !void {
20252024
2026 for (zo.symtab.items(.nlist)) |*sym| {2025 for (zo.symtab.items(.nlist)) |*sym| {
2027 if (sym.sect()) {2026 if (sym.sect()) {
2028 sym.n_sect = backlinks[sym.n_sect];2027 sym.n_sect = backlinks[sym.n_sect - 1] + 1;
2029 }2028 }
2030 }2029 }
20312030
...@@ -2232,11 +2231,11 @@ fn initSegments(self: *MachO) !void {...@@ -2232,11 +2231,11 @@ fn initSegments(self: *MachO) !void {
2232 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_size});2231 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_size});
2233 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_size});2232 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_size});
2234 }2233 }
2235 _ = try self.addSegment("__PAGEZERO", .{ .vmsize = aligned_pagezero_size });2234 self.pagezero_seg_index = try self.addSegment("__PAGEZERO", .{ .vmsize = aligned_pagezero_size });
2236 }2235 }
22372236
2238 // __TEXT segment is non-optional2237 // __TEXT segment is non-optional
2239 _ = try self.addSegment("__TEXT", .{ .prot = getSegmentProt("__TEXT") });2238 self.text_seg_index = try self.addSegment("__TEXT", .{ .prot = getSegmentProt("__TEXT") });
22402239
2241 // Next, create segments required by sections2240 // Next, create segments required by sections
2242 for (slice.items(.header)) |header| {2241 for (slice.items(.header)) |header| {
...@@ -2248,15 +2247,57 @@ fn initSegments(self: *MachO) !void {...@@ -2248,15 +2247,57 @@ fn initSegments(self: *MachO) !void {
2248 }2247 }
22492248
2250 // Add __LINKEDIT2249 // Add __LINKEDIT
2251 _ = try self.addSegment("__LINKEDIT", .{ .prot = getSegmentProt("__LINKEDIT") });2250 self.linkedit_seg_index = try self.addSegment("__LINKEDIT", .{ .prot = getSegmentProt("__LINKEDIT") });
22522251
2253 // Sort segments2252 // Sort segments
2254 const sortFn = struct {2253 const Entry = struct {
2255 fn sortFn(ctx: void, lhs: macho.segment_command_64, rhs: macho.segment_command_64) bool {2254 index: u8,
2256 return segmentLessThan(ctx, lhs.segName(), rhs.segName());2255
2256 pub fn lessThan(macho_file: *MachO, lhs: @This(), rhs: @This()) bool {
2257 return segmentLessThan(
2258 {},
2259 macho_file.segments.items[lhs.index].segName(),
2260 macho_file.segments.items[rhs.index].segName(),
2261 );
2257 }2262 }
2258 }.sortFn;2263 };
2259 mem.sort(macho.segment_command_64, self.segments.items, {}, sortFn);2264
2265 var entries = try std.ArrayList(Entry).initCapacity(gpa, self.segments.items.len);
2266 defer entries.deinit();
2267 for (0..self.segments.items.len) |index| {
2268 entries.appendAssumeCapacity(.{ .index = @intCast(index) });
2269 }
2270
2271 mem.sort(Entry, entries.items, self, Entry.lessThan);
2272
2273 const backlinks = try gpa.alloc(u8, entries.items.len);
2274 defer gpa.free(backlinks);
2275 for (entries.items, 0..) |entry, i| {
2276 backlinks[entry.index] = @intCast(i);
2277 }
2278
2279 const segments = try self.segments.toOwnedSlice(gpa);
2280 defer gpa.free(segments);
2281
2282 try self.segments.ensureTotalCapacityPrecise(gpa, segments.len);
2283 for (entries.items) |sorted| {
2284 self.segments.appendAssumeCapacity(segments[sorted.index]);
2285 }
2286
2287 for (&[_]*?u8{
2288 &self.pagezero_seg_index,
2289 &self.text_seg_index,
2290 &self.linkedit_seg_index,
2291 &self.zig_text_seg_index,
2292 &self.zig_got_seg_index,
2293 &self.zig_const_seg_index,
2294 &self.zig_data_seg_index,
2295 &self.zig_bss_seg_index,
2296 }) |maybe_index| {
2297 if (maybe_index.*) |*index| {
2298 index.* = backlinks[index.*];
2299 }
2300 }
22602301
2261 // Attach sections to segments2302 // Attach sections to segments
2262 for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| {2303 for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| {
...@@ -2277,15 +2318,6 @@ fn initSegments(self: *MachO) !void {...@@ -2277,15 +2318,6 @@ fn initSegments(self: *MachO) !void {
2277 segment.nsects += 1;2318 segment.nsects += 1;
2278 seg_id.* = segment_id;2319 seg_id.* = segment_id;
2279 }2320 }
2280
2281 self.pagezero_seg_index = self.getSegmentByName("__PAGEZERO");
2282 self.text_seg_index = self.getSegmentByName("__TEXT").?;
2283 self.linkedit_seg_index = self.getSegmentByName("__LINKEDIT").?;
2284 self.zig_text_seg_index = self.getSegmentByName("__TEXT_ZIG");
2285 self.zig_got_seg_index = self.getSegmentByName("__GOT_ZIG");
2286 self.zig_const_seg_index = self.getSegmentByName("__CONST_ZIG");
2287 self.zig_data_seg_index = self.getSegmentByName("__DATA_ZIG");
2288 self.zig_bss_seg_index = self.getSegmentByName("__BSS_ZIG");
2289}2321}
22902322
2291fn allocateSections(self: *MachO) !void {2323fn allocateSections(self: *MachO) !void {
...@@ -2300,8 +2332,8 @@ fn allocateSections(self: *MachO) !void {...@@ -2300,8 +2332,8 @@ fn allocateSections(self: *MachO) !void {
23002332
2301 const page_size = self.getPageSize();2333 const page_size = self.getPageSize();
2302 const slice = self.sections.slice();2334 const slice = self.sections.slice();
2303 const last_index = for (slice.items(.header), 0..) |header, i| {2335 const last_index = for (0..slice.items(.header).len) |i| {
2304 if (mem.indexOf(u8, header.segName(), "ZIG")) |_| break i;2336 if (self.isZigSection(@intCast(i))) break i;
2305 } else slice.items(.header).len;2337 } else slice.items(.header).len;
23062338
2307 for (slice.items(.header)[0..last_index], slice.items(.segment_id)[0..last_index]) |*header, curr_seg_id| {2339 for (slice.items(.header)[0..last_index], slice.items(.segment_id)[0..last_index]) |*header, curr_seg_id| {
...@@ -2354,8 +2386,8 @@ fn allocateSections(self: *MachO) !void {...@@ -2354,8 +2386,8 @@ fn allocateSections(self: *MachO) !void {
2354/// We allocate segments in a separate step to also consider segments that have no sections.2386/// We allocate segments in a separate step to also consider segments that have no sections.
2355fn allocateSegments(self: *MachO) void {2387fn allocateSegments(self: *MachO) void {
2356 const first_index = if (self.pagezero_seg_index) |index| index + 1 else 0;2388 const first_index = if (self.pagezero_seg_index) |index| index + 1 else 0;
2357 const last_index = for (self.segments.items, 0..) |seg, i| {2389 const last_index = for (0..self.segments.items.len) |i| {
2358 if (mem.indexOf(u8, seg.segName(), "ZIG")) |_| break i;2390 if (self.isZigSegment(@intCast(i))) break i;
2359 } else self.segments.items.len;2391 } else self.segments.items.len;
23602392
2361 var vmaddr: u64 = if (self.pagezero_seg_index) |index|2393 var vmaddr: u64 = if (self.pagezero_seg_index) |index|
...@@ -2392,23 +2424,6 @@ fn allocateSegments(self: *MachO) void {...@@ -2392,23 +2424,6 @@ fn allocateSegments(self: *MachO) void {
2392 }2424 }
2393}2425}
23942426
2395pub fn allocateAtoms(self: *MachO) void {
2396 const slice = self.sections.slice();
2397 for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
2398 if (atoms.items.len == 0) continue;
2399 for (atoms.items) |atom_index| {
2400 const atom = self.getAtom(atom_index).?;
2401 assert(atom.flags.alive);
2402 atom.value += header.addr;
2403 }
2404 }
2405
2406 for (self.thunks.items) |*thunk| {
2407 const header = self.sections.items(.header)[thunk.out_n_sect];
2408 thunk.value += header.addr;
2409 }
2410}
2411
2412fn allocateSyntheticSymbols(self: *MachO) void {2427fn allocateSyntheticSymbols(self: *MachO) void {
2413 const text_seg = self.getTextSegment();2428 const text_seg = self.getTextSegment();
24142429
...@@ -2603,7 +2618,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -2603,7 +2618,7 @@ fn writeAtoms(self: *MachO) !void {
2603 for (atoms.items) |atom_index| {2618 for (atoms.items) |atom_index| {
2604 const atom = self.getAtom(atom_index).?;2619 const atom = self.getAtom(atom_index).?;
2605 assert(atom.flags.alive);2620 assert(atom.flags.alive);
2606 const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow;2621 const off = math.cast(usize, atom.value) orelse return error.Overflow;
2607 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;2622 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
2608 try atom.getData(self, buffer[off..][0..atom_size]);2623 try atom.getData(self, buffer[off..][0..atom_size]);
2609 atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {2624 atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
...@@ -2617,7 +2632,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -2617,7 +2632,7 @@ fn writeAtoms(self: *MachO) !void {
26172632
2618 for (self.thunks.items) |thunk| {2633 for (self.thunks.items) |thunk| {
2619 const header = slice.items(.header)[thunk.out_n_sect];2634 const header = slice.items(.header)[thunk.out_n_sect];
2620 const offset = thunk.value - header.addr + header.offset;2635 const offset = thunk.value + header.offset;
2621 const buffer = try gpa.alloc(u8, thunk.size());2636 const buffer = try gpa.alloc(u8, thunk.size());
2622 defer gpa.free(buffer);2637 defer gpa.free(buffer);
2623 var stream = std.io.fixedBufferStream(buffer);2638 var stream = std.io.fixedBufferStream(buffer);
...@@ -2825,7 +2840,7 @@ pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {...@@ -2825,7 +2840,7 @@ pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {
28252840
2826 if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {2841 if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
2827 dices.appendAssumeCapacity(.{2842 dices.appendAssumeCapacity(.{
2828 .offset = @intCast(atom.value + dice.offset - start_off - base_address),2843 .offset = @intCast(atom.getAddress(self) + dice.offset - start_off - base_address),
2829 .length = dice.length,2844 .length = dice.length,
2830 .kind = dice.kind,2845 .kind = dice.kind,
2831 });2846 });
...@@ -3276,6 +3291,34 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -3276,6 +3291,34 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
3276 return null;3291 return null;
3277}3292}
32783293
3294fn detectAllocCollisionVirtual(self: *MachO, start: u64, size: u64) ?u64 {
3295 // Conservatively commit one page size as reserved space for the headers as we
3296 // expect it to grow and everything else be moved in flush anyhow.
3297 const header_size = self.getPageSize();
3298 if (start < header_size)
3299 return header_size;
3300
3301 const end = start + padToIdeal(size);
3302
3303 for (self.sections.items(.header)) |header| {
3304 const increased_size = padToIdeal(header.size);
3305 const test_end = header.addr + increased_size;
3306 if (end > header.addr and start < test_end) {
3307 return test_end;
3308 }
3309 }
3310
3311 for (self.segments.items) |seg| {
3312 const increased_size = padToIdeal(seg.vmsize);
3313 const test_end = seg.vmaddr +| increased_size;
3314 if (end > seg.vmaddr and start < test_end) {
3315 return test_end;
3316 }
3317 }
3318
3319 return null;
3320}
3321
3279fn allocatedSize(self: *MachO, start: u64) u64 {3322fn allocatedSize(self: *MachO, start: u64) u64 {
3280 if (start == 0) return 0;3323 if (start == 0) return 0;
3281 var min_pos: u64 = std.math.maxInt(u64);3324 var min_pos: u64 = std.math.maxInt(u64);
...@@ -3290,7 +3333,7 @@ fn allocatedSize(self: *MachO, start: u64) u64 {...@@ -3290,7 +3333,7 @@ fn allocatedSize(self: *MachO, start: u64) u64 {
3290 return min_pos - start;3333 return min_pos - start;
3291}3334}
32923335
3293fn allocatedVirtualSize(self: *MachO, start: u64) u64 {3336fn allocatedSizeVirtual(self: *MachO, start: u64) u64 {
3294 if (start == 0) return 0;3337 if (start == 0) return 0;
3295 var min_pos: u64 = std.math.maxInt(u64);3338 var min_pos: u64 = std.math.maxInt(u64);
3296 for (self.segments.items) |seg| {3339 for (self.segments.items) |seg| {
...@@ -3300,7 +3343,7 @@ fn allocatedVirtualSize(self: *MachO, start: u64) u64 {...@@ -3300,7 +3343,7 @@ fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
3300 return min_pos - start;3343 return min_pos - start;
3301}3344}
33023345
3303fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {3346pub fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3304 var start: u64 = 0;3347 var start: u64 = 0;
3305 while (self.detectAllocCollision(start, object_size)) |item_end| {3348 while (self.detectAllocCollision(start, object_size)) |item_end| {
3306 start = mem.alignForward(u64, item_end, min_alignment);3349 start = mem.alignForward(u64, item_end, min_alignment);
...@@ -3308,18 +3351,30 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {...@@ -3308,18 +3351,30 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3308 return start;3351 return start;
3309}3352}
33103353
3354pub fn findFreeSpaceVirtual(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3355 var start: u64 = 0;
3356 while (self.detectAllocCollisionVirtual(start, object_size)) |item_end| {
3357 start = mem.alignForward(u64, item_end, min_alignment);
3358 }
3359 return start;
3360}
3361
3362pub fn copyRangeAll(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {
3363 const file = self.base.file.?;
3364 const amt = try file.copyRangeAll(old_offset, file, new_offset, size);
3365 if (amt != size) return error.InputOutput;
3366}
3367
3311/// Like File.copyRangeAll but also ensures the source region is zeroed out after copy.3368/// Like File.copyRangeAll but also ensures the source region is zeroed out after copy.
3312/// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader.3369/// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader.
3313fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {3370fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {
3314 const gpa = self.base.comp.gpa;3371 const gpa = self.base.comp.gpa;
3315 const file = self.base.file.?;3372 try self.copyRangeAll(old_offset, new_offset, size);
3316 const amt = try file.copyRangeAll(old_offset, file, new_offset, size);
3317 if (amt != size) return error.InputOutput;
3318 const size_u = math.cast(usize, size) orelse return error.Overflow;3373 const size_u = math.cast(usize, size) orelse return error.Overflow;
3319 const zeroes = try gpa.alloc(u8, size_u);3374 const zeroes = try gpa.alloc(u8, size_u);
3320 defer gpa.free(zeroes);3375 defer gpa.free(zeroes);
3321 @memset(zeroes, 0);3376 @memset(zeroes, 0);
3322 try file.pwriteAll(zeroes, old_offset);3377 try self.base.file.?.pwriteAll(zeroes, old_offset);
3323}3378}
33243379
3325const InitMetadataOptions = struct {3380const InitMetadataOptions = struct {
...@@ -3391,8 +3446,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3391,8 +3446,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3391 .prot = macho.PROT.READ | macho.PROT.WRITE,3446 .prot = macho.PROT.READ | macho.PROT.WRITE,
3392 });3447 });
3393 }3448 }
3394 } else {
3395 @panic("TODO initMetadata when relocatable");
3396 }3449 }
33973450
3398 const appendSect = struct {3451 const appendSect = struct {
...@@ -3406,6 +3459,19 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3406,6 +3459,19 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3406 }3459 }
3407 }.appendSect;3460 }.appendSect;
34083461
3462 const allocSect = struct {
3463 fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void {
3464 const sect = &macho_file.sections.items(.header)[sect_id];
3465 const alignment = try math.powi(u32, 2, sect.@"align");
3466 if (!sect.isZerofill()) {
3467 sect.offset = math.cast(u32, macho_file.findFreeSpace(size, alignment)) orelse
3468 return error.Overflow;
3469 }
3470 sect.addr = macho_file.findFreeSpaceVirtual(size, alignment);
3471 sect.size = size;
3472 }
3473 }.allocSect;
3474
3409 {3475 {
3410 self.zig_text_sect_index = try self.addSection("__TEXT_ZIG", "__text_zig", .{3476 self.zig_text_sect_index = try self.addSection("__TEXT_ZIG", "__text_zig", .{
3411 .alignment = switch (self.getTarget().cpu.arch) {3477 .alignment = switch (self.getTarget().cpu.arch) {
...@@ -3415,7 +3481,11 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3415,7 +3481,11 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3415 },3481 },
3416 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,3482 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3417 });3483 });
3418 appendSect(self, self.zig_text_sect_index.?, self.zig_text_seg_index.?);3484 if (self.base.isRelocatable()) {
3485 try allocSect(self, self.zig_text_sect_index.?, options.program_code_size_hint);
3486 } else {
3487 appendSect(self, self.zig_text_sect_index.?, self.zig_text_seg_index.?);
3488 }
3419 }3489 }
34203490
3421 if (!self.base.isRelocatable()) {3491 if (!self.base.isRelocatable()) {
...@@ -3427,33 +3497,52 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3427,33 +3497,52 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
34273497
3428 {3498 {
3429 self.zig_const_sect_index = try self.addSection("__CONST_ZIG", "__const_zig", .{});3499 self.zig_const_sect_index = try self.addSection("__CONST_ZIG", "__const_zig", .{});
3430 appendSect(self, self.zig_const_sect_index.?, self.zig_const_seg_index.?);3500 if (self.base.isRelocatable()) {
3501 try allocSect(self, self.zig_const_sect_index.?, 1024);
3502 } else {
3503 appendSect(self, self.zig_const_sect_index.?, self.zig_const_seg_index.?);
3504 }
3431 }3505 }
34323506
3433 {3507 {
3434 self.zig_data_sect_index = try self.addSection("__DATA_ZIG", "__data_zig", .{});3508 self.zig_data_sect_index = try self.addSection("__DATA_ZIG", "__data_zig", .{});
3435 appendSect(self, self.zig_data_sect_index.?, self.zig_data_seg_index.?);3509 if (self.base.isRelocatable()) {
3510 try allocSect(self, self.zig_data_sect_index.?, 1024);
3511 } else {
3512 appendSect(self, self.zig_data_sect_index.?, self.zig_data_seg_index.?);
3513 }
3436 }3514 }
34373515
3438 {3516 {
3439 self.zig_bss_sect_index = try self.addSection("__BSS_ZIG", "__bss_zig", .{3517 self.zig_bss_sect_index = try self.addSection("__BSS_ZIG", "__bss_zig", .{
3440 .flags = macho.S_ZEROFILL,3518 .flags = macho.S_ZEROFILL,
3441 });3519 });
3442 appendSect(self, self.zig_bss_sect_index.?, self.zig_bss_seg_index.?);3520 if (self.base.isRelocatable()) {
3521 try allocSect(self, self.zig_bss_sect_index.?, 1024);
3522 } else {
3523 appendSect(self, self.zig_bss_sect_index.?, self.zig_bss_seg_index.?);
3524 }
3443 }3525 }
3444}3526}
34453527
3446pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {3528pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
3529 if (self.base.isRelocatable()) {
3530 try self.growSectionRelocatable(sect_index, needed_size);
3531 } else {
3532 try self.growSectionNonRelocatable(sect_index, needed_size);
3533 }
3534}
3535
3536fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void {
3447 const sect = &self.sections.items(.header)[sect_index];3537 const sect = &self.sections.items(.header)[sect_index];
3448 const seg_id = self.sections.items(.segment_id)[sect_index];
3449 const seg = &self.segments.items[seg_id];
34503538
3451 if (needed_size > self.allocatedSize(sect.offset) and !sect.isZerofill()) {3539 if (needed_size > self.allocatedSize(sect.offset) and !sect.isZerofill()) {
3452 const existing_size = sect.size;3540 const existing_size = sect.size;
3453 sect.size = 0;3541 sect.size = 0;
34543542
3455 // Must move the entire section.3543 // Must move the entire section.
3456 const new_offset = self.findFreeSpace(needed_size, self.getPageSize());3544 const alignment = self.getPageSize();
3545 const new_offset = self.findFreeSpace(needed_size, alignment);
34573546
3458 log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{3547 log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{
3459 sect.segName(),3548 sect.segName(),
...@@ -3465,15 +3554,19 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {...@@ -3465,15 +3554,19 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
3465 try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size);3554 try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size);
34663555
3467 sect.offset = @intCast(new_offset);3556 sect.offset = @intCast(new_offset);
3468 seg.fileoff = new_offset;
3469 }3557 }
34703558
3471 sect.size = needed_size;3559 sect.size = needed_size;
3560
3561 const seg_id = self.sections.items(.segment_id)[sect_index];
3562 const seg = &self.segments.items[seg_id];
3563 seg.fileoff = sect.offset;
3564
3472 if (!sect.isZerofill()) {3565 if (!sect.isZerofill()) {
3473 seg.filesize = needed_size;3566 seg.filesize = needed_size;
3474 }3567 }
34753568
3476 const mem_capacity = self.allocatedVirtualSize(seg.vmaddr);3569 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
3477 if (needed_size > mem_capacity) {3570 if (needed_size > mem_capacity) {
3478 var err = try self.addErrorWithNotes(2);3571 var err = try self.addErrorWithNotes(2);
3479 try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{3572 try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
...@@ -3487,6 +3580,36 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {...@@ -3487,6 +3580,36 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
3487 seg.vmsize = needed_size;3580 seg.vmsize = needed_size;
3488}3581}
34893582
3583fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void {
3584 const sect = &self.sections.items(.header)[sect_index];
3585
3586 if (needed_size > self.allocatedSize(sect.offset) and !sect.isZerofill()) {
3587 const existing_size = sect.size;
3588 sect.size = 0;
3589
3590 // Must move the entire section.
3591 const alignment = try math.powi(u32, 2, sect.@"align");
3592 const new_offset = self.findFreeSpace(needed_size, alignment);
3593 const new_addr = self.findFreeSpaceVirtual(needed_size, alignment);
3594
3595 log.debug("new '{s},{s}' file offset 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
3596 sect.segName(),
3597 sect.sectName(),
3598 new_offset,
3599 new_offset + existing_size,
3600 new_addr,
3601 new_addr + existing_size,
3602 });
3603
3604 try self.copyRangeAll(sect.offset, new_offset, existing_size);
3605
3606 sect.offset = @intCast(new_offset);
3607 sect.addr = new_addr;
3608 }
3609
3610 sect.size = needed_size;
3611}
3612
3490pub fn getTarget(self: MachO) std.Target {3613pub fn getTarget(self: MachO) std.Target {
3491 return self.base.comp.root_mod.resolved_target.result;3614 return self.base.comp.root_mod.resolved_target.result;
3492}3615}
...@@ -3532,6 +3655,36 @@ inline fn requiresThunks(self: MachO) bool {...@@ -3532,6 +3655,36 @@ inline fn requiresThunks(self: MachO) bool {
3532 return self.getTarget().cpu.arch == .aarch64;3655 return self.getTarget().cpu.arch == .aarch64;
3533}3656}
35343657
3658pub fn isZigSegment(self: MachO, seg_id: u8) bool {
3659 inline for (&[_]?u8{
3660 self.zig_text_seg_index,
3661 self.zig_got_seg_index,
3662 self.zig_const_seg_index,
3663 self.zig_data_seg_index,
3664 self.zig_bss_seg_index,
3665 }) |maybe_index| {
3666 if (maybe_index) |index| {
3667 if (index == seg_id) return true;
3668 }
3669 }
3670 return false;
3671}
3672
3673pub fn isZigSection(self: MachO, sect_id: u8) bool {
3674 inline for (&[_]?u8{
3675 self.zig_text_sect_index,
3676 self.zig_got_sect_index,
3677 self.zig_const_sect_index,
3678 self.zig_data_sect_index,
3679 self.zig_bss_sect_index,
3680 }) |maybe_index| {
3681 if (maybe_index) |index| {
3682 if (index == sect_id) return true;
3683 }
3684 }
3685 return false;
3686}
3687
3535pub fn addSegment(self: *MachO, name: []const u8, opts: struct {3688pub fn addSegment(self: *MachO, name: []const u8, opts: struct {
3536 vmaddr: u64 = 0,3689 vmaddr: u64 = 0,
3537 vmsize: u64 = 0,3690 vmsize: u64 = 0,
...@@ -4033,10 +4186,13 @@ fn formatSections(...@@ -4033,10 +4186,13 @@ fn formatSections(
4033 _ = unused_fmt_string;4186 _ = unused_fmt_string;
4034 const slice = self.sections.slice();4187 const slice = self.sections.slice();
4035 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {4188 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
4036 try writer.print("sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x})\n", .{4189 try writer.print(
4037 i, seg_id, header.segName(), header.sectName(), header.offset, header.addr,4190 "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n",
4038 header.@"align", header.size,4191 .{
4039 });4192 i, seg_id, header.segName(), header.sectName(), header.addr, header.offset,
4193 header.@"align", header.size, header.reloff, header.nreloc,
4194 },
4195 );
4040 }4196 }
4041}4197}
40424198
src/link/MachO/Atom.zig+39-34
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1/// Address allocated for this Atom.1/// Address offset allocated for this Atom wrt to its section start address.
2value: u64 = 0,2value: u64 = 0,
33
4/// Name of this Atom.4/// Name of this Atom.
...@@ -84,6 +84,11 @@ pub fn getInputAddress(self: Atom, macho_file: *MachO) u64 {...@@ -84,6 +84,11 @@ pub fn getInputAddress(self: Atom, macho_file: *MachO) u64 {
84 return self.getInputSection(macho_file).addr + self.off;84 return self.getInputSection(macho_file).addr + self.off;
85}85}
8686
87pub fn getAddress(self: Atom, macho_file: *MachO) u64 {
88 const header = macho_file.sections.items(.header)[self.out_n_sect];
89 return header.addr + self.value;
90}
91
87pub fn getPriority(self: Atom, macho_file: *MachO) u64 {92pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
88 const file = self.getFile(macho_file);93 const file = self.getFile(macho_file);
89 return (@as(u64, @intCast(file.getIndex())) << 32) | @as(u64, @intCast(self.n_sect));94 return (@as(u64, @intCast(file.getIndex())) << 32) | @as(u64, @intCast(self.n_sect));
...@@ -114,9 +119,12 @@ pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {...@@ -114,9 +119,12 @@ pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
114119
115pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {120pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
116 const segname, const sectname, const flags = blk: {121 const segname, const sectname, const flags = blk: {
122 const segname = sect.segName();
123 const sectname = sect.sectName();
124
117 if (sect.isCode()) break :blk .{125 if (sect.isCode()) break :blk .{
118 "__TEXT",126 "__TEXT",
119 sect.sectName(),127 sectname,
120 macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,128 macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
121 };129 };
122130
...@@ -127,34 +135,29 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {...@@ -127,34 +135,29 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
127 => break :blk .{ "__TEXT", "__const", macho.S_REGULAR },135 => break :blk .{ "__TEXT", "__const", macho.S_REGULAR },
128136
129 macho.S_CSTRING_LITERALS => {137 macho.S_CSTRING_LITERALS => {
130 if (mem.startsWith(u8, sect.sectName(), "__objc")) break :blk .{138 if (mem.startsWith(u8, sectname, "__objc")) break :blk .{
131 sect.segName(), sect.sectName(), macho.S_REGULAR,139 segname, sectname, macho.S_REGULAR,
132 };140 };
133 break :blk .{ "__TEXT", "__cstring", macho.S_CSTRING_LITERALS };141 break :blk .{ "__TEXT", "__cstring", macho.S_CSTRING_LITERALS };
134 },142 },
135143
136 macho.S_MOD_INIT_FUNC_POINTERS,144 macho.S_MOD_INIT_FUNC_POINTERS,
137 macho.S_MOD_TERM_FUNC_POINTERS,145 macho.S_MOD_TERM_FUNC_POINTERS,
138 => break :blk .{ "__DATA_CONST", sect.sectName(), sect.flags },
139
140 macho.S_LITERAL_POINTERS,146 macho.S_LITERAL_POINTERS,
147 => break :blk .{ "__DATA_CONST", sectname, sect.flags },
148
141 macho.S_ZEROFILL,149 macho.S_ZEROFILL,
142 macho.S_GB_ZEROFILL,150 macho.S_GB_ZEROFILL,
143 macho.S_THREAD_LOCAL_VARIABLES,151 macho.S_THREAD_LOCAL_VARIABLES,
144 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,152 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
145 macho.S_THREAD_LOCAL_REGULAR,153 macho.S_THREAD_LOCAL_REGULAR,
146 macho.S_THREAD_LOCAL_ZEROFILL,154 macho.S_THREAD_LOCAL_ZEROFILL,
147 => break :blk .{ sect.segName(), sect.sectName(), sect.flags },155 => break :blk .{ "__DATA", sectname, sect.flags },
148156
149 macho.S_COALESCED => break :blk .{157 // TODO: do we need this check here?
150 sect.segName(),158 macho.S_COALESCED => break :blk .{ segname, sectname, macho.S_REGULAR },
151 sect.sectName(),
152 macho.S_REGULAR,
153 },
154159
155 macho.S_REGULAR => {160 macho.S_REGULAR => {
156 const segname = sect.segName();
157 const sectname = sect.sectName();
158 if (mem.eql(u8, segname, "__DATA")) {161 if (mem.eql(u8, segname, "__DATA")) {
159 if (mem.eql(u8, sectname, "__const") or162 if (mem.eql(u8, sectname, "__const") or
160 mem.eql(u8, sectname, "__cfstring") or163 mem.eql(u8, sectname, "__cfstring") or
...@@ -168,7 +171,7 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {...@@ -168,7 +171,7 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
168 break :blk .{ segname, sectname, sect.flags };171 break :blk .{ segname, sectname, sect.flags };
169 },172 },
170173
171 else => break :blk .{ sect.segName(), sect.sectName(), sect.flags },174 else => break :blk .{ segname, sectname, sect.flags },
172 }175 }
173 };176 };
174 const osec = macho_file.getSectionByName(segname, sectname) orelse try macho_file.addSection(177 const osec = macho_file.getSectionByName(segname, sectname) orelse try macho_file.addSection(
...@@ -189,14 +192,17 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {...@@ -189,14 +192,17 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
189/// File offset relocation happens transparently, so it is not included in192/// File offset relocation happens transparently, so it is not included in
190/// this calculation.193/// this calculation.
191pub fn capacity(self: Atom, macho_file: *MachO) u64 {194pub fn capacity(self: Atom, macho_file: *MachO) u64 {
192 const next_value = if (macho_file.getAtom(self.next_index)) |next| next.value else std.math.maxInt(u32);195 const next_addr = if (macho_file.getAtom(self.next_index)) |next|
193 return next_value - self.value;196 next.getAddress(macho_file)
197 else
198 std.math.maxInt(u32);
199 return next_addr - self.getAddress(macho_file);
194}200}
195201
196pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {202pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
197 // No need to keep a free list node for the last block.203 // No need to keep a free list node for the last block.
198 const next = macho_file.getAtom(self.next_index) orelse return false;204 const next = macho_file.getAtom(self.next_index) orelse return false;
199 const cap = next.value - self.value;205 const cap = next.getAddress(macho_file) - self.getAddress(macho_file);
200 const ideal_cap = MachO.padToIdeal(self.size);206 const ideal_cap = MachO.padToIdeal(self.size);
201 if (cap <= ideal_cap) return false;207 if (cap <= ideal_cap) return false;
202 const surplus = cap - ideal_cap;208 const surplus = cap - ideal_cap;
...@@ -263,15 +269,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -263,15 +269,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
263 atom_placement = last.atom_index;269 atom_placement = last.atom_index;
264 break :blk new_start_vaddr;270 break :blk new_start_vaddr;
265 } else {271 } else {
266 break :blk sect.addr;272 break :blk 0;
267 }273 }
268 };274 };
269275
270 log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{276 log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{
271 self.atom_index,277 self.atom_index,
272 self.getName(macho_file),278 self.getName(macho_file),
273 self.value,279 self.getAddress(macho_file),
274 self.value + self.size,280 self.getAddress(macho_file) + self.size,
275 });281 });
276282
277 const expand_section = if (atom_placement) |placement_index|283 const expand_section = if (atom_placement) |placement_index|
...@@ -279,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -279,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
279 else285 else
280 true;286 true;
281 if (expand_section) {287 if (expand_section) {
282 const needed_size = (self.value + self.size) - sect.addr;288 const needed_size = self.value + self.size;
283 try macho_file.growSection(self.out_n_sect, needed_size);289 try macho_file.growSection(self.out_n_sect, needed_size);
284 last_atom_index.* = self.atom_index;290 last_atom_index.* = self.atom_index;
285291
...@@ -544,7 +550,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {...@@ -544,7 +550,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
544 const name = self.getName(macho_file);550 const name = self.getName(macho_file);
545 const relocs = self.getRelocs(macho_file);551 const relocs = self.getRelocs(macho_file);
546552
547 relocs_log.debug("{x}: {s}", .{ self.value, name });553 relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name });
548554
549 var has_error = false;555 var has_error = false;
550 var stream = std.io.fixedBufferStream(buffer);556 var stream = std.io.fixedBufferStream(buffer);
...@@ -569,7 +575,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {...@@ -569,7 +575,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
569 try macho_file.reportParseError2(575 try macho_file.reportParseError2(
570 file.getIndex(),576 file.getIndex(),
571 "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {s}, target {s}",577 "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {s}, target {s}",
572 .{ name, self.value, rel.offset, @tagName(rel.type), target },578 .{ name, self.getAddress(macho_file), rel.offset, @tagName(rel.type), target },
573 );579 );
574 has_error = true;580 has_error = true;
575 },581 },
...@@ -604,7 +610,7 @@ fn resolveRelocInner(...@@ -604,7 +610,7 @@ fn resolveRelocInner(
604 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;610 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
605 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];611 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
606 const seg = macho_file.segments.items[seg_id];612 const seg = macho_file.segments.items[seg_id];
607 const P = @as(i64, @intCast(self.value)) + @as(i64, @intCast(rel_offset));613 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
608 const A = rel.addend + rel.getRelocAddend(cpu_arch);614 const A = rel.addend + rel.getRelocAddend(cpu_arch);
609 const S: i64 = @intCast(rel.getTargetAddress(macho_file));615 const S: i64 = @intCast(rel.getTargetAddress(macho_file));
610 const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));616 const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));
...@@ -690,7 +696,7 @@ fn resolveRelocInner(...@@ -690,7 +696,7 @@ fn resolveRelocInner(
690 .aarch64 => {696 .aarch64 => {
691 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {697 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
692 const thunk = self.getThunk(macho_file);698 const thunk = self.getThunk(macho_file);
693 const S_: i64 = @intCast(thunk.getAddress(rel.target));699 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
694 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;700 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
695 };701 };
696 var inst = aarch64.Instruction{702 var inst = aarch64.Instruction{
...@@ -919,7 +925,7 @@ const x86_64 = struct {...@@ -919,7 +925,7 @@ const x86_64 = struct {
919 var err = try macho_file.addErrorWithNotes(2);925 var err = try macho_file.addErrorWithNotes(2);
920 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{926 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{
921 self.getName(macho_file),927 self.getName(macho_file),
922 self.value,928 self.getAddress(macho_file),
923 rel.offset,929 rel.offset,
924 @tagName(rel.type),930 @tagName(rel.type),
925 });931 });
...@@ -990,12 +996,11 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra...@@ -990,12 +996,11 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
990996
991 const cpu_arch = macho_file.getTarget().cpu.arch;997 const cpu_arch = macho_file.getTarget().cpu.arch;
992 const relocs = self.getRelocs(macho_file);998 const relocs = self.getRelocs(macho_file);
993 const sect = macho_file.sections.items(.header)[self.out_n_sect];
994 var stream = std.io.fixedBufferStream(code);999 var stream = std.io.fixedBufferStream(code);
9951000
996 for (relocs) |rel| {1001 for (relocs) |rel| {
997 const rel_offset = rel.offset - self.off;1002 const rel_offset = rel.offset - self.off;
998 const r_address: i32 = math.cast(i32, self.value + rel_offset - sect.addr) orelse return error.Overflow;1003 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
999 const r_symbolnum = r_symbolnum: {1004 const r_symbolnum = r_symbolnum: {
1000 const r_symbolnum: u32 = switch (rel.tag) {1005 const r_symbolnum: u32 = switch (rel.tag) {
1001 .local => rel.getTargetAtom(macho_file).out_n_sect + 1,1006 .local => rel.getTargetAtom(macho_file).out_n_sect + 1,
...@@ -1062,7 +1067,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra...@@ -1062,7 +1067,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
1062 .x86_64 => {1067 .x86_64 => {
1063 if (rel.meta.pcrel) {1068 if (rel.meta.pcrel) {
1064 if (rel.tag == .local) {1069 if (rel.tag == .local) {
1065 addend -= @as(i64, @intCast(self.value + rel_offset));1070 addend -= @as(i64, @intCast(self.getAddress(macho_file) + rel_offset));
1066 } else {1071 } else {
1067 addend += 4;1072 addend += 4;
1068 }1073 }
...@@ -1143,10 +1148,10 @@ fn format2(...@@ -1143,10 +1148,10 @@ fn format2(
1143 _ = unused_fmt_string;1148 _ = unused_fmt_string;
1144 const atom = ctx.atom;1149 const atom = ctx.atom;
1145 const macho_file = ctx.macho_file;1150 const macho_file = ctx.macho_file;
1146 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : thunk({d})", .{1151 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1147 atom.atom_index, atom.getName(macho_file), atom.value,1152 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1148 atom.out_n_sect, atom.alignment, atom.size,1153 atom.out_n_sect, atom.alignment, atom.size,
1149 atom.thunk_index,1154 atom.getRelocs(macho_file).len, atom.thunk_index,
1150 });1155 });
1151 if (!atom.flags.alive) try writer.writeAll(" : [*]");1156 if (!atom.flags.alive) try writer.writeAll(" : [*]");
1152 if (atom.unwind_records.len > 0) {1157 if (atom.unwind_records.len > 0) {
src/link/MachO/Relocation.zig+1-1
...@@ -22,7 +22,7 @@ pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) *Atom {...@@ -22,7 +22,7 @@ pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) *Atom {
2222
23pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 {23pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
24 return switch (rel.tag) {24 return switch (rel.tag) {
25 .local => rel.getTargetAtom(macho_file).value,25 .local => rel.getTargetAtom(macho_file).getAddress(macho_file),
26 .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file),26 .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file),
27 };27 };
28}28}
src/link/MachO/Symbol.zig+2-2
...@@ -118,7 +118,7 @@ pub fn getAddress(symbol: Symbol, opts: struct {...@@ -118,7 +118,7 @@ pub fn getAddress(symbol: Symbol, opts: struct {
118 return symbol.getObjcStubsAddress(macho_file);118 return symbol.getObjcStubsAddress(macho_file);
119 }119 }
120 }120 }
121 if (symbol.getAtom(macho_file)) |atom| return atom.value + symbol.value;121 if (symbol.getAtom(macho_file)) |atom| return atom.getAddress(macho_file) + symbol.value;
122 return symbol.value;122 return symbol.value;
123}123}
124124
...@@ -145,7 +145,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {...@@ -145,7 +145,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
145 const extra = symbol.getExtra(macho_file).?;145 const extra = symbol.getExtra(macho_file).?;
146 const atom = macho_file.getAtom(extra.objc_selrefs).?;146 const atom = macho_file.getAtom(extra.objc_selrefs).?;
147 assert(atom.flags.alive);147 assert(atom.flags.alive);
148 return atom.value;148 return atom.getAddress(macho_file);
149}149}
150150
151pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {151pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
src/link/MachO/UnwindInfo.zig+2-2
...@@ -490,12 +490,12 @@ pub const Record = struct {...@@ -490,12 +490,12 @@ pub const Record = struct {
490490
491 pub fn getAtomAddress(rec: Record, macho_file: *MachO) u64 {491 pub fn getAtomAddress(rec: Record, macho_file: *MachO) u64 {
492 const atom = rec.getAtom(macho_file);492 const atom = rec.getAtom(macho_file);
493 return atom.value + rec.atom_offset;493 return atom.getAddress(macho_file) + rec.atom_offset;
494 }494 }
495495
496 pub fn getLsdaAddress(rec: Record, macho_file: *MachO) u64 {496 pub fn getLsdaAddress(rec: Record, macho_file: *MachO) u64 {
497 const lsda = rec.getLsdaAtom(macho_file) orelse return 0;497 const lsda = rec.getLsdaAtom(macho_file) orelse return 0;
498 return lsda.value + rec.lsda_offset;498 return lsda.getAddress(macho_file) + rec.lsda_offset;
499 }499 }
500500
501 pub fn format(501 pub fn format(
src/link/MachO/ZigObject.zig+7-5
...@@ -154,7 +154,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8...@@ -154,7 +154,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
154 @memset(buffer, 0);154 @memset(buffer, 0);
155 },155 },
156 else => {156 else => {
157 const file_offset = sect.offset + atom.value - sect.addr;157 const file_offset = sect.offset + atom.value;
158 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);158 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
159 if (amt != buffer.len) return error.InputOutput;159 if (amt != buffer.len) return error.InputOutput;
160 },160 },
...@@ -196,8 +196,10 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {...@@ -196,8 +196,10 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {
196 const atom = macho_file.getAtom(atom_index).?;196 const atom = macho_file.getAtom(atom_index).?;
197 break :blk nlist.n_value - atom.getInputAddress(macho_file);197 break :blk nlist.n_value - atom.getInputAddress(macho_file);
198 } else nlist.n_value;198 } else nlist.n_value;
199 const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0;
199 symbol.value = value;200 symbol.value = value;
200 symbol.atom = atom_index;201 symbol.atom = atom_index;
202 symbol.out_n_sect = out_n_sect;
201 symbol.nlist_idx = nlist_idx;203 symbol.nlist_idx = nlist_idx;
202 symbol.file = self.index;204 symbol.file = self.index;
203 symbol.flags.weak = nlist.weakDef();205 symbol.flags.weak = nlist.weakDef();
...@@ -715,7 +717,7 @@ fn updateDeclCode(...@@ -715,7 +717,7 @@ fn updateDeclCode(
715 } else if (code.len < old_size) {717 } else if (code.len < old_size) {
716 atom.shrink(macho_file);718 atom.shrink(macho_file);
717 } else if (macho_file.getAtom(atom.next_index) == null) {719 } else if (macho_file.getAtom(atom.next_index) == null) {
718 const needed_size = atom.value + code.len - sect.addr;720 const needed_size = atom.value + code.len;
719 sect.size = needed_size;721 sect.size = needed_size;
720 }722 }
721 } else {723 } else {
...@@ -733,7 +735,7 @@ fn updateDeclCode(...@@ -733,7 +735,7 @@ fn updateDeclCode(
733 }735 }
734736
735 if (!sect.isZerofill()) {737 if (!sect.isZerofill()) {
736 const file_offset = sect.offset + atom.value - sect.addr;738 const file_offset = sect.offset + atom.value;
737 try macho_file.base.file.?.pwriteAll(code, file_offset);739 try macho_file.base.file.?.pwriteAll(code, file_offset);
738 }740 }
739}741}
...@@ -1036,7 +1038,7 @@ fn lowerConst(...@@ -1036,7 +1038,7 @@ fn lowerConst(
1036 nlist.n_value = 0;1038 nlist.n_value = 0;
10371039
1038 const sect = macho_file.sections.items(.header)[output_section_index];1040 const sect = macho_file.sections.items(.header)[output_section_index];
1039 const file_offset = sect.offset + atom.value - sect.addr;1041 const file_offset = sect.offset + atom.value;
1040 try macho_file.base.file.?.pwriteAll(code, file_offset);1042 try macho_file.base.file.?.pwriteAll(code, file_offset);
10411043
1042 return .{ .ok = sym_index };1044 return .{ .ok = sym_index };
...@@ -1213,7 +1215,7 @@ fn updateLazySymbol(...@@ -1213,7 +1215,7 @@ fn updateLazySymbol(
1213 }1215 }
12141216
1215 const sect = macho_file.sections.items(.header)[output_section_index];1217 const sect = macho_file.sections.items(.header)[output_section_index];
1216 const file_offset = sect.offset + atom.value - sect.addr;1218 const file_offset = sect.offset + atom.value;
1217 try macho_file.base.file.?.pwriteAll(code, file_offset);1219 try macho_file.base.file.?.pwriteAll(code, file_offset);
1218}1220}
12191221
src/link/MachO/eh_frame.zig+4-4
...@@ -416,7 +416,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {...@@ -416,7 +416,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
416 {416 {
417 const offset = fde.out_offset + 8;417 const offset = fde.out_offset + 8;
418 const saddr = sect.addr + offset;418 const saddr = sect.addr + offset;
419 const taddr = fde.getAtom(macho_file).value;419 const taddr = fde.getAtom(macho_file).getAddress(macho_file);
420 std.mem.writeInt(420 std.mem.writeInt(
421 i64,421 i64,
422 buffer[offset..][0..8],422 buffer[offset..][0..8],
...@@ -428,7 +428,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {...@@ -428,7 +428,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
428 if (fde.getLsdaAtom(macho_file)) |atom| {428 if (fde.getLsdaAtom(macho_file)) |atom| {
429 const offset = fde.out_offset + fde.lsda_ptr_offset;429 const offset = fde.out_offset + fde.lsda_ptr_offset;
430 const saddr = sect.addr + offset;430 const saddr = sect.addr + offset;
431 const taddr = atom.value + fde.lsda_offset;431 const taddr = atom.getAddress(macho_file) + fde.lsda_offset;
432 switch (fde.getCie(macho_file).lsda_size.?) {432 switch (fde.getCie(macho_file).lsda_size.?) {
433 .p32 => std.mem.writeInt(433 .p32 => std.mem.writeInt(
434 i32,434 i32,
...@@ -501,7 +501,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho....@@ -501,7 +501,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
501 {501 {
502 const offset = fde.out_offset + 8;502 const offset = fde.out_offset + 8;
503 const saddr = sect.addr + offset;503 const saddr = sect.addr + offset;
504 const taddr = fde.getAtom(macho_file).value;504 const taddr = fde.getAtom(macho_file).getAddress(macho_file);
505 std.mem.writeInt(505 std.mem.writeInt(
506 i64,506 i64,
507 code[offset..][0..8],507 code[offset..][0..8],
...@@ -513,7 +513,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho....@@ -513,7 +513,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
513 if (fde.getLsdaAtom(macho_file)) |atom| {513 if (fde.getLsdaAtom(macho_file)) |atom| {
514 const offset = fde.out_offset + fde.lsda_ptr_offset;514 const offset = fde.out_offset + fde.lsda_ptr_offset;
515 const saddr = sect.addr + offset;515 const saddr = sect.addr + offset;
516 const taddr = atom.value + fde.lsda_offset;516 const taddr = atom.getAddress(macho_file) + fde.lsda_offset;
517 switch (fde.getCie(macho_file).lsda_size.?) {517 switch (fde.getCie(macho_file).lsda_size.?) {
518 .p32 => std.mem.writeInt(518 .p32 => std.mem.writeInt(
519 i32,519 i32,
src/link/MachO/relocatable.zig+153-60
...@@ -12,7 +12,7 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u...@@ -12,7 +12,7 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
1212
13 if (module_obj_path) |path| try positionals.append(.{ .path = path });13 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1414
15 if (positionals.items.len == 1) {15 if (macho_file.getZigObject() == null and positionals.items.len == 1) {
16 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all16 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
17 // debug info segments/sections (this is apparently by design by Apple), we copy17 // debug info segments/sections (this is apparently by design by Apple), we copy
18 // the *only* input file over.18 // the *only* input file over.
...@@ -46,50 +46,23 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u...@@ -46,50 +46,23 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
4646
47 try macho_file.addUndefinedGlobals();47 try macho_file.addUndefinedGlobals();
48 try macho_file.resolveSymbols();48 try macho_file.resolveSymbols();
49 markExports(macho_file);49 try markExports(macho_file);
50 claimUnresolved(macho_file);50 try claimUnresolved(macho_file);
51 try initOutputSections(macho_file);51 try initOutputSections(macho_file);
52 try macho_file.sortSections();52 try macho_file.sortSections();
53 try macho_file.addAtomsToSections();53 try macho_file.addAtomsToSections();
54 try calcSectionSizes(macho_file);54 try calcSectionSizes(macho_file);
5555
56 {56 try createSegment(macho_file);
57 // For relocatable, we only ever need a single segment so create it now.57 try allocateSections(macho_file);
58 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;58 allocateSegment(macho_file);
59 try macho_file.segments.append(gpa, .{
60 .cmdsize = @sizeOf(macho.segment_command_64),
61 .segname = MachO.makeStaticString(""),
62 .maxprot = prot,
63 .initprot = prot,
64 });
65 const seg = &macho_file.segments.items[0];
66 seg.nsects = @intCast(macho_file.sections.items(.header).len);
67 seg.cmdsize += seg.nsects * @sizeOf(macho.section_64);
68 }
69
70 var off = try allocateSections(macho_file);
7159
72 {60 var off = off: {
73 // Allocate the single segment.61 const seg = macho_file.segments.items[0];
74 assert(macho_file.segments.items.len == 1);62 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
75 const seg = &macho_file.segments.items[0];63 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
76 var vmaddr: u64 = 0;64 };
77 var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);65 off = allocateSectionsRelocs(macho_file, off);
78 seg.vmaddr = vmaddr;
79 seg.fileoff = fileoff;
80
81 for (macho_file.sections.items(.header)) |header| {
82 vmaddr = header.addr + header.size;
83 if (!header.isZerofill()) {
84 fileoff = header.offset + header.size;
85 }
86 }
87
88 seg.vmsize = vmaddr - seg.vmaddr;
89 seg.filesize = fileoff - seg.fileoff;
90 }
91
92 macho_file.allocateAtoms();
9366
94 state_log.debug("{}", .{macho_file.dumpState()});67 state_log.debug("{}", .{macho_file.dumpState()});
9568
...@@ -109,8 +82,13 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u...@@ -109,8 +82,13 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
109 try writeHeader(macho_file, ncmds, sizeofcmds);82 try writeHeader(macho_file, ncmds, sizeofcmds);
110}83}
11184
112fn markExports(macho_file: *MachO) void {85fn markExports(macho_file: *MachO) error{OutOfMemory}!void {
113 for (macho_file.objects.items) |index| {86 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
87 defer objects.deinit();
88 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
89 objects.appendSliceAssumeCapacity(macho_file.objects.items);
90
91 for (objects.items) |index| {
114 for (macho_file.getFile(index).?.getSymbols()) |sym_index| {92 for (macho_file.getFile(index).?.getSymbols()) |sym_index| {
115 const sym = macho_file.getSymbol(sym_index);93 const sym = macho_file.getSymbol(sym_index);
116 const file = sym.getFile(macho_file) orelse continue;94 const file = sym.getFile(macho_file) orelse continue;
...@@ -122,13 +100,22 @@ fn markExports(macho_file: *MachO) void {...@@ -122,13 +100,22 @@ fn markExports(macho_file: *MachO) void {
122 }100 }
123}101}
124102
125fn claimUnresolved(macho_file: *MachO) void {103fn claimUnresolved(macho_file: *MachO) error{OutOfMemory}!void {
126 for (macho_file.objects.items) |index| {104 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
127 const object = macho_file.getFile(index).?.object;105 defer objects.deinit();
106 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
107 objects.appendSliceAssumeCapacity(macho_file.objects.items);
108
109 for (objects.items) |index| {
110 const file = macho_file.getFile(index).?;
128111
129 for (object.symbols.items, 0..) |sym_index, i| {112 for (file.getSymbols(), 0..) |sym_index, i| {
130 const nlist_idx = @as(Symbol.Index, @intCast(i));113 const nlist_idx = @as(Symbol.Index, @intCast(i));
131 const nlist = object.symtab.items(.nlist)[nlist_idx];114 const nlist = switch (file) {
115 .object => |x| x.symtab.items(.nlist)[nlist_idx],
116 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
117 else => unreachable,
118 };
132 if (!nlist.ext()) continue;119 if (!nlist.ext()) continue;
133 if (!nlist.undf()) continue;120 if (!nlist.undf()) continue;
134121
...@@ -203,6 +190,16 @@ fn calcSectionSizes(macho_file: *MachO) !void {...@@ -203,6 +190,16 @@ fn calcSectionSizes(macho_file: *MachO) !void {
203 sect.@"align" = 3;190 sect.@"align" = 3;
204 sect.nreloc = eh_frame.calcNumRelocs(macho_file);191 sect.nreloc = eh_frame.calcNumRelocs(macho_file);
205 }192 }
193
194 if (macho_file.getZigObject()) |zo| {
195 for (zo.atoms.items) |atom_index| {
196 const atom = macho_file.getAtom(atom_index) orelse continue;
197 if (!atom.flags.alive) continue;
198 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
199 if (!macho_file.isZigSection(atom.out_n_sect)) continue;
200 header.nreloc += atom.calcNumRelocs(macho_file);
201 }
202 }
206}203}
207204
208fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {205fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
...@@ -231,30 +228,66 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {...@@ -231,30 +228,66 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
231 sect.@"align" = 3;228 sect.@"align" = 3;
232}229}
233230
234fn allocateSections(macho_file: *MachO) !u32 {231fn allocateSections(macho_file: *MachO) !void {
235 var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
236 var vmaddr: u64 = 0;
237 const slice = macho_file.sections.slice();232 const slice = macho_file.sections.slice();
238233
239 for (slice.items(.header)) |*header| {234 const last_index = for (0..slice.items(.header).len) |i| {
235 if (macho_file.isZigSection(@intCast(i))) break i;
236 } else slice.items(.header).len;
237
238 for (slice.items(.header)[0..last_index]) |*header| {
240 const alignment = try math.powi(u32, 2, header.@"align");239 const alignment = try math.powi(u32, 2, header.@"align");
241 vmaddr = mem.alignForward(u64, vmaddr, alignment);240 if (!header.isZerofill()) {
242 header.addr = vmaddr;241 header.offset = math.cast(u32, macho_file.findFreeSpace(header.size, alignment)) orelse
243 vmaddr += header.size;242 return error.Overflow;
243 }
244 header.addr = macho_file.findFreeSpaceVirtual(header.size, alignment);
245 }
246}
247
248fn createSegment(macho_file: *MachO) !void {
249 const gpa = macho_file.base.comp.gpa;
250
251 // For relocatable, we only ever need a single segment so create it now.
252 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;
253 try macho_file.segments.append(gpa, .{
254 .cmdsize = @sizeOf(macho.segment_command_64),
255 .segname = MachO.makeStaticString(""),
256 .maxprot = prot,
257 .initprot = prot,
258 });
259 const seg = &macho_file.segments.items[0];
260 seg.nsects = @intCast(macho_file.sections.items(.header).len);
261 seg.cmdsize += seg.nsects * @sizeOf(macho.section_64);
262}
244263
264fn allocateSegment(macho_file: *MachO) void {
265 // Allocate the single segment.
266 const seg = &macho_file.segments.items[0];
267 var vmaddr: u64 = 0;
268 var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
269 seg.vmaddr = vmaddr;
270 seg.fileoff = fileoff;
271
272 for (macho_file.sections.items(.header)) |header| {
273 vmaddr = @max(vmaddr, header.addr + header.size);
245 if (!header.isZerofill()) {274 if (!header.isZerofill()) {
246 fileoff = mem.alignForward(u32, fileoff, alignment);275 fileoff = @max(fileoff, header.offset + header.size);
247 header.offset = fileoff;
248 fileoff += @intCast(header.size);
249 }276 }
250 }277 }
251278
279 seg.vmsize = vmaddr - seg.vmaddr;
280 seg.filesize = fileoff - seg.fileoff;
281}
282
283fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
284 var fileoff = off;
285 const slice = macho_file.sections.slice();
252 for (slice.items(.header)) |*header| {286 for (slice.items(.header)) |*header| {
253 if (header.nreloc == 0) continue;287 if (header.nreloc == 0) continue;
254 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));288 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
255 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);289 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
256 }290 }
257
258 return fileoff;291 return fileoff;
259}292}
260293
...@@ -272,9 +305,10 @@ fn writeAtoms(macho_file: *MachO) !void {...@@ -272,9 +305,10 @@ fn writeAtoms(macho_file: *MachO) !void {
272 const cpu_arch = macho_file.getTarget().cpu.arch;305 const cpu_arch = macho_file.getTarget().cpu.arch;
273 const slice = macho_file.sections.slice();306 const slice = macho_file.sections.slice();
274307
275 for (slice.items(.header), slice.items(.atoms)) |header, atoms| {308 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
276 if (atoms.items.len == 0) continue;309 if (atoms.items.len == 0) continue;
277 if (header.isZerofill()) continue;310 if (header.isZerofill()) continue;
311 if (macho_file.isZigSection(@intCast(i))) continue;
278312
279 const size = math.cast(usize, header.size) orelse return error.Overflow;313 const size = math.cast(usize, header.size) orelse return error.Overflow;
280 const code = try gpa.alloc(u8, size);314 const code = try gpa.alloc(u8, size);
...@@ -288,9 +322,9 @@ fn writeAtoms(macho_file: *MachO) !void {...@@ -288,9 +322,9 @@ fn writeAtoms(macho_file: *MachO) !void {
288 for (atoms.items) |atom_index| {322 for (atoms.items) |atom_index| {
289 const atom = macho_file.getAtom(atom_index).?;323 const atom = macho_file.getAtom(atom_index).?;
290 assert(atom.flags.alive);324 assert(atom.flags.alive);
291 const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow;325 const off = math.cast(usize, atom.value) orelse return error.Overflow;
292 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;326 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
293 try atom.getFile(macho_file).object.getAtomData(atom.*, code[off..][0..atom_size]);327 try atom.getData(macho_file, code[off..][0..atom_size]);
294 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);328 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
295 }329 }
296330
...@@ -302,6 +336,63 @@ fn writeAtoms(macho_file: *MachO) !void {...@@ -302,6 +336,63 @@ fn writeAtoms(macho_file: *MachO) !void {
302 try macho_file.base.file.?.pwriteAll(code, header.offset);336 try macho_file.base.file.?.pwriteAll(code, header.offset);
303 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);337 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
304 }338 }
339
340 if (macho_file.getZigObject()) |zo| {
341 // TODO: this is ugly; perhaps we should aggregrate before?
342 var relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);
343 defer {
344 for (relocs.values()) |*list| {
345 list.deinit();
346 }
347 relocs.deinit();
348 }
349
350 for (macho_file.sections.items(.header), 0..) |header, n_sect| {
351 if (header.isZerofill()) continue;
352 if (!macho_file.isZigSection(@intCast(n_sect))) continue;
353 const gop = try relocs.getOrPut(@intCast(n_sect));
354 if (gop.found_existing) continue;
355 gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
356 }
357
358 for (zo.atoms.items) |atom_index| {
359 const atom = macho_file.getAtom(atom_index) orelse continue;
360 if (!atom.flags.alive) continue;
361 const header = macho_file.sections.items(.header)[atom.out_n_sect];
362 if (header.isZerofill()) continue;
363 if (!macho_file.isZigSection(atom.out_n_sect)) continue;
364 if (atom.getRelocs(macho_file).len == 0) continue;
365 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
366 const code = try gpa.alloc(u8, atom_size);
367 defer gpa.free(code);
368 atom.getData(macho_file, code) catch |err| switch (err) {
369 error.InputOutput => {
370 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
371 atom.getName(macho_file),
372 });
373 return error.FlushFailure;
374 },
375 else => |e| {
376 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
377 atom.getName(macho_file),
378 @errorName(e),
379 });
380 return error.FlushFailure;
381 },
382 };
383 const file_offset = header.offset + atom.value;
384 const rels = relocs.getPtr(atom.out_n_sect).?;
385 try atom.writeRelocs(macho_file, code, rels);
386 try macho_file.base.file.?.pwriteAll(code, file_offset);
387 }
388
389 for (relocs.keys(), relocs.values()) |sect_id, rels| {
390 const header = macho_file.sections.items(.header)[sect_id];
391 assert(rels.items.len == header.nreloc);
392 mem.sort(macho.relocation_info, rels.items, {}, sortReloc);
393 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff);
394 }
395 }
305}396}
306397
307fn writeCompactUnwind(macho_file: *MachO) !void {398fn writeCompactUnwind(macho_file: *MachO) !void {
...@@ -492,6 +583,7 @@ const assert = std.debug.assert;...@@ -492,6 +583,7 @@ const assert = std.debug.assert;
492const eh_frame = @import("eh_frame.zig");583const eh_frame = @import("eh_frame.zig");
493const link = @import("../../link.zig");584const link = @import("../../link.zig");
494const load_commands = @import("load_commands.zig");585const load_commands = @import("load_commands.zig");
586const log = std.log.scoped(.link);
495const macho = std.macho;587const macho = std.macho;
496const math = std.math;588const math = std.math;
497const mem = std.mem;589const mem = std.mem;
...@@ -501,5 +593,6 @@ const trace = @import("../../tracy.zig").trace;...@@ -501,5 +593,6 @@ const trace = @import("../../tracy.zig").trace;
501593
502const Atom = @import("Atom.zig");594const Atom = @import("Atom.zig");
503const Compilation = @import("../../Compilation.zig");595const Compilation = @import("../../Compilation.zig");
596const File = @import("file.zig").File;
504const MachO = @import("../MachO.zig");597const MachO = @import("../MachO.zig");
505const Symbol = @import("Symbol.zig");598const Symbol = @import("Symbol.zig");
src/link/MachO/thunks.zig+9-4
...@@ -66,7 +66,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {...@@ -66,7 +66,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
66 if (atom.out_n_sect != target.out_n_sect) return false;66 if (atom.out_n_sect != target.out_n_sect) return false;
67 const target_atom = target.getAtom(macho_file).?;67 const target_atom = target.getAtom(macho_file).?;
68 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;68 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
69 const saddr = @as(i64, @intCast(atom.value)) + @as(i64, @intCast(rel.offset - atom.off));69 const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
70 const taddr: i64 = @intCast(rel.getTargetAddress(macho_file));70 const taddr: i64 = @intCast(rel.getTargetAddress(macho_file));
71 _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;71 _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
72 return true;72 return true;
...@@ -85,14 +85,19 @@ pub const Thunk = struct {...@@ -85,14 +85,19 @@ pub const Thunk = struct {
85 return thunk.symbols.keys().len * trampoline_size;85 return thunk.symbols.keys().len * trampoline_size;
86 }86 }
8787
88 pub fn getAddress(thunk: Thunk, sym_index: Symbol.Index) u64 {88 pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 {
89 return thunk.value + thunk.symbols.getIndex(sym_index).? * trampoline_size;89 const header = macho_file.sections.items(.header)[thunk.out_n_sect];
90 return header.addr + thunk.value;
91 }
92
93 pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, macho_file: *MachO) u64 {
94 return thunk.getAddress(macho_file) + thunk.symbols.getIndex(sym_index).? * trampoline_size;
90 }95 }
9196
92 pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {97 pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
93 for (thunk.symbols.keys(), 0..) |sym_index, i| {98 for (thunk.symbols.keys(), 0..) |sym_index, i| {
94 const sym = macho_file.getSymbol(sym_index);99 const sym = macho_file.getSymbol(sym_index);
95 const saddr = thunk.value + i * trampoline_size;100 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
96 const taddr = sym.getAddress(.{}, macho_file);101 const taddr = sym.getAddress(.{}, macho_file);
97 const pages = try Relocation.calcNumberOfPages(saddr, taddr);102 const pages = try Relocation.calcNumberOfPages(saddr, taddr);
98 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);103 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
test/link/link.zig+12-3
...@@ -27,14 +27,23 @@ pub const Options = struct {...@@ -27,14 +27,23 @@ pub const Options = struct {
27 optimize: std.builtin.OptimizeMode = .Debug,27 optimize: std.builtin.OptimizeMode = .Debug,
28 use_llvm: bool = true,28 use_llvm: bool = true,
29 use_lld: bool = false,29 use_lld: bool = false,
30 strip: ?bool = null,
30};31};
3132
32pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step {33pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step {
33 const target = opts.target.result.zigTriple(b.allocator) catch @panic("OOM");34 const target = opts.target.result.zigTriple(b.allocator) catch @panic("OOM");
34 const optimize = @tagName(opts.optimize);35 const optimize = @tagName(opts.optimize);
35 const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";36 const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
36 const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}", .{37 const use_lld = if (opts.use_lld) "lld" else "no-lld";
37 prefix, target, optimize, use_llvm,38 if (opts.strip) |strip| {
39 const s = if (strip) "strip" else "no-strip";
40 const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}-{s}-{s}", .{
41 prefix, target, optimize, use_llvm, use_lld, s,
42 }) catch @panic("OOM");
43 return b.step(name, "");
44 }
45 const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}-{s}", .{
46 prefix, target, optimize, use_llvm, use_lld,
38 }) catch @panic("OOM");47 }) catch @panic("OOM");
39 return b.step(name, "");48 return b.step(name, "");
40}49}
...@@ -87,7 +96,7 @@ fn addCompileStep(...@@ -87,7 +96,7 @@ fn addCompileStep(
87 break :rsf b.addWriteFiles().add("a.zig", bytes);96 break :rsf b.addWriteFiles().add("a.zig", bytes);
88 },97 },
89 .pic = overlay.pic,98 .pic = overlay.pic,
90 .strip = overlay.strip,99 .strip = if (base.strip) |s| s else overlay.strip,
91 },100 },
92 .use_llvm = base.use_llvm,101 .use_llvm = base.use_llvm,
93 .use_lld = base.use_lld,102 .use_lld = base.use_lld,
test/link/macho.zig+14-1
...@@ -15,6 +15,11 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -15,6 +15,11 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
15 .os_tag = .macos,15 .os_tag = .macos,
16 });16 });
1717
18 // Exercise linker with self-hosted backend (no LLVM)
19 macho_step.dependOn(testHelloZig(b, .{ .use_llvm = false, .target = x86_64_target }));
20 macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .strip = true, .target = x86_64_target }));
21
22 // Exercise linker with LLVM backend
18 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));23 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
19 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));24 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
20 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));25 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
...@@ -1234,7 +1239,14 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step {...@@ -1234,7 +1239,14 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step {
1234 const run = addRunArtifact(exe);1239 const run = addRunArtifact(exe);
1235 run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") });1240 run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") });
1236 run.addCheck(.{ .expect_stderr_match = b.dupe("decrFoo=0") });1241 run.addCheck(.{ .expect_stderr_match = b.dupe("decrFoo=0") });
1237 run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") });1242 if (opts.use_llvm) {
1243 // TODO: enable this once self-hosted can print panics and stack traces
1244 run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") });
1245 }
1246 if (builtin.os.tag == .macos) {
1247 const signal: u32 = if (opts.use_llvm) std.os.darwin.SIG.ABRT else std.os.darwin.SIG.TRAP;
1248 run.addCheck(.{ .expect_term = .{ .Signal = signal } });
1249 }
1238 test_step.dependOn(&run.step);1250 test_step.dependOn(&run.step);
12391251
1240 return test_step;1252 return test_step;
...@@ -2307,6 +2319,7 @@ fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {...@@ -2307,6 +2319,7 @@ fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
2307 return link.addTestStep(b, "macho-" ++ prefix, opts);2319 return link.addTestStep(b, "macho-" ++ prefix, opts);
2308}2320}
23092321
2322const builtin = @import("builtin");
2310const addAsmSourceBytes = link.addAsmSourceBytes;2323const addAsmSourceBytes = link.addAsmSourceBytes;
2311const addCSourceBytes = link.addCSourceBytes;2324const addCSourceBytes = link.addCSourceBytes;
2312const addRunArtifact = link.addRunArtifact;2325const addRunArtifact = link.addRunArtifact;