| author | |
| committer | |
| log | 9bf97b8494524074b1d3cfe71cd08aae335ba576 |
| tree | 531e0ecdc83b766752696417bf35f8a411cb9f7b |
| parent | 5b803aecfb302718b67c465adfdaaef500ab8c68 |
| parent | ca86dc61ddf1f2dd96f78e217ade71f04604d144 |
| signature |
macho: emit relocatable with self-hosted x86_64 backend11 files changed, 467 insertions(+), 184 deletions(-)
src/link/MachO.zig+224-68| ... | ... | @@ -285,8 +285,7 @@ pub fn createEmpty( |
| 285 | 285 | }; |
| 286 | 286 | try self.d_sym.?.initMetadata(self); |
| 287 | 287 | } 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"); | |
| 290 | 289 | }, |
| 291 | 290 | .code_view => unreachable, |
| 292 | 291 | } |
| ... | ... | @@ -597,7 +596,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 597 | 596 | |
| 598 | 597 | try self.allocateSections(); |
| 599 | 598 | self.allocateSegments(); |
| 600 | self.allocateAtoms(); | |
| 601 | 599 | self.allocateSyntheticSymbols(); |
| 602 | 600 | try self.allocateLinkeditSegment(); |
| 603 | 601 | |
| ... | ... | @@ -615,7 +613,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 615 | 613 | if (!atom.flags.alive) continue; |
| 616 | 614 | const sect = &self.sections.items(.header)[atom.out_n_sect]; |
| 617 | 615 | 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; | |
| 619 | 618 | // TODO: we will resolve and write ZigObject's TLS data twice: |
| 620 | 619 | // once here, and once in writeAtoms |
| 621 | 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 | 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 | 639 | atom.resolveRelocs(self, code) catch |err| switch (err) { |
| 641 | 640 | error.ResolveFailed => has_resolve_error = true, |
| 642 | 641 | else => |e| { |
| ... | ... | @@ -2025,7 +2024,7 @@ pub fn sortSections(self: *MachO) !void { |
| 2025 | 2024 | |
| 2026 | 2025 | for (zo.symtab.items(.nlist)) |*sym| { |
| 2027 | 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 | } |
| 2031 | 2030 | |
| ... | ... | @@ -2232,11 +2231,11 @@ fn initSegments(self: *MachO) !void { |
| 2232 | 2231 | log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_size}); |
| 2233 | 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 | } |
| 2237 | 2236 | |
| 2238 | 2237 | // __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") }); | |
| 2240 | 2239 | |
| 2241 | 2240 | // Next, create segments required by sections |
| 2242 | 2241 | for (slice.items(.header)) |header| { |
| ... | ... | @@ -2248,15 +2247,57 @@ fn initSegments(self: *MachO) !void { |
| 2248 | 2247 | } |
| 2249 | 2248 | |
| 2250 | 2249 | // Add __LINKEDIT |
| 2251 | _ = try self.addSegment("__LINKEDIT", .{ .prot = getSegmentProt("__LINKEDIT") }); | |
| 2250 | self.linkedit_seg_index = try self.addSegment("__LINKEDIT", .{ .prot = getSegmentProt("__LINKEDIT") }); | |
| 2252 | 2251 | |
| 2253 | 2252 | // 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 | ); | |
| 2257 | 2262 | } |
| 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 | } | |
| 2260 | 2301 | |
| 2261 | 2302 | // Attach sections to segments |
| 2262 | 2303 | for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| { |
| ... | ... | @@ -2277,15 +2318,6 @@ fn initSegments(self: *MachO) !void { |
| 2277 | 2318 | segment.nsects += 1; |
| 2278 | 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 | } |
| 2290 | 2322 | |
| 2291 | 2323 | fn allocateSections(self: *MachO) !void { |
| ... | ... | @@ -2300,8 +2332,8 @@ fn allocateSections(self: *MachO) !void { |
| 2300 | 2332 | |
| 2301 | 2333 | const page_size = self.getPageSize(); |
| 2302 | 2334 | 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; | |
| 2305 | 2337 | } else slice.items(.header).len; |
| 2306 | 2338 | |
| 2307 | 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 | 2386 | /// We allocate segments in a separate step to also consider segments that have no sections. |
| 2355 | 2387 | fn allocateSegments(self: *MachO) void { |
| 2356 | 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| { | |
| 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; | |
| 2359 | 2391 | } else self.segments.items.len; |
| 2360 | 2392 | |
| 2361 | 2393 | var vmaddr: u64 = if (self.pagezero_seg_index) |index| |
| ... | ... | @@ -2392,23 +2424,6 @@ fn allocateSegments(self: *MachO) void { |
| 2392 | 2424 | } |
| 2393 | 2425 | } |
| 2394 | 2426 | |
| 2395 | pub 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 | ||
| 2412 | 2427 | fn allocateSyntheticSymbols(self: *MachO) void { |
| 2413 | 2428 | const text_seg = self.getTextSegment(); |
| 2414 | 2429 | |
| ... | ... | @@ -2603,7 +2618,7 @@ fn writeAtoms(self: *MachO) !void { |
| 2603 | 2618 | for (atoms.items) |atom_index| { |
| 2604 | 2619 | const atom = self.getAtom(atom_index).?; |
| 2605 | 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 | 2622 | const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; |
| 2608 | 2623 | try atom.getData(self, buffer[off..][0..atom_size]); |
| 2609 | 2624 | atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) { |
| ... | ... | @@ -2617,7 +2632,7 @@ fn writeAtoms(self: *MachO) !void { |
| 2617 | 2632 | |
| 2618 | 2633 | for (self.thunks.items) |thunk| { |
| 2619 | 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 | 2636 | const buffer = try gpa.alloc(u8, thunk.size()); |
| 2622 | 2637 | defer gpa.free(buffer); |
| 2623 | 2638 | var stream = std.io.fixedBufferStream(buffer); |
| ... | ... | @@ -2825,7 +2840,7 @@ pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 { |
| 2825 | 2840 | |
| 2826 | 2841 | if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| { |
| 2827 | 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 | 2844 | .length = dice.length, |
| 2830 | 2845 | .kind = dice.kind, |
| 2831 | 2846 | }); |
| ... | ... | @@ -3276,6 +3291,34 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 3276 | 3291 | return null; |
| 3277 | 3292 | } |
| 3278 | 3293 | |
| 3294 | fn 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 | ||
| 3279 | 3322 | fn allocatedSize(self: *MachO, start: u64) u64 { |
| 3280 | 3323 | if (start == 0) return 0; |
| 3281 | 3324 | var min_pos: u64 = std.math.maxInt(u64); |
| ... | ... | @@ -3290,7 +3333,7 @@ fn allocatedSize(self: *MachO, start: u64) u64 { |
| 3290 | 3333 | return min_pos - start; |
| 3291 | 3334 | } |
| 3292 | 3335 | |
| 3293 | fn allocatedVirtualSize(self: *MachO, start: u64) u64 { | |
| 3336 | fn allocatedSizeVirtual(self: *MachO, start: u64) u64 { | |
| 3294 | 3337 | if (start == 0) return 0; |
| 3295 | 3338 | var min_pos: u64 = std.math.maxInt(u64); |
| 3296 | 3339 | for (self.segments.items) |seg| { |
| ... | ... | @@ -3300,7 +3343,7 @@ fn allocatedVirtualSize(self: *MachO, start: u64) u64 { |
| 3300 | 3343 | return min_pos - start; |
| 3301 | 3344 | } |
| 3302 | 3345 | |
| 3303 | fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 { | |
| 3346 | pub fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 { | |
| 3304 | 3347 | var start: u64 = 0; |
| 3305 | 3348 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 3306 | 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 | 3351 | return start; |
| 3309 | 3352 | } |
| 3310 | 3353 | |
| 3354 | pub 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 | ||
| 3362 | pub 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 | 3368 | /// Like File.copyRangeAll but also ensures the source region is zeroed out after copy. |
| 3312 | 3369 | /// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader. |
| 3313 | 3370 | fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void { |
| 3314 | 3371 | 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); | |
| 3318 | 3373 | const size_u = math.cast(usize, size) orelse return error.Overflow; |
| 3319 | 3374 | const zeroes = try gpa.alloc(u8, size_u); |
| 3320 | 3375 | defer gpa.free(zeroes); |
| 3321 | 3376 | @memset(zeroes, 0); |
| 3322 | try file.pwriteAll(zeroes, old_offset); | |
| 3377 | try self.base.file.?.pwriteAll(zeroes, old_offset); | |
| 3323 | 3378 | } |
| 3324 | 3379 | |
| 3325 | 3380 | const InitMetadataOptions = struct { |
| ... | ... | @@ -3391,8 +3446,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3391 | 3446 | .prot = macho.PROT.READ | macho.PROT.WRITE, |
| 3392 | 3447 | }); |
| 3393 | 3448 | } |
| 3394 | } else { | |
| 3395 | @panic("TODO initMetadata when relocatable"); | |
| 3396 | 3449 | } |
| 3397 | 3450 | |
| 3398 | 3451 | const appendSect = struct { |
| ... | ... | @@ -3406,6 +3459,19 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3406 | 3459 | } |
| 3407 | 3460 | }.appendSect; |
| 3408 | 3461 | |
| 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 | 3476 | self.zig_text_sect_index = try self.addSection("__TEXT_ZIG", "__text_zig", .{ |
| 3411 | 3477 | .alignment = switch (self.getTarget().cpu.arch) { |
| ... | ... | @@ -3415,7 +3481,11 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3415 | 3481 | }, |
| 3416 | 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 | } |
| 3420 | 3490 | |
| 3421 | 3491 | if (!self.base.isRelocatable()) { |
| ... | ... | @@ -3427,33 +3497,52 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3427 | 3497 | |
| 3428 | 3498 | { |
| 3429 | 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 | } |
| 3432 | 3506 | |
| 3433 | 3507 | { |
| 3434 | 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 | } |
| 3437 | 3515 | |
| 3438 | 3516 | { |
| 3439 | 3517 | self.zig_bss_sect_index = try self.addSection("__BSS_ZIG", "__bss_zig", .{ |
| 3440 | 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 | } |
| 3445 | 3527 | |
| 3446 | 3528 | pub 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 | ||
| 3536 | fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void { | |
| 3447 | 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]; | |
| 3450 | 3538 | |
| 3451 | 3539 | if (needed_size > self.allocatedSize(sect.offset) and !sect.isZerofill()) { |
| 3452 | 3540 | const existing_size = sect.size; |
| 3453 | 3541 | sect.size = 0; |
| 3454 | 3542 | |
| 3455 | 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); | |
| 3457 | 3546 | |
| 3458 | 3547 | log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{ |
| 3459 | 3548 | sect.segName(), |
| ... | ... | @@ -3465,15 +3554,19 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void { |
| 3465 | 3554 | try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size); |
| 3466 | 3555 | |
| 3467 | 3556 | sect.offset = @intCast(new_offset); |
| 3468 | seg.fileoff = new_offset; | |
| 3469 | 3557 | } |
| 3470 | 3558 | |
| 3471 | 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 | 3565 | if (!sect.isZerofill()) { |
| 3473 | 3566 | seg.filesize = needed_size; |
| 3474 | 3567 | } |
| 3475 | 3568 | |
| 3476 | const mem_capacity = self.allocatedVirtualSize(seg.vmaddr); | |
| 3569 | const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr); | |
| 3477 | 3570 | if (needed_size > mem_capacity) { |
| 3478 | 3571 | var err = try self.addErrorWithNotes(2); |
| 3479 | 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 | 3580 | seg.vmsize = needed_size; |
| 3488 | 3581 | } |
| 3489 | 3582 | |
| 3583 | fn 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 | ||
| 3490 | 3613 | pub fn getTarget(self: MachO) std.Target { |
| 3491 | 3614 | return self.base.comp.root_mod.resolved_target.result; |
| 3492 | 3615 | } |
| ... | ... | @@ -3532,6 +3655,36 @@ inline fn requiresThunks(self: MachO) bool { |
| 3532 | 3655 | return self.getTarget().cpu.arch == .aarch64; |
| 3533 | 3656 | } |
| 3534 | 3657 | |
| 3658 | pub 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 | ||
| 3673 | pub 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 | ||
| 3535 | 3688 | pub fn addSegment(self: *MachO, name: []const u8, opts: struct { |
| 3536 | 3689 | vmaddr: u64 = 0, |
| 3537 | 3690 | vmsize: u64 = 0, |
| ... | ... | @@ -4033,10 +4186,13 @@ fn formatSections( |
| 4033 | 4186 | _ = unused_fmt_string; |
| 4034 | 4187 | const slice = self.sections.slice(); |
| 4035 | 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", .{ | |
| 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 | ); | |
| 4040 | 4196 | } |
| 4041 | 4197 | } |
| 4042 | 4198 |
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. | |
| 2 | 2 | value: u64 = 0, |
| 3 | 3 | |
| 4 | 4 | /// Name of this Atom. |
| ... | ... | @@ -84,6 +84,11 @@ pub fn getInputAddress(self: Atom, macho_file: *MachO) u64 { |
| 84 | 84 | return self.getInputSection(macho_file).addr + self.off; |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | pub 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 | ||
| 87 | 92 | pub fn getPriority(self: Atom, macho_file: *MachO) u64 { |
| 88 | 93 | const file = self.getFile(macho_file); |
| 89 | 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 | 119 | |
| 115 | 120 | pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { |
| 116 | 121 | const segname, const sectname, const flags = blk: { |
| 122 | const segname = sect.segName(); | |
| 123 | const sectname = sect.sectName(); | |
| 124 | ||
| 117 | 125 | if (sect.isCode()) break :blk .{ |
| 118 | 126 | "__TEXT", |
| 119 | sect.sectName(), | |
| 127 | sectname, | |
| 120 | 128 | macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 121 | 129 | }; |
| 122 | 130 | |
| ... | ... | @@ -127,34 +135,29 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { |
| 127 | 135 | => break :blk .{ "__TEXT", "__const", macho.S_REGULAR }, |
| 128 | 136 | |
| 129 | 137 | 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, | |
| 132 | 140 | }; |
| 133 | 141 | break :blk .{ "__TEXT", "__cstring", macho.S_CSTRING_LITERALS }; |
| 134 | 142 | }, |
| 135 | 143 | |
| 136 | 144 | macho.S_MOD_INIT_FUNC_POINTERS, |
| 137 | 145 | macho.S_MOD_TERM_FUNC_POINTERS, |
| 138 | => break :blk .{ "__DATA_CONST", sect.sectName(), sect.flags }, | |
| 139 | ||
| 140 | 146 | macho.S_LITERAL_POINTERS, |
| 147 | => break :blk .{ "__DATA_CONST", sectname, sect.flags }, | |
| 148 | ||
| 141 | 149 | macho.S_ZEROFILL, |
| 142 | 150 | macho.S_GB_ZEROFILL, |
| 143 | 151 | macho.S_THREAD_LOCAL_VARIABLES, |
| 144 | 152 | macho.S_THREAD_LOCAL_VARIABLE_POINTERS, |
| 145 | 153 | macho.S_THREAD_LOCAL_REGULAR, |
| 146 | 154 | macho.S_THREAD_LOCAL_ZEROFILL, |
| 147 | => break :blk .{ sect.segName(), sect.sectName(), sect.flags }, | |
| 155 | => break :blk .{ "__DATA", sectname, sect.flags }, | |
| 148 | 156 | |
| 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 }, | |
| 154 | 159 | |
| 155 | 160 | macho.S_REGULAR => { |
| 156 | const segname = sect.segName(); | |
| 157 | const sectname = sect.sectName(); | |
| 158 | 161 | if (mem.eql(u8, segname, "__DATA")) { |
| 159 | 162 | if (mem.eql(u8, sectname, "__const") or |
| 160 | 163 | mem.eql(u8, sectname, "__cfstring") or |
| ... | ... | @@ -168,7 +171,7 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { |
| 168 | 171 | break :blk .{ segname, sectname, sect.flags }; |
| 169 | 172 | }, |
| 170 | 173 | |
| 171 | else => break :blk .{ sect.segName(), sect.sectName(), sect.flags }, | |
| 174 | else => break :blk .{ segname, sectname, sect.flags }, | |
| 172 | 175 | } |
| 173 | 176 | }; |
| 174 | 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 | 192 | /// File offset relocation happens transparently, so it is not included in |
| 190 | 193 | /// this calculation. |
| 191 | 194 | pub 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); | |
| 194 | 200 | } |
| 195 | 201 | |
| 196 | 202 | pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { |
| 197 | 203 | // No need to keep a free list node for the last block. |
| 198 | 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 | 206 | const ideal_cap = MachO.padToIdeal(self.size); |
| 201 | 207 | if (cap <= ideal_cap) return false; |
| 202 | 208 | const surplus = cap - ideal_cap; |
| ... | ... | @@ -263,15 +269,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void { |
| 263 | 269 | atom_placement = last.atom_index; |
| 264 | 270 | break :blk new_start_vaddr; |
| 265 | 271 | } else { |
| 266 | break :blk sect.addr; | |
| 272 | break :blk 0; | |
| 267 | 273 | } |
| 268 | 274 | }; |
| 269 | 275 | |
| 270 | 276 | log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{ |
| 271 | 277 | self.atom_index, |
| 272 | 278 | self.getName(macho_file), |
| 273 | self.value, | |
| 274 | self.value + self.size, | |
| 279 | self.getAddress(macho_file), | |
| 280 | self.getAddress(macho_file) + self.size, | |
| 275 | 281 | }); |
| 276 | 282 | |
| 277 | 283 | const expand_section = if (atom_placement) |placement_index| |
| ... | ... | @@ -279,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void { |
| 279 | 285 | else |
| 280 | 286 | true; |
| 281 | 287 | if (expand_section) { |
| 282 | const needed_size = (self.value + self.size) - sect.addr; | |
| 288 | const needed_size = self.value + self.size; | |
| 283 | 289 | try macho_file.growSection(self.out_n_sect, needed_size); |
| 284 | 290 | last_atom_index.* = self.atom_index; |
| 285 | 291 | |
| ... | ... | @@ -544,7 +550,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { |
| 544 | 550 | const name = self.getName(macho_file); |
| 545 | 551 | const relocs = self.getRelocs(macho_file); |
| 546 | 552 | |
| 547 | relocs_log.debug("{x}: {s}", .{ self.value, name }); | |
| 553 | relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name }); | |
| 548 | 554 | |
| 549 | 555 | var has_error = false; |
| 550 | 556 | var stream = std.io.fixedBufferStream(buffer); |
| ... | ... | @@ -569,7 +575,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { |
| 569 | 575 | try macho_file.reportParseError2( |
| 570 | 576 | file.getIndex(), |
| 571 | 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 | 580 | has_error = true; |
| 575 | 581 | }, |
| ... | ... | @@ -604,7 +610,7 @@ fn resolveRelocInner( |
| 604 | 610 | const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; |
| 605 | 611 | const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect]; |
| 606 | 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 | 614 | const A = rel.addend + rel.getRelocAddend(cpu_arch); |
| 609 | 615 | const S: i64 = @intCast(rel.getTargetAddress(macho_file)); |
| 610 | 616 | const G: i64 = @intCast(rel.getGotTargetAddress(macho_file)); |
| ... | ... | @@ -690,7 +696,7 @@ fn resolveRelocInner( |
| 690 | 696 | .aarch64 => { |
| 691 | 697 | const disp: i28 = math.cast(i28, S + A - P) orelse blk: { |
| 692 | 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 | 700 | break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow; |
| 695 | 701 | }; |
| 696 | 702 | var inst = aarch64.Instruction{ |
| ... | ... | @@ -919,7 +925,7 @@ const x86_64 = struct { |
| 919 | 925 | var err = try macho_file.addErrorWithNotes(2); |
| 920 | 926 | try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{ |
| 921 | 927 | self.getName(macho_file), |
| 922 | self.value, | |
| 928 | self.getAddress(macho_file), | |
| 923 | 929 | rel.offset, |
| 924 | 930 | @tagName(rel.type), |
| 925 | 931 | }); |
| ... | ... | @@ -990,12 +996,11 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra |
| 990 | 996 | |
| 991 | 997 | const cpu_arch = macho_file.getTarget().cpu.arch; |
| 992 | 998 | const relocs = self.getRelocs(macho_file); |
| 993 | const sect = macho_file.sections.items(.header)[self.out_n_sect]; | |
| 994 | 999 | var stream = std.io.fixedBufferStream(code); |
| 995 | 1000 | |
| 996 | 1001 | for (relocs) |rel| { |
| 997 | 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 | 1004 | const r_symbolnum = r_symbolnum: { |
| 1000 | 1005 | const r_symbolnum: u32 = switch (rel.tag) { |
| 1001 | 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 | 1067 | .x86_64 => { |
| 1063 | 1068 | if (rel.meta.pcrel) { |
| 1064 | 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 | 1071 | } else { |
| 1067 | 1072 | addend += 4; |
| 1068 | 1073 | } |
| ... | ... | @@ -1143,10 +1148,10 @@ fn format2( |
| 1143 | 1148 | _ = unused_fmt_string; |
| 1144 | 1149 | const atom = ctx.atom; |
| 1145 | 1150 | 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, | |
| 1150 | 1155 | }); |
| 1151 | 1156 | if (!atom.flags.alive) try writer.writeAll(" : [*]"); |
| 1152 | 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 | 22 | |
| 23 | 23 | pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 { |
| 24 | 24 | return switch (rel.tag) { |
| 25 | .local => rel.getTargetAtom(macho_file).value, | |
| 25 | .local => rel.getTargetAtom(macho_file).getAddress(macho_file), | |
| 26 | 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 | 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 | 122 | return symbol.value; |
| 123 | 123 | } |
| 124 | 124 | |
| ... | ... | @@ -145,7 +145,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 { |
| 145 | 145 | const extra = symbol.getExtra(macho_file).?; |
| 146 | 146 | const atom = macho_file.getAtom(extra.objc_selrefs).?; |
| 147 | 147 | assert(atom.flags.alive); |
| 148 | return atom.value; | |
| 148 | return atom.getAddress(macho_file); | |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | 151 | pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 { |
src/link/MachO/UnwindInfo.zig+2-2| ... | ... | @@ -490,12 +490,12 @@ pub const Record = struct { |
| 490 | 490 | |
| 491 | 491 | pub fn getAtomAddress(rec: Record, macho_file: *MachO) u64 { |
| 492 | 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 | } |
| 495 | 495 | |
| 496 | 496 | pub fn getLsdaAddress(rec: Record, macho_file: *MachO) u64 { |
| 497 | 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 | } |
| 500 | 500 | |
| 501 | 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 | 154 | @memset(buffer, 0); |
| 155 | 155 | }, |
| 156 | 156 | else => { |
| 157 | const file_offset = sect.offset + atom.value - sect.addr; | |
| 157 | const file_offset = sect.offset + atom.value; | |
| 158 | 158 | const amt = try macho_file.base.file.?.preadAll(buffer, file_offset); |
| 159 | 159 | if (amt != buffer.len) return error.InputOutput; |
| 160 | 160 | }, |
| ... | ... | @@ -196,8 +196,10 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void { |
| 196 | 196 | const atom = macho_file.getAtom(atom_index).?; |
| 197 | 197 | break :blk nlist.n_value - atom.getInputAddress(macho_file); |
| 198 | 198 | } else nlist.n_value; |
| 199 | const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0; | |
| 199 | 200 | symbol.value = value; |
| 200 | 201 | symbol.atom = atom_index; |
| 202 | symbol.out_n_sect = out_n_sect; | |
| 201 | 203 | symbol.nlist_idx = nlist_idx; |
| 202 | 204 | symbol.file = self.index; |
| 203 | 205 | symbol.flags.weak = nlist.weakDef(); |
| ... | ... | @@ -715,7 +717,7 @@ fn updateDeclCode( |
| 715 | 717 | } else if (code.len < old_size) { |
| 716 | 718 | atom.shrink(macho_file); |
| 717 | 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 | 721 | sect.size = needed_size; |
| 720 | 722 | } |
| 721 | 723 | } else { |
| ... | ... | @@ -733,7 +735,7 @@ fn updateDeclCode( |
| 733 | 735 | } |
| 734 | 736 | |
| 735 | 737 | if (!sect.isZerofill()) { |
| 736 | const file_offset = sect.offset + atom.value - sect.addr; | |
| 738 | const file_offset = sect.offset + atom.value; | |
| 737 | 739 | try macho_file.base.file.?.pwriteAll(code, file_offset); |
| 738 | 740 | } |
| 739 | 741 | } |
| ... | ... | @@ -1036,7 +1038,7 @@ fn lowerConst( |
| 1036 | 1038 | nlist.n_value = 0; |
| 1037 | 1039 | |
| 1038 | 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 | 1042 | try macho_file.base.file.?.pwriteAll(code, file_offset); |
| 1041 | 1043 | |
| 1042 | 1044 | return .{ .ok = sym_index }; |
| ... | ... | @@ -1213,7 +1215,7 @@ fn updateLazySymbol( |
| 1213 | 1215 | } |
| 1214 | 1216 | |
| 1215 | 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 | 1219 | try macho_file.base.file.?.pwriteAll(code, file_offset); |
| 1218 | 1220 | } |
| 1219 | 1221 |
src/link/MachO/eh_frame.zig+4-4| ... | ... | @@ -416,7 +416,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void { |
| 416 | 416 | { |
| 417 | 417 | const offset = fde.out_offset + 8; |
| 418 | 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 | 420 | std.mem.writeInt( |
| 421 | 421 | i64, |
| 422 | 422 | buffer[offset..][0..8], |
| ... | ... | @@ -428,7 +428,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void { |
| 428 | 428 | if (fde.getLsdaAtom(macho_file)) |atom| { |
| 429 | 429 | const offset = fde.out_offset + fde.lsda_ptr_offset; |
| 430 | 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 | 432 | switch (fde.getCie(macho_file).lsda_size.?) { |
| 433 | 433 | .p32 => std.mem.writeInt( |
| 434 | 434 | i32, |
| ... | ... | @@ -501,7 +501,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho. |
| 501 | 501 | { |
| 502 | 502 | const offset = fde.out_offset + 8; |
| 503 | 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 | 505 | std.mem.writeInt( |
| 506 | 506 | i64, |
| 507 | 507 | code[offset..][0..8], |
| ... | ... | @@ -513,7 +513,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho. |
| 513 | 513 | if (fde.getLsdaAtom(macho_file)) |atom| { |
| 514 | 514 | const offset = fde.out_offset + fde.lsda_ptr_offset; |
| 515 | 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 | 517 | switch (fde.getCie(macho_file).lsda_size.?) { |
| 518 | 518 | .p32 => std.mem.writeInt( |
| 519 | 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 | 12 | |
| 13 | 13 | if (module_obj_path) |path| try positionals.append(.{ .path = path }); |
| 14 | 14 | |
| 15 | if (positionals.items.len == 1) { | |
| 15 | if (macho_file.getZigObject() == null and positionals.items.len == 1) { | |
| 16 | 16 | // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all |
| 17 | 17 | // debug info segments/sections (this is apparently by design by Apple), we copy |
| 18 | 18 | // the *only* input file over. |
| ... | ... | @@ -46,50 +46,23 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u |
| 46 | 46 | |
| 47 | 47 | try macho_file.addUndefinedGlobals(); |
| 48 | 48 | try macho_file.resolveSymbols(); |
| 49 | markExports(macho_file); | |
| 50 | claimUnresolved(macho_file); | |
| 49 | try markExports(macho_file); | |
| 50 | try claimUnresolved(macho_file); | |
| 51 | 51 | try initOutputSections(macho_file); |
| 52 | 52 | try macho_file.sortSections(); |
| 53 | 53 | try macho_file.addAtomsToSections(); |
| 54 | 54 | try calcSectionSizes(macho_file); |
| 55 | 55 | |
| 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); | |
| 71 | 59 | |
| 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); | |
| 93 | 66 | |
| 94 | 67 | state_log.debug("{}", .{macho_file.dumpState()}); |
| 95 | 68 | |
| ... | ... | @@ -109,8 +82,13 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u |
| 109 | 82 | try writeHeader(macho_file, ncmds, sizeofcmds); |
| 110 | 83 | } |
| 111 | 84 | |
| 112 | fn markExports(macho_file: *MachO) void { | |
| 113 | for (macho_file.objects.items) |index| { | |
| 85 | fn 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| { | |
| 114 | 92 | for (macho_file.getFile(index).?.getSymbols()) |sym_index| { |
| 115 | 93 | const sym = macho_file.getSymbol(sym_index); |
| 116 | 94 | const file = sym.getFile(macho_file) orelse continue; |
| ... | ... | @@ -122,13 +100,22 @@ fn markExports(macho_file: *MachO) void { |
| 122 | 100 | } |
| 123 | 101 | } |
| 124 | 102 | |
| 125 | fn claimUnresolved(macho_file: *MachO) void { | |
| 126 | for (macho_file.objects.items) |index| { | |
| 127 | const object = macho_file.getFile(index).?.object; | |
| 103 | fn 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).?; | |
| 128 | 111 | |
| 129 | for (object.symbols.items, 0..) |sym_index, i| { | |
| 112 | for (file.getSymbols(), 0..) |sym_index, i| { | |
| 130 | 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 | 119 | if (!nlist.ext()) continue; |
| 133 | 120 | if (!nlist.undf()) continue; |
| 134 | 121 | |
| ... | ... | @@ -203,6 +190,16 @@ fn calcSectionSizes(macho_file: *MachO) !void { |
| 203 | 190 | sect.@"align" = 3; |
| 204 | 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 | } |
| 207 | 204 | |
| 208 | 205 | fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void { |
| ... | ... | @@ -231,30 +228,66 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void { |
| 231 | 228 | sect.@"align" = 3; |
| 232 | 229 | } |
| 233 | 230 | |
| 234 | fn allocateSections(macho_file: *MachO) !u32 { | |
| 235 | var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64); | |
| 236 | var vmaddr: u64 = 0; | |
| 231 | fn allocateSections(macho_file: *MachO) !void { | |
| 237 | 232 | const slice = macho_file.sections.slice(); |
| 238 | 233 | |
| 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 | 239 | 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 | ||
| 248 | fn 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 | } | |
| 244 | 263 | |
| 264 | fn 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 | 274 | 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); | |
| 249 | 276 | } |
| 250 | 277 | } |
| 251 | 278 | |
| 279 | seg.vmsize = vmaddr - seg.vmaddr; | |
| 280 | seg.filesize = fileoff - seg.fileoff; | |
| 281 | } | |
| 282 | ||
| 283 | fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 { | |
| 284 | var fileoff = off; | |
| 285 | const slice = macho_file.sections.slice(); | |
| 252 | 286 | for (slice.items(.header)) |*header| { |
| 253 | 287 | if (header.nreloc == 0) continue; |
| 254 | 288 | header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info)); |
| 255 | 289 | fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info); |
| 256 | 290 | } |
| 257 | ||
| 258 | 291 | return fileoff; |
| 259 | 292 | } |
| 260 | 293 | |
| ... | ... | @@ -272,9 +305,10 @@ fn writeAtoms(macho_file: *MachO) !void { |
| 272 | 305 | const cpu_arch = macho_file.getTarget().cpu.arch; |
| 273 | 306 | const slice = macho_file.sections.slice(); |
| 274 | 307 | |
| 275 | for (slice.items(.header), slice.items(.atoms)) |header, atoms| { | |
| 308 | for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| { | |
| 276 | 309 | if (atoms.items.len == 0) continue; |
| 277 | 310 | if (header.isZerofill()) continue; |
| 311 | if (macho_file.isZigSection(@intCast(i))) continue; | |
| 278 | 312 | |
| 279 | 313 | const size = math.cast(usize, header.size) orelse return error.Overflow; |
| 280 | 314 | const code = try gpa.alloc(u8, size); |
| ... | ... | @@ -288,9 +322,9 @@ fn writeAtoms(macho_file: *MachO) !void { |
| 288 | 322 | for (atoms.items) |atom_index| { |
| 289 | 323 | const atom = macho_file.getAtom(atom_index).?; |
| 290 | 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 | 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 | 328 | try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs); |
| 295 | 329 | } |
| 296 | 330 | |
| ... | ... | @@ -302,6 +336,63 @@ fn writeAtoms(macho_file: *MachO) !void { |
| 302 | 336 | try macho_file.base.file.?.pwriteAll(code, header.offset); |
| 303 | 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 | } |
| 306 | 397 | |
| 307 | 398 | fn writeCompactUnwind(macho_file: *MachO) !void { |
| ... | ... | @@ -492,6 +583,7 @@ const assert = std.debug.assert; |
| 492 | 583 | const eh_frame = @import("eh_frame.zig"); |
| 493 | 584 | const link = @import("../../link.zig"); |
| 494 | 585 | const load_commands = @import("load_commands.zig"); |
| 586 | const log = std.log.scoped(.link); | |
| 495 | 587 | const macho = std.macho; |
| 496 | 588 | const math = std.math; |
| 497 | 589 | const mem = std.mem; |
| ... | ... | @@ -501,5 +593,6 @@ const trace = @import("../../tracy.zig").trace; |
| 501 | 593 | |
| 502 | 594 | const Atom = @import("Atom.zig"); |
| 503 | 595 | const Compilation = @import("../../Compilation.zig"); |
| 596 | const File = @import("file.zig").File; | |
| 504 | 597 | const MachO = @import("../MachO.zig"); |
| 505 | 598 | const 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 | 66 | if (atom.out_n_sect != target.out_n_sect) return false; |
| 67 | 67 | const target_atom = target.getAtom(macho_file).?; |
| 68 | 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 | 70 | const taddr: i64 = @intCast(rel.getTargetAddress(macho_file)); |
| 71 | 71 | _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false; |
| 72 | 72 | return true; |
| ... | ... | @@ -85,14 +85,19 @@ pub const Thunk = struct { |
| 85 | 85 | return thunk.symbols.keys().len * trampoline_size; |
| 86 | 86 | } |
| 87 | 87 | |
| 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; | |
| 90 | 95 | } |
| 91 | 96 | |
| 92 | 97 | pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void { |
| 93 | 98 | for (thunk.symbols.keys(), 0..) |sym_index, i| { |
| 94 | 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 | 101 | const taddr = sym.getAddress(.{}, macho_file); |
| 97 | 102 | const pages = try Relocation.calcNumberOfPages(saddr, taddr); |
| 98 | 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 | 27 | optimize: std.builtin.OptimizeMode = .Debug, |
| 28 | 28 | use_llvm: bool = true, |
| 29 | 29 | use_lld: bool = false, |
| 30 | strip: ?bool = null, | |
| 30 | 31 | }; |
| 31 | 32 | |
| 32 | 33 | pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step { |
| 33 | 34 | const target = opts.target.result.zigTriple(b.allocator) catch @panic("OOM"); |
| 34 | 35 | const optimize = @tagName(opts.optimize); |
| 35 | 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 | 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, | |
| 38 | 47 | }) catch @panic("OOM"); |
| 39 | 48 | return b.step(name, ""); |
| 40 | 49 | } |
| ... | ... | @@ -87,7 +96,7 @@ fn addCompileStep( |
| 87 | 96 | break :rsf b.addWriteFiles().add("a.zig", bytes); |
| 88 | 97 | }, |
| 89 | 98 | .pic = overlay.pic, |
| 90 | .strip = overlay.strip, | |
| 99 | .strip = if (base.strip) |s| s else overlay.strip, | |
| 91 | 100 | }, |
| 92 | 101 | .use_llvm = base.use_llvm, |
| 93 | 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 | 15 | .os_tag = .macos, |
| 16 | 16 | }); |
| 17 | 17 | |
| 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 | 23 | macho_step.dependOn(testDeadStrip(b, .{ .target = default_target })); |
| 19 | 24 | macho_step.dependOn(testEmptyObject(b, .{ .target = default_target })); |
| 20 | 25 | macho_step.dependOn(testEmptyZig(b, .{ .target = default_target })); |
| ... | ... | @@ -1234,7 +1239,14 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step { |
| 1234 | 1239 | const run = addRunArtifact(exe); |
| 1235 | 1240 | run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") }); |
| 1236 | 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 | 1250 | test_step.dependOn(&run.step); |
| 1239 | 1251 | |
| 1240 | 1252 | return test_step; |
| ... | ... | @@ -2307,6 +2319,7 @@ fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { |
| 2307 | 2319 | return link.addTestStep(b, "macho-" ++ prefix, opts); |
| 2308 | 2320 | } |
| 2309 | 2321 | |
| 2322 | const builtin = @import("builtin"); | |
| 2310 | 2323 | const addAsmSourceBytes = link.addAsmSourceBytes; |
| 2311 | 2324 | const addCSourceBytes = link.addCSourceBytes; |
| 2312 | 2325 | const addRunArtifact = link.addRunArtifact; |