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(
285285 };
286286 try self.d_sym.?.initMetadata(self);
287287 } else {
288 try self.reportUnexpectedError("TODO: implement generating and emitting __DWARF in .o file", .{});
289 return error.Unexpected;
288 @panic("TODO: implement generating and emitting __DWARF in .o file");
290289 },
291290 .code_view => unreachable,
292291 }
......@@ -597,7 +596,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
597596
598597 try self.allocateSections();
599598 self.allocateSegments();
600 self.allocateAtoms();
601599 self.allocateSyntheticSymbols();
602600 try self.allocateLinkeditSegment();
603601
......@@ -615,7 +613,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
615613 if (!atom.flags.alive) continue;
616614 const sect = &self.sections.items(.header)[atom.out_n_sect];
617615 if (sect.isZerofill()) continue;
618 if (mem.indexOf(u8, sect.segName(), "ZIG") == null) continue; // Non-Zig sections are handled separately
616 if (!self.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
617 if (atom.getRelocs(self).len == 0) continue;
619618 // TODO: we will resolve and write ZigObject's TLS data twice:
620619 // once here, and once in writeAtoms
621620 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
636635 return error.FlushFailure;
637636 },
638637 };
639 const file_offset = sect.offset + atom.value - sect.addr;
638 const file_offset = sect.offset + atom.value;
640639 atom.resolveRelocs(self, code) catch |err| switch (err) {
641640 error.ResolveFailed => has_resolve_error = true,
642641 else => |e| {
......@@ -2025,7 +2024,7 @@ pub fn sortSections(self: *MachO) !void {
20252024
20262025 for (zo.symtab.items(.nlist)) |*sym| {
20272026 if (sym.sect()) {
2028 sym.n_sect = backlinks[sym.n_sect];
2027 sym.n_sect = backlinks[sym.n_sect - 1] + 1;
20292028 }
20302029 }
20312030
......@@ -2232,11 +2231,11 @@ fn initSegments(self: *MachO) !void {
22322231 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_size});
22332232 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_size});
22342233 }
2235 _ = try self.addSegment("__PAGEZERO", .{ .vmsize = aligned_pagezero_size });
2234 self.pagezero_seg_index = try self.addSegment("__PAGEZERO", .{ .vmsize = aligned_pagezero_size });
22362235 }
22372236
22382237 // __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
22412240 // Next, create segments required by sections
22422241 for (slice.items(.header)) |header| {
......@@ -2248,15 +2247,57 @@ fn initSegments(self: *MachO) !void {
22482247 }
22492248
22502249 // Add __LINKEDIT
2251 _ = try self.addSegment("__LINKEDIT", .{ .prot = getSegmentProt("__LINKEDIT") });
2250 self.linkedit_seg_index = try self.addSegment("__LINKEDIT", .{ .prot = getSegmentProt("__LINKEDIT") });
22522251
22532252 // Sort segments
2254 const sortFn = struct {
2255 fn sortFn(ctx: void, lhs: macho.segment_command_64, rhs: macho.segment_command_64) bool {
2256 return segmentLessThan(ctx, lhs.segName(), rhs.segName());
2253 const Entry = struct {
2254 index: u8,
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 );
22572262 }
2258 }.sortFn;
2259 mem.sort(macho.segment_command_64, self.segments.items, {}, sortFn);
2263 };
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
22612302 // Attach sections to segments
22622303 for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| {
......@@ -2277,15 +2318,6 @@ fn initSegments(self: *MachO) !void {
22772318 segment.nsects += 1;
22782319 seg_id.* = segment_id;
22792320 }
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");
22892321}
22902322
22912323fn allocateSections(self: *MachO) !void {
......@@ -2300,8 +2332,8 @@ fn allocateSections(self: *MachO) !void {
23002332
23012333 const page_size = self.getPageSize();
23022334 const slice = self.sections.slice();
2303 const last_index = for (slice.items(.header), 0..) |header, i| {
2304 if (mem.indexOf(u8, header.segName(), "ZIG")) |_| break i;
2335 const last_index = for (0..slice.items(.header).len) |i| {
2336 if (self.isZigSection(@intCast(i))) break i;
23052337 } else slice.items(.header).len;
23062338
23072339 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 {
23542386/// We allocate segments in a separate step to also consider segments that have no sections.
23552387fn allocateSegments(self: *MachO) void {
23562388 const first_index = if (self.pagezero_seg_index) |index| index + 1 else 0;
2357 const last_index = for (self.segments.items, 0..) |seg, i| {
2358 if (mem.indexOf(u8, seg.segName(), "ZIG")) |_| break i;
2389 const last_index = for (0..self.segments.items.len) |i| {
2390 if (self.isZigSegment(@intCast(i))) break i;
23592391 } else self.segments.items.len;
23602392
23612393 var vmaddr: u64 = if (self.pagezero_seg_index) |index|
......@@ -2392,23 +2424,6 @@ fn allocateSegments(self: *MachO) void {
23922424 }
23932425}
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
24122427fn allocateSyntheticSymbols(self: *MachO) void {
24132428 const text_seg = self.getTextSegment();
24142429
......@@ -2603,7 +2618,7 @@ fn writeAtoms(self: *MachO) !void {
26032618 for (atoms.items) |atom_index| {
26042619 const atom = self.getAtom(atom_index).?;
26052620 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;
26072622 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
26082623 try atom.getData(self, buffer[off..][0..atom_size]);
26092624 atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
......@@ -2617,7 +2632,7 @@ fn writeAtoms(self: *MachO) !void {
26172632
26182633 for (self.thunks.items) |thunk| {
26192634 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;
26212636 const buffer = try gpa.alloc(u8, thunk.size());
26222637 defer gpa.free(buffer);
26232638 var stream = std.io.fixedBufferStream(buffer);
......@@ -2825,7 +2840,7 @@ pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {
28252840
28262841 if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
28272842 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),
28292844 .length = dice.length,
28302845 .kind = dice.kind,
28312846 });
......@@ -3276,6 +3291,34 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
32763291 return null;
32773292}
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
32793322fn allocatedSize(self: *MachO, start: u64) u64 {
32803323 if (start == 0) return 0;
32813324 var min_pos: u64 = std.math.maxInt(u64);
......@@ -3290,7 +3333,7 @@ fn allocatedSize(self: *MachO, start: u64) u64 {
32903333 return min_pos - start;
32913334}
32923335
3293fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
3336fn allocatedSizeVirtual(self: *MachO, start: u64) u64 {
32943337 if (start == 0) return 0;
32953338 var min_pos: u64 = std.math.maxInt(u64);
32963339 for (self.segments.items) |seg| {
......@@ -3300,7 +3343,7 @@ fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
33003343 return min_pos - start;
33013344}
33023345
3303fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3346pub fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
33043347 var start: u64 = 0;
33053348 while (self.detectAllocCollision(start, object_size)) |item_end| {
33063349 start = mem.alignForward(u64, item_end, min_alignment);
......@@ -3308,18 +3351,30 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
33083351 return start;
33093352}
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
33113368/// Like File.copyRangeAll but also ensures the source region is zeroed out after copy.
33123369/// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader.
33133370fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {
33143371 const gpa = self.base.comp.gpa;
3315 const file = self.base.file.?;
3316 const amt = try file.copyRangeAll(old_offset, file, new_offset, size);
3317 if (amt != size) return error.InputOutput;
3372 try self.copyRangeAll(old_offset, new_offset, size);
33183373 const size_u = math.cast(usize, size) orelse return error.Overflow;
33193374 const zeroes = try gpa.alloc(u8, size_u);
33203375 defer gpa.free(zeroes);
33213376 @memset(zeroes, 0);
3322 try file.pwriteAll(zeroes, old_offset);
3377 try self.base.file.?.pwriteAll(zeroes, old_offset);
33233378}
33243379
33253380const InitMetadataOptions = struct {
......@@ -3391,8 +3446,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
33913446 .prot = macho.PROT.READ | macho.PROT.WRITE,
33923447 });
33933448 }
3394 } else {
3395 @panic("TODO initMetadata when relocatable");
33963449 }
33973450
33983451 const appendSect = struct {
......@@ -3406,6 +3459,19 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
34063459 }
34073460 }.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
34093475 {
34103476 self.zig_text_sect_index = try self.addSection("__TEXT_ZIG", "__text_zig", .{
34113477 .alignment = switch (self.getTarget().cpu.arch) {
......@@ -3415,7 +3481,11 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
34153481 },
34163482 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
34173483 });
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 }
34193489 }
34203490
34213491 if (!self.base.isRelocatable()) {
......@@ -3427,33 +3497,52 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
34273497
34283498 {
34293499 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 }
34313505 }
34323506
34333507 {
34343508 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 }
34363514 }
34373515
34383516 {
34393517 self.zig_bss_sect_index = try self.addSection("__BSS_ZIG", "__bss_zig", .{
34403518 .flags = macho.S_ZEROFILL,
34413519 });
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 }
34433525 }
34443526}
34453527
34463528pub 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 {
34473537 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
34513539 if (needed_size > self.allocatedSize(sect.offset) and !sect.isZerofill()) {
34523540 const existing_size = sect.size;
34533541 sect.size = 0;
34543542
34553543 // 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
34583547 log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{
34593548 sect.segName(),
......@@ -3465,15 +3554,19 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
34653554 try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size);
34663555
34673556 sect.offset = @intCast(new_offset);
3468 seg.fileoff = new_offset;
34693557 }
34703558
34713559 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
34723565 if (!sect.isZerofill()) {
34733566 seg.filesize = needed_size;
34743567 }
34753568
3476 const mem_capacity = self.allocatedVirtualSize(seg.vmaddr);
3569 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
34773570 if (needed_size > mem_capacity) {
34783571 var err = try self.addErrorWithNotes(2);
34793572 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 {
34873580 seg.vmsize = needed_size;
34883581}
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
34903613pub fn getTarget(self: MachO) std.Target {
34913614 return self.base.comp.root_mod.resolved_target.result;
34923615}
......@@ -3532,6 +3655,36 @@ inline fn requiresThunks(self: MachO) bool {
35323655 return self.getTarget().cpu.arch == .aarch64;
35333656}
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
35353688pub fn addSegment(self: *MachO, name: []const u8, opts: struct {
35363689 vmaddr: u64 = 0,
35373690 vmsize: u64 = 0,
......@@ -4033,10 +4186,13 @@ fn formatSections(
40334186 _ = unused_fmt_string;
40344187 const slice = self.sections.slice();
40354188 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", .{
4037 i, seg_id, header.segName(), header.sectName(), header.offset, header.addr,
4038 header.@"align", header.size,
4039 });
4189 try writer.print(
4190 "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n",
4191 .{
4192 i, seg_id, header.segName(), header.sectName(), header.addr, header.offset,
4193 header.@"align", header.size, header.reloff, header.nreloc,
4194 },
4195 );
40404196 }
40414197}
40424198
src/link/MachO/Atom.zig+39-34
......@@ -1,4 +1,4 @@
1/// Address allocated for this Atom.
1/// Address offset allocated for this Atom wrt to its section start address.
22value: u64 = 0,
33
44/// Name of this Atom.
......@@ -84,6 +84,11 @@ pub fn getInputAddress(self: Atom, macho_file: *MachO) u64 {
8484 return self.getInputSection(macho_file).addr + self.off;
8585}
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
8792pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
8893 const file = self.getFile(macho_file);
8994 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 {
114119
115120pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
116121 const segname, const sectname, const flags = blk: {
122 const segname = sect.segName();
123 const sectname = sect.sectName();
124
117125 if (sect.isCode()) break :blk .{
118126 "__TEXT",
119 sect.sectName(),
127 sectname,
120128 macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
121129 };
122130
......@@ -127,34 +135,29 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
127135 => break :blk .{ "__TEXT", "__const", macho.S_REGULAR },
128136
129137 macho.S_CSTRING_LITERALS => {
130 if (mem.startsWith(u8, sect.sectName(), "__objc")) break :blk .{
131 sect.segName(), sect.sectName(), macho.S_REGULAR,
138 if (mem.startsWith(u8, sectname, "__objc")) break :blk .{
139 segname, sectname, macho.S_REGULAR,
132140 };
133141 break :blk .{ "__TEXT", "__cstring", macho.S_CSTRING_LITERALS };
134142 },
135143
136144 macho.S_MOD_INIT_FUNC_POINTERS,
137145 macho.S_MOD_TERM_FUNC_POINTERS,
138 => break :blk .{ "__DATA_CONST", sect.sectName(), sect.flags },
139
140146 macho.S_LITERAL_POINTERS,
147 => break :blk .{ "__DATA_CONST", sectname, sect.flags },
148
141149 macho.S_ZEROFILL,
142150 macho.S_GB_ZEROFILL,
143151 macho.S_THREAD_LOCAL_VARIABLES,
144152 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
145153 macho.S_THREAD_LOCAL_REGULAR,
146154 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 .{
150 sect.segName(),
151 sect.sectName(),
152 macho.S_REGULAR,
153 },
157 // TODO: do we need this check here?
158 macho.S_COALESCED => break :blk .{ segname, sectname, macho.S_REGULAR },
154159
155160 macho.S_REGULAR => {
156 const segname = sect.segName();
157 const sectname = sect.sectName();
158161 if (mem.eql(u8, segname, "__DATA")) {
159162 if (mem.eql(u8, sectname, "__const") or
160163 mem.eql(u8, sectname, "__cfstring") or
......@@ -168,7 +171,7 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
168171 break :blk .{ segname, sectname, sect.flags };
169172 },
170173
171 else => break :blk .{ sect.segName(), sect.sectName(), sect.flags },
174 else => break :blk .{ segname, sectname, sect.flags },
172175 }
173176 };
174177 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 {
189192/// File offset relocation happens transparently, so it is not included in
190193/// this calculation.
191194pub 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);
193 return next_value - self.value;
195 const next_addr = if (macho_file.getAtom(self.next_index)) |next|
196 next.getAddress(macho_file)
197 else
198 std.math.maxInt(u32);
199 return next_addr - self.getAddress(macho_file);
194200}
195201
196202pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
197203 // No need to keep a free list node for the last block.
198204 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);
200206 const ideal_cap = MachO.padToIdeal(self.size);
201207 if (cap <= ideal_cap) return false;
202208 const surplus = cap - ideal_cap;
......@@ -263,15 +269,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
263269 atom_placement = last.atom_index;
264270 break :blk new_start_vaddr;
265271 } else {
266 break :blk sect.addr;
272 break :blk 0;
267273 }
268274 };
269275
270276 log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{
271277 self.atom_index,
272278 self.getName(macho_file),
273 self.value,
274 self.value + self.size,
279 self.getAddress(macho_file),
280 self.getAddress(macho_file) + self.size,
275281 });
276282
277283 const expand_section = if (atom_placement) |placement_index|
......@@ -279,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
279285 else
280286 true;
281287 if (expand_section) {
282 const needed_size = (self.value + self.size) - sect.addr;
288 const needed_size = self.value + self.size;
283289 try macho_file.growSection(self.out_n_sect, needed_size);
284290 last_atom_index.* = self.atom_index;
285291
......@@ -544,7 +550,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
544550 const name = self.getName(macho_file);
545551 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
549555 var has_error = false;
550556 var stream = std.io.fixedBufferStream(buffer);
......@@ -569,7 +575,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
569575 try macho_file.reportParseError2(
570576 file.getIndex(),
571577 "{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 },
573579 );
574580 has_error = true;
575581 },
......@@ -604,7 +610,7 @@ fn resolveRelocInner(
604610 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
605611 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
606612 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));
608614 const A = rel.addend + rel.getRelocAddend(cpu_arch);
609615 const S: i64 = @intCast(rel.getTargetAddress(macho_file));
610616 const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));
......@@ -690,7 +696,7 @@ fn resolveRelocInner(
690696 .aarch64 => {
691697 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
692698 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));
694700 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
695701 };
696702 var inst = aarch64.Instruction{
......@@ -919,7 +925,7 @@ const x86_64 = struct {
919925 var err = try macho_file.addErrorWithNotes(2);
920926 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{
921927 self.getName(macho_file),
922 self.value,
928 self.getAddress(macho_file),
923929 rel.offset,
924930 @tagName(rel.type),
925931 });
......@@ -990,12 +996,11 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
990996
991997 const cpu_arch = macho_file.getTarget().cpu.arch;
992998 const relocs = self.getRelocs(macho_file);
993 const sect = macho_file.sections.items(.header)[self.out_n_sect];
994999 var stream = std.io.fixedBufferStream(code);
9951000
9961001 for (relocs) |rel| {
9971002 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;
9991004 const r_symbolnum = r_symbolnum: {
10001005 const r_symbolnum: u32 = switch (rel.tag) {
10011006 .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
10621067 .x86_64 => {
10631068 if (rel.meta.pcrel) {
10641069 if (rel.tag == .local) {
1065 addend -= @as(i64, @intCast(self.value + rel_offset));
1070 addend -= @as(i64, @intCast(self.getAddress(macho_file) + rel_offset));
10661071 } else {
10671072 addend += 4;
10681073 }
......@@ -1143,10 +1148,10 @@ fn format2(
11431148 _ = unused_fmt_string;
11441149 const atom = ctx.atom;
11451150 const macho_file = ctx.macho_file;
1146 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : thunk({d})", .{
1147 atom.atom_index, atom.getName(macho_file), atom.value,
1148 atom.out_n_sect, atom.alignment, atom.size,
1149 atom.thunk_index,
1151 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1152 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1153 atom.out_n_sect, atom.alignment, atom.size,
1154 atom.getRelocs(macho_file).len, atom.thunk_index,
11501155 });
11511156 if (!atom.flags.alive) try writer.writeAll(" : [*]");
11521157 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 {
2222
2323pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
2424 return switch (rel.tag) {
25 .local => rel.getTargetAtom(macho_file).value,
25 .local => rel.getTargetAtom(macho_file).getAddress(macho_file),
2626 .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file),
2727 };
2828}
src/link/MachO/Symbol.zig+2-2
......@@ -118,7 +118,7 @@ pub fn getAddress(symbol: Symbol, opts: struct {
118118 return symbol.getObjcStubsAddress(macho_file);
119119 }
120120 }
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;
122122 return symbol.value;
123123}
124124
......@@ -145,7 +145,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
145145 const extra = symbol.getExtra(macho_file).?;
146146 const atom = macho_file.getAtom(extra.objc_selrefs).?;
147147 assert(atom.flags.alive);
148 return atom.value;
148 return atom.getAddress(macho_file);
149149}
150150
151151pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
src/link/MachO/UnwindInfo.zig+2-2
......@@ -490,12 +490,12 @@ pub const Record = struct {
490490
491491 pub fn getAtomAddress(rec: Record, macho_file: *MachO) u64 {
492492 const atom = rec.getAtom(macho_file);
493 return atom.value + rec.atom_offset;
493 return atom.getAddress(macho_file) + rec.atom_offset;
494494 }
495495
496496 pub fn getLsdaAddress(rec: Record, macho_file: *MachO) u64 {
497497 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;
499499 }
500500
501501 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
154154 @memset(buffer, 0);
155155 },
156156 else => {
157 const file_offset = sect.offset + atom.value - sect.addr;
157 const file_offset = sect.offset + atom.value;
158158 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
159159 if (amt != buffer.len) return error.InputOutput;
160160 },
......@@ -196,8 +196,10 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {
196196 const atom = macho_file.getAtom(atom_index).?;
197197 break :blk nlist.n_value - atom.getInputAddress(macho_file);
198198 } else nlist.n_value;
199 const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0;
199200 symbol.value = value;
200201 symbol.atom = atom_index;
202 symbol.out_n_sect = out_n_sect;
201203 symbol.nlist_idx = nlist_idx;
202204 symbol.file = self.index;
203205 symbol.flags.weak = nlist.weakDef();
......@@ -715,7 +717,7 @@ fn updateDeclCode(
715717 } else if (code.len < old_size) {
716718 atom.shrink(macho_file);
717719 } 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;
719721 sect.size = needed_size;
720722 }
721723 } else {
......@@ -733,7 +735,7 @@ fn updateDeclCode(
733735 }
734736
735737 if (!sect.isZerofill()) {
736 const file_offset = sect.offset + atom.value - sect.addr;
738 const file_offset = sect.offset + atom.value;
737739 try macho_file.base.file.?.pwriteAll(code, file_offset);
738740 }
739741}
......@@ -1036,7 +1038,7 @@ fn lowerConst(
10361038 nlist.n_value = 0;
10371039
10381040 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;
10401042 try macho_file.base.file.?.pwriteAll(code, file_offset);
10411043
10421044 return .{ .ok = sym_index };
......@@ -1213,7 +1215,7 @@ fn updateLazySymbol(
12131215 }
12141216
12151217 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;
12171219 try macho_file.base.file.?.pwriteAll(code, file_offset);
12181220}
12191221
src/link/MachO/eh_frame.zig+4-4
......@@ -416,7 +416,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
416416 {
417417 const offset = fde.out_offset + 8;
418418 const saddr = sect.addr + offset;
419 const taddr = fde.getAtom(macho_file).value;
419 const taddr = fde.getAtom(macho_file).getAddress(macho_file);
420420 std.mem.writeInt(
421421 i64,
422422 buffer[offset..][0..8],
......@@ -428,7 +428,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
428428 if (fde.getLsdaAtom(macho_file)) |atom| {
429429 const offset = fde.out_offset + fde.lsda_ptr_offset;
430430 const saddr = sect.addr + offset;
431 const taddr = atom.value + fde.lsda_offset;
431 const taddr = atom.getAddress(macho_file) + fde.lsda_offset;
432432 switch (fde.getCie(macho_file).lsda_size.?) {
433433 .p32 => std.mem.writeInt(
434434 i32,
......@@ -501,7 +501,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
501501 {
502502 const offset = fde.out_offset + 8;
503503 const saddr = sect.addr + offset;
504 const taddr = fde.getAtom(macho_file).value;
504 const taddr = fde.getAtom(macho_file).getAddress(macho_file);
505505 std.mem.writeInt(
506506 i64,
507507 code[offset..][0..8],
......@@ -513,7 +513,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
513513 if (fde.getLsdaAtom(macho_file)) |atom| {
514514 const offset = fde.out_offset + fde.lsda_ptr_offset;
515515 const saddr = sect.addr + offset;
516 const taddr = atom.value + fde.lsda_offset;
516 const taddr = atom.getAddress(macho_file) + fde.lsda_offset;
517517 switch (fde.getCie(macho_file).lsda_size.?) {
518518 .p32 => std.mem.writeInt(
519519 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
1212
1313 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) {
1616 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
1717 // debug info segments/sections (this is apparently by design by Apple), we copy
1818 // the *only* input file over.
......@@ -46,50 +46,23 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
4646
4747 try macho_file.addUndefinedGlobals();
4848 try macho_file.resolveSymbols();
49 markExports(macho_file);
50 claimUnresolved(macho_file);
49 try markExports(macho_file);
50 try claimUnresolved(macho_file);
5151 try initOutputSections(macho_file);
5252 try macho_file.sortSections();
5353 try macho_file.addAtomsToSections();
5454 try calcSectionSizes(macho_file);
5555
56 {
57 // For relocatable, we only ever need a single segment so create it now.
58 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;
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);
56 try createSegment(macho_file);
57 try allocateSections(macho_file);
58 allocateSegment(macho_file);
7159
72 {
73 // Allocate the single segment.
74 assert(macho_file.segments.items.len == 1);
75 const seg = &macho_file.segments.items[0];
76 var vmaddr: u64 = 0;
77 var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
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();
60 var off = off: {
61 const seg = macho_file.segments.items[0];
62 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
63 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
64 };
65 off = allocateSectionsRelocs(macho_file, off);
9366
9467 state_log.debug("{}", .{macho_file.dumpState()});
9568
......@@ -109,8 +82,13 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
10982 try writeHeader(macho_file, ncmds, sizeofcmds);
11083}
11184
112fn markExports(macho_file: *MachO) void {
113 for (macho_file.objects.items) |index| {
85fn markExports(macho_file: *MachO) error{OutOfMemory}!void {
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| {
11492 for (macho_file.getFile(index).?.getSymbols()) |sym_index| {
11593 const sym = macho_file.getSymbol(sym_index);
11694 const file = sym.getFile(macho_file) orelse continue;
......@@ -122,13 +100,22 @@ fn markExports(macho_file: *MachO) void {
122100 }
123101}
124102
125fn claimUnresolved(macho_file: *MachO) void {
126 for (macho_file.objects.items) |index| {
127 const object = macho_file.getFile(index).?.object;
103fn claimUnresolved(macho_file: *MachO) error{OutOfMemory}!void {
104 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
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| {
130113 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 };
132119 if (!nlist.ext()) continue;
133120 if (!nlist.undf()) continue;
134121
......@@ -203,6 +190,16 @@ fn calcSectionSizes(macho_file: *MachO) !void {
203190 sect.@"align" = 3;
204191 sect.nreloc = eh_frame.calcNumRelocs(macho_file);
205192 }
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 }
206203}
207204
208205fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
......@@ -231,30 +228,66 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
231228 sect.@"align" = 3;
232229}
233230
234fn allocateSections(macho_file: *MachO) !u32 {
235 var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
236 var vmaddr: u64 = 0;
231fn allocateSections(macho_file: *MachO) !void {
237232 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| {
240239 const alignment = try math.powi(u32, 2, header.@"align");
241 vmaddr = mem.alignForward(u64, vmaddr, alignment);
242 header.addr = vmaddr;
243 vmaddr += header.size;
240 if (!header.isZerofill()) {
241 header.offset = math.cast(u32, macho_file.findFreeSpace(header.size, alignment)) orelse
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);
245274 if (!header.isZerofill()) {
246 fileoff = mem.alignForward(u32, fileoff, alignment);
247 header.offset = fileoff;
248 fileoff += @intCast(header.size);
275 fileoff = @max(fileoff, header.offset + header.size);
249276 }
250277 }
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();
252286 for (slice.items(.header)) |*header| {
253287 if (header.nreloc == 0) continue;
254288 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
255289 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
256290 }
257
258291 return fileoff;
259292}
260293
......@@ -272,9 +305,10 @@ fn writeAtoms(macho_file: *MachO) !void {
272305 const cpu_arch = macho_file.getTarget().cpu.arch;
273306 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| {
276309 if (atoms.items.len == 0) continue;
277310 if (header.isZerofill()) continue;
311 if (macho_file.isZigSection(@intCast(i))) continue;
278312
279313 const size = math.cast(usize, header.size) orelse return error.Overflow;
280314 const code = try gpa.alloc(u8, size);
......@@ -288,9 +322,9 @@ fn writeAtoms(macho_file: *MachO) !void {
288322 for (atoms.items) |atom_index| {
289323 const atom = macho_file.getAtom(atom_index).?;
290324 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;
292326 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]);
294328 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
295329 }
296330
......@@ -302,6 +336,63 @@ fn writeAtoms(macho_file: *MachO) !void {
302336 try macho_file.base.file.?.pwriteAll(code, header.offset);
303337 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
304338 }
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 }
305396}
306397
307398fn writeCompactUnwind(macho_file: *MachO) !void {
......@@ -492,6 +583,7 @@ const assert = std.debug.assert;
492583const eh_frame = @import("eh_frame.zig");
493584const link = @import("../../link.zig");
494585const load_commands = @import("load_commands.zig");
586const log = std.log.scoped(.link);
495587const macho = std.macho;
496588const math = std.math;
497589const mem = std.mem;
......@@ -501,5 +593,6 @@ const trace = @import("../../tracy.zig").trace;
501593
502594const Atom = @import("Atom.zig");
503595const Compilation = @import("../../Compilation.zig");
596const File = @import("file.zig").File;
504597const MachO = @import("../MachO.zig");
505598const 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 {
6666 if (atom.out_n_sect != target.out_n_sect) return false;
6767 const target_atom = target.getAtom(macho_file).?;
6868 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));
7070 const taddr: i64 = @intCast(rel.getTargetAddress(macho_file));
7171 _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
7272 return true;
......@@ -85,14 +85,19 @@ pub const Thunk = struct {
8585 return thunk.symbols.keys().len * trampoline_size;
8686 }
8787
88 pub fn getAddress(thunk: Thunk, sym_index: Symbol.Index) u64 {
89 return thunk.value + thunk.symbols.getIndex(sym_index).? * trampoline_size;
88 pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 {
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;
9095 }
9196
9297 pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
9398 for (thunk.symbols.keys(), 0..) |sym_index, i| {
9499 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;
96101 const taddr = sym.getAddress(.{}, macho_file);
97102 const pages = try Relocation.calcNumberOfPages(saddr, taddr);
98103 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 {
2727 optimize: std.builtin.OptimizeMode = .Debug,
2828 use_llvm: bool = true,
2929 use_lld: bool = false,
30 strip: ?bool = null,
3031};
3132
3233pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step {
3334 const target = opts.target.result.zigTriple(b.allocator) catch @panic("OOM");
3435 const optimize = @tagName(opts.optimize);
3536 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 prefix, target, optimize, use_llvm,
37 const use_lld = if (opts.use_lld) "lld" else "no-lld";
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,
3847 }) catch @panic("OOM");
3948 return b.step(name, "");
4049}
......@@ -87,7 +96,7 @@ fn addCompileStep(
8796 break :rsf b.addWriteFiles().add("a.zig", bytes);
8897 },
8998 .pic = overlay.pic,
90 .strip = overlay.strip,
99 .strip = if (base.strip) |s| s else overlay.strip,
91100 },
92101 .use_llvm = base.use_llvm,
93102 .use_lld = base.use_lld,
test/link/macho.zig+14-1
......@@ -15,6 +15,11 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
1515 .os_tag = .macos,
1616 });
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
1823 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
1924 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
2025 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
......@@ -1234,7 +1239,14 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step {
12341239 const run = addRunArtifact(exe);
12351240 run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") });
12361241 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 }
12381250 test_step.dependOn(&run.step);
12391251
12401252 return test_step;
......@@ -2307,6 +2319,7 @@ fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
23072319 return link.addTestStep(b, "macho-" ++ prefix, opts);
23082320}
23092321
2322const builtin = @import("builtin");
23102323const addAsmSourceBytes = link.addAsmSourceBytes;
23112324const addCSourceBytes = link.addCSourceBytes;
23122325const addRunArtifact = link.addRunArtifact;