authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-08 08:01:31+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-18 09:13:08+02:00
log9d5a900f4bf23fc3cf6e2848b9e9c370bba6c938
tree7409adf56a95d47ff6d7b86fde25584c75a6030f
parentf8cea21514ad18a2b47260783183d047f5a966c0

macho: migrate Object to self-ownership of atoms and symbols


2 files changed, 905 insertions(+), 387 deletions(-)

src/link/MachO.zig+8-10
......@@ -63,9 +63,6 @@ dso_handle_index: ?Symbol.Index = null,
6363objc_msg_send_index: ?Symbol.Index = null,
6464entry_index: ?Symbol.Index = null,
6565
66/// List of atoms that are either synthetic or map directly to the Zig source program.
67atoms: std.ArrayListUnmanaged(Atom) = .{},
68atoms_extra: std.ArrayListUnmanaged(u32) = .{},
6966thunks: std.ArrayListUnmanaged(Thunk) = .{},
7067
7168/// String interning table
......@@ -549,13 +546,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
549546 try dead_strip.gcAtoms(self);
550547 }
551548
552 self.checkDuplicates() catch |err| switch (err) {
553 error.HasDuplicates => return error.FlushFailure,
554 else => |e| {
555 try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{});
556 return e;
557 },
558 };
549 // TODO
550 // self.checkDuplicates() catch |err| switch (err) {
551 // error.HasDuplicates => return error.FlushFailure,
552 // else => |e| {
553 // try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{});
554 // return e;
555 // },
556 // };
559557
560558 self.markImportsAndExports();
561559 self.deadStripDylibs();
src/link/MachO/Object.zig+897-377
......@@ -12,7 +12,11 @@ symtab: std.MultiArrayList(Nlist) = .{},
1212strtab: std.ArrayListUnmanaged(u8) = .{},
1313
1414symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
15symbols_extra: std.ArrayListUnmanaged(u32) = .{},
16globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
1517atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
18atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
19atoms_extra: std.ArrayListUnmanaged(u32) = .{},
1620
1721platform: ?MachO.Platform = null,
1822compile_unit: ?CompileUnit = null,
......@@ -30,6 +34,7 @@ data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
3034alive: bool = true,
3135hidden: bool = false,
3236
37compact_unwind_ctx: CompactUnwindCtx = .{},
3338output_symtab_ctx: MachO.SymtabCtx = .{},
3439output_ar_state: Archive.ArState = .{},
3540
......@@ -51,7 +56,11 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
5156 self.symtab.deinit(allocator);
5257 self.strtab.deinit(allocator);
5358 self.symbols.deinit(allocator);
59 self.symbols_extra.deinit(allocator);
60 self.globals.deinit(allocator);
5461 self.atoms.deinit(allocator);
62 self.atoms_indexes.deinit(allocator);
63 self.atoms_extra.deinit(allocator);
5564 self.cies.deinit(allocator);
5665 self.fdes.deinit(allocator);
5766 self.eh_frame_data.deinit(allocator);
......@@ -70,6 +79,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
7079
7180 const gpa = macho_file.base.comp.gpa;
7281 const handle = macho_file.getFileHandle(self.file_handle);
82 const cpu_arch = macho_file.getTarget().cpu.arch;
83
84 // Atom at index 0 is reserved as null atom
85 try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) });
7386
7487 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
7588 {
......@@ -86,7 +99,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
8699 return error.InvalidCpuArch;
87100 },
88101 };
89 if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
102 if (cpu_arch != this_cpu_arch) {
90103 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
91104 return error.InvalidCpuArch;
92105 }
......@@ -198,20 +211,20 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
198211 mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan);
199212
200213 if (self.hasSubsections()) {
201 try self.initSubsections(nlists.items, macho_file);
214 try self.initSubsections(gpa, nlists.items);
202215 } else {
203 try self.initSections(nlists.items, macho_file);
216 try self.initSections(gpa, nlists.items);
204217 }
205218
206 try self.initCstringLiterals(macho_file);
207 try self.initFixedSizeLiterals(macho_file);
208 try self.initPointerLiterals(macho_file);
219 try self.initCstringLiterals(gpa, handle, macho_file);
220 try self.initFixedSizeLiterals(gpa, macho_file);
221 try self.initPointerLiterals(gpa, macho_file);
209222 try self.linkNlistToAtom(macho_file);
210223
211224 try self.sortAtoms(macho_file);
212 try self.initSymbols(macho_file);
213 try self.initSymbolStabs(nlists.items, macho_file);
214 try self.initRelocs(macho_file);
225 try self.initSymbols(gpa, macho_file);
226 try self.initSymbolStabs(gpa, nlists.items, macho_file);
227 try self.initRelocs(handle, cpu_arch, macho_file);
215228
216229 // Parse DWARF __TEXT,__eh_frame section
217230 if (self.eh_frame_sect_index) |index| {
......@@ -224,13 +237,13 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
224237 }
225238
226239 if (self.hasUnwindRecords() or self.hasEhFrameRecords()) {
227 try self.parseUnwindRecords(gpa, macho_file.getTarget().cpu.arch, macho_file);
240 try self.parseUnwindRecords(gpa, cpu_arch, macho_file);
228241 }
229242
230243 if (self.platform) |platform| {
231244 if (!macho_file.platform.eqlTarget(platform)) {
232245 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
233 platform.fmtTarget(macho_file.getTarget().cpu.arch),
246 platform.fmtTarget(cpu_arch),
234247 });
235248 return error.InvalidTarget;
236249 }
......@@ -247,7 +260,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
247260 }
248261
249262 for (self.atoms.items) |atom_index| {
250 const atom = macho_file.getAtom(atom_index).?;
263 const atom = self.getAtom(atom_index) orelse continue;
251264 const isec = atom.getInputSection(macho_file);
252265 if (mem.eql(u8, isec.sectName(), "__eh_frame") or
253266 mem.eql(u8, isec.sectName(), "__compact_unwind") or
......@@ -276,10 +289,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool {
276289 return sect.type() == macho.S_LITERAL_POINTERS;
277290}
278291
279fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
292fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
280293 const tracy = trace(@src());
281294 defer tracy.end();
282 const gpa = macho_file.base.comp.gpa;
283295 const slice = self.sections.slice();
284296 for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {
285297 if (isCstringLiteral(sect)) continue;
......@@ -294,17 +306,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
294306 } else nlists.len;
295307
296308 if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
297 const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
298 defer gpa.free(name);
309 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
310 defer allocator.free(name);
299311 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
300 const atom_index = try self.addAtom(.{
301 .name = try self.addString(gpa, name),
312 const atom_index = try self.addAtom(allocator, .{
313 .name = try self.addString(allocator, name),
302314 .n_sect = @intCast(n_sect),
303315 .off = 0,
304316 .size = size,
305317 .alignment = sect.@"align",
306 }, macho_file);
307 try subsections.append(gpa, .{
318 });
319 try self.atoms_indexes.append(allocator, atom_index);
320 try subsections.append(allocator, .{
308321 .atom = atom_index,
309322 .off = 0,
310323 });
......@@ -327,14 +340,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
327340 @min(@ctz(nlist.nlist.n_value), sect.@"align")
328341 else
329342 sect.@"align";
330 const atom_index = try self.addAtom(.{
343 const atom_index = try self.addAtom(allocator, .{
331344 .name = nlist.nlist.n_strx,
332345 .n_sect = @intCast(n_sect),
333346 .off = nlist.nlist.n_value - sect.addr,
334347 .size = size,
335348 .alignment = alignment,
336 }, macho_file);
337 try subsections.append(gpa, .{
349 });
350 try self.atoms_indexes.append(allocator, atom_index);
351 try subsections.append(allocator, .{
338352 .atom = atom_index,
339353 .off = nlist.nlist.n_value - sect.addr,
340354 });
......@@ -346,30 +360,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
346360 }
347361}
348362
349fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
363fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
350364 const tracy = trace(@src());
351365 defer tracy.end();
352 const gpa = macho_file.base.comp.gpa;
353366 const slice = self.sections.slice();
354367
355 try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);
368 try self.atoms.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
369 try self.atoms_indexes.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
356370
357371 for (slice.items(.header), 0..) |sect, n_sect| {
358372 if (isCstringLiteral(sect)) continue;
359373 if (isFixedSizeLiteral(sect)) continue;
360374 if (isPtrLiteral(sect)) continue;
361375
362 const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
363 defer gpa.free(name);
376 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
377 defer allocator.free(name);
364378
365 const atom_index = try self.addAtom(.{
366 .name = try self.addString(gpa, name),
379 const atom_index = try self.addAtom(allocator, .{
380 .name = try self.addString(allocator, name),
367381 .n_sect = @intCast(n_sect),
368382 .off = 0,
369383 .size = sect.size,
370384 .alignment = sect.@"align",
371 }, macho_file);
372 try slice.items(.subsections)[n_sect].append(gpa, .{ .atom = atom_index, .off = 0 });
385 });
386 try self.atoms_indexes.append(allocator, atom_index);
387 try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 });
373388
374389 const nlist_start = for (nlists, 0..) |nlist, i| {
375390 if (nlist.nlist.n_sect - 1 == n_sect) break i;
......@@ -398,21 +413,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
398413 }
399414}
400415
401fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
416fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, macho_file: *MachO) !void {
402417 const tracy = trace(@src());
403418 defer tracy.end();
404419
405 const gpa = macho_file.base.comp.gpa;
406420 const slice = self.sections.slice();
407421
408422 for (slice.items(.header), 0..) |sect, n_sect| {
409423 if (!isCstringLiteral(sect)) continue;
410424
411 const data = try self.getSectionData(@intCast(n_sect), macho_file);
412 defer gpa.free(data);
425 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
426 const data = try allocator.alloc(u8, sect_size);
427 defer allocator.free(data);
428 const amt = try file.preadAll(data, sect.offset + self.offset);
429 if (amt != data.len) return error.InputOutput;
413430
431 var count: u32 = 0;
414432 var start: u32 = 0;
415433 while (start < data.len) {
434 defer count += 1;
416435 var end = start;
417436 while (end < data.len - 1 and data[end] != 0) : (end += 1) {}
418437 if (data[end] != 0) {
......@@ -425,32 +444,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
425444 }
426445 end += 1;
427446
428 const atom_index = try self.addAtom(.{
429 .name = 0,
447 const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count});
448 defer allocator.free(name);
449 const name_str = try self.addString(allocator, name);
450
451 const atom_index = try self.addAtom(allocator, .{
452 .name = name_str,
430453 .n_sect = @intCast(n_sect),
431454 .off = start,
432455 .size = end - start,
433456 .alignment = sect.@"align",
434 }, macho_file);
435 try slice.items(.subsections)[n_sect].append(gpa, .{
457 });
458 try self.atoms_indexes.append(allocator, atom_index);
459 try slice.items(.subsections)[n_sect].append(allocator, .{
436460 .atom = atom_index,
437461 .off = start,
438462 });
439463
464 const atom = self.getAtom(atom_index).?;
465 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
466 self.symtab.set(nlist_index, .{
467 .nlist = .{
468 .n_strx = name_str,
469 .n_type = macho.N_SECT,
470 .n_sect = @intCast(atom.n_sect + 1),
471 .n_desc = 0,
472 .n_value = atom.getInputAddress(macho_file),
473 },
474 .size = atom.size,
475 .atom = atom_index,
476 });
477 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
478
440479 start = end;
441480 }
442481 }
443482}
444483
445fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
484fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
446485 const tracy = trace(@src());
447486 defer tracy.end();
448487
449 const gpa = macho_file.base.comp.gpa;
450488 const slice = self.sections.slice();
451489
452490 for (slice.items(.header), 0..) |sect, n_sect| {
453491 if (!isFixedSizeLiteral(sect)) continue;
492
454493 const rec_size: u8 = switch (sect.type()) {
455494 macho.S_4BYTE_LITERALS => 4,
456495 macho.S_8BYTE_LITERALS => 8,
......@@ -465,28 +504,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
465504 );
466505 return error.MalformedObject;
467506 }
507
468508 var pos: u32 = 0;
469 while (pos < sect.size) : (pos += rec_size) {
470 const atom_index = try self.addAtom(.{
471 .name = 0,
509 var count: u32 = 0;
510 while (pos < sect.size) : ({
511 pos += rec_size;
512 count += 1;
513 }) {
514 const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count});
515 defer allocator.free(name);
516 const name_str = try self.addString(allocator, name);
517
518 const atom_index = try self.addAtom(allocator, .{
519 .name = name_str,
472520 .n_sect = @intCast(n_sect),
473521 .off = pos,
474522 .size = rec_size,
475523 .alignment = sect.@"align",
476 }, macho_file);
477 try slice.items(.subsections)[n_sect].append(gpa, .{
524 });
525 try self.atoms_indexes.append(allocator, atom_index);
526 try slice.items(.subsections)[n_sect].append(allocator, .{
478527 .atom = atom_index,
479528 .off = pos,
480529 });
530
531 const atom = self.getAtom(atom_index).?;
532 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
533 self.symtab.set(nlist_index, .{
534 .nlist = .{
535 .n_strx = name_str,
536 .n_type = macho.N_SECT,
537 .n_sect = @intCast(atom.n_sect + 1),
538 .n_desc = 0,
539 .n_value = atom.getInputAddress(macho_file),
540 },
541 .size = atom.size,
542 .atom = atom_index,
543 });
544 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
481545 }
482546 }
483547}
484548
485fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
549fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
486550 const tracy = trace(@src());
487551 defer tracy.end();
488552
489 const gpa = macho_file.base.comp.gpa;
490553 const slice = self.sections.slice();
491554
492555 for (slice.items(.header), 0..) |sect, n_sect| {
......@@ -505,134 +568,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
505568
506569 for (0..num_ptrs) |i| {
507570 const pos: u32 = @as(u32, @intCast(i)) * rec_size;
508 const atom_index = try self.addAtom(.{
509 .name = 0,
571
572 const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i});
573 defer allocator.free(name);
574 const name_str = try self.addString(allocator, name);
575
576 const atom_index = try self.addAtom(allocator, .{
577 .name = name_str,
510578 .n_sect = @intCast(n_sect),
511579 .off = pos,
512580 .size = rec_size,
513581 .alignment = sect.@"align",
514 }, macho_file);
515 try slice.items(.subsections)[n_sect].append(gpa, .{
582 });
583 try self.atoms_indexes.append(allocator, atom_index);
584 try slice.items(.subsections)[n_sect].append(allocator, .{
516585 .atom = atom_index,
517586 .off = pos,
518587 });
588
589 const atom = self.getAtom(atom_index).?;
590 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
591 self.symtab.set(nlist_index, .{
592 .nlist = .{
593 .n_strx = name_str,
594 .n_type = macho.N_SECT,
595 .n_sect = @intCast(atom.n_sect + 1),
596 .n_desc = 0,
597 .n_value = atom.getInputAddress(macho_file),
598 },
599 .size = atom.size,
600 .atom = atom_index,
601 });
602 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
519603 }
520604 }
521605}
522606
523607pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
608 const tracy = trace(@src());
609 defer tracy.end();
610
524611 const gpa = macho_file.base.comp.gpa;
612 const file = macho_file.getFileHandle(self.file_handle);
525613
526614 var buffer = std.ArrayList(u8).init(gpa);
527615 defer buffer.deinit();
528616
617 var sections_data = std.AutoHashMap(u32, []const u8).init(gpa);
618 try sections_data.ensureTotalCapacity(@intCast(self.sections.items(.header).len));
619 defer {
620 var it = sections_data.iterator();
621 while (it.next()) |entry| {
622 gpa.free(entry.value_ptr.*);
623 }
624 sections_data.deinit();
625 }
626
529627 const slice = self.sections.slice();
530 for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| {
628 for (slice.items(.header), slice.items(.subsections)) |header, subs| {
531629 if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {
532 const data = try self.getSectionData(@intCast(n_sect), macho_file);
630 const sect_size = math.cast(usize, header.size) orelse return error.Overflow;
631 const data = try gpa.alloc(u8, sect_size);
533632 defer gpa.free(data);
633 const amt = try file.preadAll(data, header.offset + self.offset);
634 if (amt != data.len) return error.InputOutput;
534635
535636 for (subs.items) |sub| {
536 const atom = macho_file.getAtom(sub.atom).?;
637 const atom = self.getAtom(sub.atom).?;
537638 const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;
538639 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
539640 const atom_data = data[atom_off..][0..atom_size];
540641 const res = try lp.insert(gpa, header.type(), atom_data);
541642 if (!res.found_existing) {
542 res.atom.* = sub.atom;
643 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
644 } else {
645 const lp_sym = lp.getSymbol(res.index, macho_file);
646 const lp_atom = lp_sym.getAtom(macho_file).?;
647 lp_atom.alignment = @max(lp_atom.alignment, atom.alignment);
648 atom.flags.alive = false;
543649 }
544 atom.flags.literal_pool = true;
545 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
650 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
546651 }
547652 } else if (isPtrLiteral(header)) {
548653 for (subs.items) |sub| {
549 const atom = macho_file.getAtom(sub.atom).?;
654 const atom = self.getAtom(sub.atom).?;
550655 const relocs = atom.getRelocs(macho_file);
551656 assert(relocs.len == 1);
552657 const rel = relocs[0];
553658 const target = switch (rel.tag) {
554 .local => rel.target,
555 .@"extern" => rel.getTargetSymbol(macho_file).atom,
659 .local => rel.getTargetAtom(atom.*, macho_file),
660 .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
556661 };
557662 const addend = math.cast(u32, rel.addend) orelse return error.Overflow;
558 const target_atom = macho_file.getAtom(target).?;
559 const target_atom_size = math.cast(usize, target_atom.size) orelse return error.Overflow;
560 try buffer.ensureUnusedCapacity(target_atom_size);
561 buffer.resize(target_atom_size) catch unreachable;
562 try target_atom.getData(macho_file, buffer.items);
663 const target_size = math.cast(usize, target.size) orelse return error.Overflow;
664 try buffer.ensureUnusedCapacity(target_size);
665 buffer.resize(target_size) catch unreachable;
666 const gop = try sections_data.getOrPut(target.n_sect);
667 if (!gop.found_existing) {
668 const target_sect = slice.items(.header)[target.n_sect];
669 const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow;
670 const data = try gpa.alloc(u8, target_sect_size);
671 const amt = try file.preadAll(data, target_sect.offset + self.offset);
672 if (amt != data.len) return error.InputOutput;
673 gop.value_ptr.* = data;
674 }
675 const data = gop.value_ptr.*;
676 const target_off = math.cast(usize, target.off) orelse return error.Overflow;
677 @memcpy(buffer.items, data[target_off..][0..target_size]);
563678 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
564679 buffer.clearRetainingCapacity();
565680 if (!res.found_existing) {
566 res.atom.* = sub.atom;
681 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
682 } else {
683 const lp_sym = lp.getSymbol(res.index, macho_file);
684 const lp_atom = lp_sym.getAtom(macho_file).?;
685 lp_atom.alignment = @max(lp_atom.alignment, atom.alignment);
686 atom.flags.alive = false;
567687 }
568 atom.flags.literal_pool = true;
569 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
688 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
570689 }
571690 }
572691 }
573692}
574693
575pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
576 for (self.atoms.items) |atom_index| {
577 const atom = macho_file.getAtom(atom_index) orelse continue;
694pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
695 const tracy = trace(@src());
696 defer tracy.end();
697
698 for (self.getAtoms()) |atom_index| {
699 const atom = self.getAtom(atom_index) orelse continue;
578700 if (!atom.flags.alive) continue;
579 if (!atom.flags.relocs) continue;
580701
581702 const relocs = blk: {
582 const extra = atom.getExtra(macho_file).?;
703 const extra = atom.getExtra(macho_file);
583704 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
584705 break :blk relocs[extra.rel_index..][0..extra.rel_count];
585706 };
586 for (relocs) |*rel| switch (rel.tag) {
587 .local => {
588 const target = macho_file.getAtom(rel.target).?;
589 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
590 const lp_atom = lp.getAtom(lp_index, macho_file);
591 if (target.atom_index != lp_atom.atom_index) {
592 lp_atom.alignment = lp_atom.alignment.max(target.alignment);
593 target.flags.alive = false;
594 rel.target = lp_atom.atom_index;
595 }
596 }
597 },
598 .@"extern" => {
599 const target_sym = rel.getTargetSymbol(macho_file);
600 if (target_sym.getAtom(macho_file)) |target_atom| {
601 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
602 const lp_atom = lp.getAtom(lp_index, macho_file);
603 if (target_atom.atom_index != lp_atom.atom_index) {
604 lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
605 target_atom.flags.alive = false;
606 target_sym.atom = lp_atom.atom_index;
607 }
608 }
609 }
610 },
611 };
707 for (relocs) |*rel| {
708 if (rel.tag != .@"extern") continue;
709 const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
710 const file = target_sym_ref.getFile(macho_file) orelse continue;
711 if (file.getIndex() != self.index) continue;
712 const target_sym = target_sym_ref.getSymbol(macho_file).?;
713 const target_atom = target_sym.getAtom(macho_file) orelse continue;
714 const isec = target_atom.getInputSection(macho_file);
715 if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
716 const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
717 const lp_sym = lp.getSymbol(lp_index, macho_file);
718 const lp_atom_ref = lp_sym.atom_ref;
719 if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
720 target_sym.atom_ref = lp_atom_ref;
721 }
722 }
612723 }
613}
614
615const AddAtomArgs = struct {
616 name: u32,
617 n_sect: u8,
618 off: u64,
619 size: u64,
620 alignment: u32,
621};
622724
623fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {
624 const gpa = macho_file.base.comp.gpa;
625 const atom_index = try macho_file.addAtom();
626 const atom = macho_file.getAtom(atom_index).?;
627 atom.file = self.index;
628 atom.atom_index = atom_index;
629 atom.name = args.name;
630 atom.n_sect = args.n_sect;
631 atom.size = args.size;
632 atom.alignment = Atom.Alignment.fromLog2Units(args.alignment);
633 atom.off = args.off;
634 try self.atoms.append(gpa, atom_index);
635 return atom_index;
725 for (self.symbols.items) |*sym| {
726 const atom = sym.getAtom(macho_file) orelse continue;
727 const isec = atom.getInputSection(macho_file);
728 if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
729 const lp_index = atom.getExtra(macho_file).literal_pool_index;
730 const lp_sym = lp.getSymbol(lp_index, macho_file);
731 const lp_atom_ref = lp_sym.atom_ref;
732 if (atom.atom_index != lp_atom_ref.index or self.index != lp_atom_ref.file) {
733 sym.atom_ref = lp_atom_ref;
734 }
735 }
636736}
637737
638738pub fn findAtom(self: Object, addr: u64) ?Atom.Index {
......@@ -704,55 +804,59 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
704804 }
705805}
706806
707fn initSymbols(self: *Object, macho_file: *MachO) !void {
807fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
708808 const tracy = trace(@src());
709809 defer tracy.end();
710 const gpa = macho_file.base.comp.gpa;
810
711811 const slice = self.symtab.slice();
812 const nsyms = slice.items(.nlist).len;
712813
713 try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len);
814 try self.symbols.ensureTotalCapacityPrecise(allocator, nsyms);
815 try self.symbols_extra.ensureTotalCapacityPrecise(allocator, nsyms * @sizeOf(Symbol.Extra));
816 try self.globals.ensureTotalCapacityPrecise(allocator, nsyms);
817 self.globals.resize(allocator, nsyms) catch unreachable;
818 @memset(self.globals.items, 0);
714819
715820 for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {
716 if (nlist.ext()) {
717 const name = self.getString(nlist.n_strx);
718 const off = try macho_file.strings.insert(gpa, name);
719 const gop = try macho_file.getOrCreateGlobal(off);
720 self.symbols.addOneAssumeCapacity().* = gop.index;
721 if (nlist.undf() and nlist.weakRef()) {
722 macho_file.getSymbol(gop.index).flags.weak_ref = true;
723 }
724 continue;
725 }
726
727 const index = try macho_file.addSymbol();
728 self.symbols.appendAssumeCapacity(index);
729 const symbol = macho_file.getSymbol(index);
730 symbol.* = .{
731 .value = nlist.n_value,
732 .name = nlist.n_strx,
733 .nlist_idx = @intCast(i),
734 .atom = 0,
735 .file = self.index,
736 };
737
738 if (macho_file.getAtom(atom_index)) |atom| {
821 const index = self.addSymbolAssumeCapacity();
822 const symbol = &self.symbols.items[index];
823 symbol.value = nlist.n_value;
824 symbol.name = .{ .pos = nlist.n_strx, .len = @intCast(self.getNStrx(nlist.n_strx).len + 1) };
825 symbol.nlist_idx = @intCast(i);
826 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
827
828 if (self.getAtom(atom_index)) |atom| {
739829 assert(!nlist.abs());
740830 symbol.value -= atom.getInputAddress(macho_file);
741 symbol.atom = atom_index;
831 symbol.atom_ref = .{ .index = atom_index, .file = self.index };
742832 }
743833
834 symbol.flags.weak = nlist.weakDef();
744835 symbol.flags.abs = nlist.abs();
836 symbol.flags.tentative = nlist.tentative();
745837 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
838 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
839 symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.options.dylib and macho_file.options.namespace == .flat and !nlist.pext();
746840
747841 if (nlist.sect() and
748842 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
749843 {
750844 symbol.flags.tlv = true;
751845 }
846
847 if (nlist.ext()) {
848 if (nlist.undf()) {
849 symbol.flags.weak_ref = nlist.weakRef();
850 } else if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
851 symbol.visibility = .hidden;
852 } else {
853 symbol.visibility = .global;
854 }
855 }
752856 }
753857}
754858
755fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
859fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_file: *MachO) !void {
756860 const tracy = trace(@src());
757861 defer tracy.end();
758862
......@@ -778,14 +882,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
778882
779883 if (start == end) return;
780884
781 const gpa = macho_file.base.comp.gpa;
782885 const syms = self.symtab.items(.nlist);
783886 const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };
784887
785888 // We need to cache nlists by name so that we can properly resolve local N_GSYM stabs.
786889 // What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an
787890 // external or private external.
788 var addr_lookup = std.StringHashMap(u64).init(gpa);
891 var addr_lookup = std.StringHashMap(u64).init(allocator);
789892 defer addr_lookup.deinit();
790893 for (syms) |sym| {
791894 if (sym.sect() and (sym.ext() or sym.pext())) {
......@@ -834,29 +937,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
834937 return error.MalformedObject;
835938 },
836939 }
837 try sf.stabs.append(gpa, stab);
940 try sf.stabs.append(allocator, stab);
838941 }
839942
840 try self.stab_files.append(gpa, sf);
943 try self.stab_files.append(allocator, sf);
841944 }
842945}
843946
844947fn sortAtoms(self: *Object, macho_file: *MachO) !void {
845 const lessThanAtom = struct {
846 fn lessThanAtom(ctx: *MachO, lhs: Atom.Index, rhs: Atom.Index) bool {
847 return ctx.getAtom(lhs).?.getInputAddress(ctx) < ctx.getAtom(rhs).?.getInputAddress(ctx);
948 const Ctx = struct {
949 object: *Object,
950 mfile: *MachO,
951
952 fn lessThanAtom(ctx: @This(), lhs: Atom.Index, rhs: Atom.Index) bool {
953 return ctx.object.getAtom(lhs).?.getInputAddress(ctx.mfile) <
954 ctx.object.getAtom(rhs).?.getInputAddress(ctx.mfile);
848955 }
849 }.lessThanAtom;
850 mem.sort(Atom.Index, self.atoms.items, macho_file, lessThanAtom);
956 };
957 mem.sort(Atom.Index, self.atoms_indexes.items, Ctx{
958 .object = self,
959 .mfile = macho_file,
960 }, Ctx.lessThanAtom);
851961}
852962
853fn initRelocs(self: *Object, macho_file: *MachO) !void {
963fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
854964 const tracy = trace(@src());
855965 defer tracy.end();
856 const cpu_arch = macho_file.getTarget().cpu.arch;
857966 const slice = self.sections.slice();
858967
859 for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {
968 for (slice.items(.header), slice.items(.relocs)) |sect, *out| {
860969 if (sect.nreloc == 0) continue;
861970 // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit
862971 // debug symbol stabs in the relocatable. This made me curious why that is. For now,
......@@ -865,8 +974,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
865974 !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;
866975
867976 switch (cpu_arch) {
868 .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
869 .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
977 .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file),
978 .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file),
870979 else => unreachable,
871980 }
872981
......@@ -878,7 +987,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
878987
879988 var next_reloc: u32 = 0;
880989 for (subsections.items) |subsection| {
881 const atom = macho_file.getAtom(subsection.atom).?;
990 const atom = self.getAtom(subsection.atom).?;
882991 if (!atom.flags.alive) continue;
883992 if (next_reloc >= relocs.items.len) break;
884993 const end_addr = atom.off + atom.size;
......@@ -887,27 +996,22 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
887996 while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}
888997
889998 const rel_count = next_reloc - rel_index;
890 try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file);
891 atom.flags.relocs = true;
999 atom.addExtra(.{ .rel_index = @intCast(rel_index), .rel_count = @intCast(rel_count) }, macho_file);
8921000 }
8931001 }
8941002}
8951003
896fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
1004fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
8971005 const tracy = trace(@src());
8981006 defer tracy.end();
899 const gpa = macho_file.base.comp.gpa;
9001007 const nlists = self.symtab.items(.nlist);
9011008 const slice = self.sections.slice();
9021009 const sect = slice.items(.header)[sect_id];
9031010 const relocs = slice.items(.relocs)[sect_id];
9041011
905 // TODO: read into buffer directly
906 const data = try self.getSectionData(sect_id, macho_file);
907 defer gpa.free(data);
908
909 try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
910 self.eh_frame_data.appendSliceAssumeCapacity(data);
1012 try self.eh_frame_data.resize(allocator, sect.size);
1013 const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
1014 if (amt != self.eh_frame_data.items.len) return error.InputOutput;
9111015
9121016 // Check for non-personality relocs in FDEs and apply them
9131017 for (relocs.items, 0..) |rel, i| {
......@@ -939,12 +1043,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
9391043 var it = eh_frame.Iterator{ .data = self.eh_frame_data.items };
9401044 while (try it.next()) |rec| {
9411045 switch (rec.tag) {
942 .cie => try self.cies.append(gpa, .{
1046 .cie => try self.cies.append(allocator, .{
9431047 .offset = rec.offset,
9441048 .size = rec.size,
9451049 .file = self.index,
9461050 }),
947 .fde => try self.fdes.append(gpa, .{
1051 .fde => try self.fdes.append(allocator, .{
9481052 .offset = rec.offset,
9491053 .size = rec.size,
9501054 .cie = undefined,
......@@ -1205,8 +1309,7 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.
12051309 {}
12061310
12071311 const atom = rec.getAtom(macho_file);
1208 try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
1209 atom.flags.unwind = true;
1312 atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
12101313 }
12111314}
12121315
......@@ -1233,11 +1336,31 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
12331336
12341337 if (debug_info_index == null or debug_abbrev_index == null) return;
12351338
1236 const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file);
1339 const slice = self.sections.slice();
1340 const file = macho_file.getFileHandle(self.file_handle);
1341 const debug_info = blk: {
1342 const sect = slice.items(.header)[debug_info_index.?];
1343 const data = try gpa.alloc(u8, sect.size);
1344 const amt = try file.preadAll(data, sect.offset + self.offset);
1345 if (amt != data.len) return error.InputOutput;
1346 break :blk data;
1347 };
12371348 defer gpa.free(debug_info);
1238 const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file);
1349 const debug_abbrev = blk: {
1350 const sect = slice.items(.header)[debug_abbrev_index.?];
1351 const data = try gpa.alloc(u8, sect.size);
1352 const amt = try file.preadAll(data, sect.offset + self.offset);
1353 if (amt != data.len) return error.InputOutput;
1354 break :blk data;
1355 };
12391356 defer gpa.free(debug_abbrev);
1240 const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
1357 const debug_str = if (debug_str_index) |sid| blk: {
1358 const sect = slice.items(.header)[sid];
1359 const data = try gpa.alloc(u8, sect.size);
1360 const amt = try file.preadAll(data, sect.offset + self.offset);
1361 if (amt != data.len) return error.InputOutput;
1362 break :blk data;
1363 } else &[0]u8{};
12411364 defer gpa.free(debug_str);
12421365
12431366 self.compile_unit = self.findCompileUnit(.{
......@@ -1347,83 +1470,51 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {
13471470 const tracy = trace(@src());
13481471 defer tracy.end();
13491472
1350 for (self.symbols.items, 0..) |index, i| {
1351 const nlist_idx = @as(Symbol.Index, @intCast(i));
1352 const nlist = self.symtab.items(.nlist)[nlist_idx];
1353 const atom_index = self.symtab.items(.atom)[nlist_idx];
1473 const gpa = macho_file.base.allocator;
13541474
1475 for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
13551476 if (!nlist.ext()) continue;
1356 if (nlist.undf() and !nlist.tentative()) continue;
13571477 if (nlist.sect()) {
1358 const atom = macho_file.getAtom(atom_index).?;
1359 if (!atom.flags.alive) continue;
1478 const atom = self.getAtom(atom_index).?;
1479 if (!atom.alive.load(.seq_cst)) continue;
1480 }
1481
1482 const gop = try macho_file.resolver.getOrPut(gpa, .{
1483 .index = @intCast(i),
1484 .file = self.index,
1485 }, macho_file);
1486 if (!gop.found_existing) {
1487 gop.ref.* = .{ .index = 0, .file = 0 };
1488 }
1489 global.* = gop.index;
1490
1491 if (nlist.undf() and !nlist.tentative()) continue;
1492 if (gop.ref.getFile(macho_file) == null) {
1493 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
1494 continue;
13601495 }
13611496
1362 const symbol = macho_file.getSymbol(index);
13631497 if (self.asFile().getSymbolRank(.{
13641498 .archive = !self.alive,
13651499 .weak = nlist.weakDef(),
13661500 .tentative = nlist.tentative(),
1367 }) < symbol.getSymbolRank(macho_file)) {
1368 const value = if (nlist.sect()) blk: {
1369 const atom = macho_file.getAtom(atom_index).?;
1370 break :blk nlist.n_value - atom.getInputAddress(macho_file);
1371 } else nlist.n_value;
1372 symbol.value = value;
1373 symbol.atom = atom_index;
1374 symbol.nlist_idx = nlist_idx;
1375 symbol.file = self.index;
1376 symbol.flags.weak = nlist.weakDef();
1377 symbol.flags.abs = nlist.abs();
1378 symbol.flags.tentative = nlist.tentative();
1379 symbol.flags.weak_ref = false;
1380 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
1381 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
1382 // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
1383 symbol.flags.interposable = false;
1384
1385 if (nlist.sect() and
1386 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
1387 {
1388 symbol.flags.tlv = true;
1389 }
1390 }
1391
1392 // Regardless of who the winner is, we still merge symbol visibility here.
1393 if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
1394 if (symbol.visibility != .global) {
1395 symbol.visibility = .hidden;
1396 }
1397 } else {
1398 symbol.visibility = .global;
1501 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
1502 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
13991503 }
14001504 }
14011505}
14021506
1403pub fn resetGlobals(self: *Object, macho_file: *MachO) void {
1404 for (self.symbols.items, 0..) |sym_index, nlist_idx| {
1405 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
1406 const sym = macho_file.getSymbol(sym_index);
1407 const name = sym.name;
1408 const global = sym.flags.global;
1409 const weak_ref = sym.flags.weak_ref;
1410 sym.* = .{};
1411 sym.name = name;
1412 sym.flags.global = global;
1413 sym.flags.weak_ref = weak_ref;
1414 }
1415}
1416
14171507pub fn markLive(self: *Object, macho_file: *MachO) void {
14181508 const tracy = trace(@src());
14191509 defer tracy.end();
14201510
1421 for (self.symbols.items, 0..) |index, nlist_idx| {
1422 const nlist = self.symtab.items(.nlist)[nlist_idx];
1511 for (0..self.symbols.items.len) |i| {
1512 const nlist = self.symtab.items(.nlist)[i];
14231513 if (!nlist.ext()) continue;
14241514
1425 const sym = macho_file.getSymbol(index);
1426 const file = sym.getFile(macho_file) orelse continue;
1515 const ref = self.getSymbolRef(@intCast(i), macho_file);
1516 const file = ref.getFile(macho_file) orelse continue;
1517 const sym = ref.getSymbol(macho_file).?;
14271518 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
14281519 if (should_keep and file == .object and !file.object.alive) {
14291520 file.object.alive = true;
......@@ -1432,30 +1523,47 @@ pub fn markLive(self: *Object, macho_file: *MachO) void {
14321523 }
14331524}
14341525
1435pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {
1436 for (self.symbols.items, 0..) |index, nlist_idx| {
1437 const sym = macho_file.getSymbol(index);
1438 if (sym.visibility != .global) continue;
1439 const file = sym.getFile(macho_file) orelse continue;
1440 if (file.getIndex() == self.index) continue;
1526pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void {
1527 const tracy = trace(@src());
1528 defer tracy.end();
14411529
1442 const nlist = self.symtab.items(.nlist)[nlist_idx];
1443 if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
1444 const gop = try dupes.getOrPut(index);
1445 if (!gop.found_existing) {
1446 gop.value_ptr.* = .{};
1447 }
1448 try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
1530 for (self.symbols.items, 0..) |sym, i| {
1531 const ref = self.getSymbolRef(@intCast(i), macho_file);
1532 const global = ref.getSymbol(macho_file) orelse continue;
1533 if (global.visibility != .global) {
1534 global.visibility = sym.visibility;
1535 }
1536 if (sym.flags.weak_ref) {
1537 global.flags.weak_ref = true;
14491538 }
14501539 }
14511540}
14521541
1542// TODO
1543// pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {
1544// for (self.symbols.items, 0..) |index, nlist_idx| {
1545// const sym = macho_file.getSymbol(index);
1546// if (sym.visibility != .global) continue;
1547// const file = sym.getFile(macho_file) orelse continue;
1548// if (file.getIndex() == self.index) continue;
1549
1550// const nlist = self.symtab.items(.nlist)[nlist_idx];
1551// if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
1552// const gop = try dupes.getOrPut(index);
1553// if (!gop.found_existing) {
1554// gop.value_ptr.* = .{};
1555// }
1556// try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
1557// }
1558// }
1559// }
1560
14531561pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
14541562 const tracy = trace(@src());
14551563 defer tracy.end();
14561564
1457 for (self.atoms.items) |atom_index| {
1458 const atom = macho_file.getAtom(atom_index).?;
1565 for (self.getAtoms()) |atom_index| {
1566 const atom = self.getAtom(atom_index) orelse continue;
14591567 if (!atom.flags.alive) continue;
14601568 const sect = atom.getInputSection(macho_file);
14611569 if (sect.isZerofill()) continue;
......@@ -1480,38 +1588,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
14801588 defer tracy.end();
14811589 const gpa = macho_file.base.comp.gpa;
14821590
1483 for (self.symbols.items, 0..) |index, i| {
1484 const sym = macho_file.getSymbol(index);
1591 for (self.symbols.items, self.globals.items, 0..) |*sym, off, i| {
14851592 if (!sym.flags.tentative) continue;
1486 const sym_file = sym.getFile(macho_file).?;
1487 if (sym_file.getIndex() != self.index) continue;
1593 if (macho_file.resolver.get(off).?.file != self.index) continue;
14881594
14891595 const nlist_idx = @as(Symbol.Index, @intCast(i));
14901596 const nlist = &self.symtab.items(.nlist)[nlist_idx];
14911597 const nlist_atom = &self.symtab.items(.atom)[nlist_idx];
14921598
1493 const atom_index = try macho_file.addAtom();
1494 try self.atoms.append(gpa, atom_index);
1495
14961599 const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});
14971600 defer gpa.free(name);
1498 const atom = macho_file.getAtom(atom_index).?;
1499 atom.atom_index = atom_index;
1500 atom.name = try self.addString(gpa, name);
1501 atom.file = self.index;
1502 atom.size = nlist.n_value;
1503 atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f);
15041601
1602 const alignment = (nlist.n_desc >> 8) & 0x0f;
15051603 const n_sect = try self.addSection(gpa, "__DATA", "__common");
1604 const atom_index = try self.addAtom(gpa, .{
1605 .name = try self.addString(gpa, name),
1606 .n_sect = n_sect,
1607 .off = 0,
1608 .size = nlist.n_value,
1609 .alignment = alignment,
1610 });
1611 try self.atoms_indexes.append(gpa, atom_index);
1612
15061613 const sect = &self.sections.items(.header)[n_sect];
15071614 sect.flags = macho.S_ZEROFILL;
1508 sect.size = atom.size;
1509 sect.@"align" = atom.alignment.toLog2Units();
1510 atom.n_sect = n_sect;
1615 sect.size = nlist.n_value;
1616 sect.@"align" = alignment;
15111617
15121618 sym.value = 0;
1513 sym.atom = atom_index;
1514 sym.flags.global = true;
1619 sym.atom_ref = .{ .index = atom_index, .file = self.index };
15151620 sym.flags.weak = false;
15161621 sym.flags.weak_ref = false;
15171622 sym.flags.tentative = false;
......@@ -1525,6 +1630,56 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
15251630 }
15261631}
15271632
1633pub fn claimUnresolved(self: *Object, macho_file: *MachO) void {
1634 const tracy = trace(@src());
1635 defer tracy.end();
1636
1637 for (self.symbols.items, 0..) |*sym, i| {
1638 const nlist = self.symtab.items(.nlist)[i];
1639 if (!nlist.ext()) continue;
1640 if (!nlist.undf()) continue;
1641
1642 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
1643
1644 const is_import = switch (macho_file.options.undefined_treatment) {
1645 .@"error" => false,
1646 .warn, .suppress => nlist.weakRef(),
1647 .dynamic_lookup => true,
1648 };
1649 if (is_import) {
1650 sym.value = 0;
1651 sym.atom_ref = .{ .index = 0, .file = 0 };
1652 sym.flags.weak = false;
1653 sym.flags.weak_ref = nlist.weakRef();
1654 sym.flags.import = is_import;
1655 sym.visibility = .global;
1656
1657 const idx = self.globals.items[i];
1658 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
1659 }
1660 }
1661}
1662
1663pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void {
1664 const tracy = trace(@src());
1665 defer tracy.end();
1666
1667 for (self.symbols.items, self.symtab.items(.nlist), 0..) |*sym, nlist, i| {
1668 if (!nlist.ext()) continue;
1669 if (!nlist.undf()) continue;
1670 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
1671
1672 sym.value = 0;
1673 sym.atom_ref = .{ .index = 0, .file = 0 };
1674 sym.flags.weak_ref = nlist.weakRef();
1675 sym.flags.import = true;
1676 sym.visibility = .global;
1677
1678 const idx = self.globals.items[i];
1679 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
1680 }
1681}
1682
15281683fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 {
15291684 const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator)));
15301685 self.sections.set(n_sect, .{
......@@ -1646,19 +1801,22 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
16461801 const tracy = trace(@src());
16471802 defer tracy.end();
16481803
1649 for (self.symbols.items) |sym_index| {
1650 const sym = macho_file.getSymbol(sym_index);
1651 const file = sym.getFile(macho_file) orelse continue;
1804 const is_obj = macho_file.base.isObject();
1805
1806 for (self.symbols.items, 0..) |*sym, i| {
1807 const ref = self.getSymbolRef(@intCast(i), macho_file);
1808 const file = ref.getFile(macho_file) orelse continue;
16521809 if (file.getIndex() != self.index) continue;
16531810 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
16541811 if (sym.isSymbolStab(macho_file)) continue;
16551812 const name = sym.getName(macho_file);
1813 if (name.len == 0) continue;
16561814 // TODO in -r mode, we actually want to merge symbol names and emit only one
16571815 // work it out when emitting relocs
1658 if (name.len > 0 and
1659 (name[0] == 'L' or name[0] == 'l' or
1816 if ((name[0] == 'L' or name[0] == 'l' or
16601817 mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and
1661 !macho_file.base.isObject()) continue;
1818 !is_obj)
1819 continue;
16621820 sym.flags.output_symtab = true;
16631821 if (sym.isLocal()) {
16641822 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
......@@ -1693,9 +1851,9 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
16931851 self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
16941852 }
16951853
1696 for (self.symbols.items) |sym_index| {
1697 const sym = macho_file.getSymbol(sym_index);
1698 const file = sym.getFile(macho_file) orelse continue;
1854 for (self.symbols.items, 0..) |sym, i| {
1855 const ref = self.getSymbolRef(@intCast(i), macho_file);
1856 const file = ref.getFile(macho_file) orelse continue;
16991857 if (file.getIndex() != self.index) continue;
17001858 if (!sym.flags.output_symtab) continue;
17011859 if (macho_file.base.isObject()) {
......@@ -1732,28 +1890,204 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
17321890 }
17331891}
17341892
1735pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
1893pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
1894 const tracy = trace(@src());
1895 defer tracy.end();
1896
1897 const gpa = macho_file.base.comp.gpa;
1898 const headers = self.sections.items(.header);
1899 const sections_data = try gpa.alloc([]const u8, headers.len);
1900 defer {
1901 for (sections_data) |data| {
1902 gpa.free(data);
1903 }
1904 gpa.free(sections_data);
1905 }
1906 @memset(sections_data, &[0]u8{});
1907 const file = macho_file.getFileHandle(self.file_handle);
1908
1909 for (headers, 0..) |header, n_sect| {
1910 if (header.isZerofill()) continue;
1911 const data = try gpa.alloc(u8, header.size);
1912 const amt = try file.preadAll(data, header.offset + self.offset);
1913 if (amt != data.len) return error.InputOutput;
1914 sections_data[n_sect] = data;
1915 }
1916 for (self.getAtoms()) |atom_index| {
1917 const atom = self.getAtom(atom_index) orelse continue;
1918 if (!atom.flags.alive) continue;
1919 const sect = atom.getInputSection(macho_file);
1920 if (sect.isZerofill()) continue;
1921 const off = atom.value;
1922 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1923 const data = sections_data[atom.n_sect];
1924 @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]);
1925 try atom.resolveRelocs(macho_file, buffer[off..][0..atom.size]);
1926 }
1927}
1928
1929pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
1930 const tracy = trace(@src());
1931 defer tracy.end();
1932
1933 const gpa = macho_file.base.allocator;
1934 const headers = self.sections.items(.header);
1935 const sections_data = try gpa.alloc([]const u8, headers.len);
1936 defer {
1937 for (sections_data) |data| {
1938 gpa.free(data);
1939 }
1940 gpa.free(sections_data);
1941 }
1942 @memset(sections_data, &[0]u8{});
1943 const file = macho_file.getFileHandle(self.file_handle);
1944
1945 for (headers, 0..) |header, n_sect| {
1946 if (header.isZerofill()) continue;
1947 const data = try gpa.alloc(u8, header.size);
1948 const amt = try file.preadAll(data, header.offset + self.offset);
1949 if (amt != data.len) return error.InputOutput;
1950 sections_data[n_sect] = data;
1951 }
1952 for (self.getAtoms()) |atom_index| {
1953 const atom = self.getAtom(atom_index) orelse continue;
1954 if (!atom.flags.alive) continue;
1955 const sect = atom.getInputSection(macho_file);
1956 if (sect.isZerofill()) continue;
1957 const off = atom.value;
1958 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1959 const data = sections_data[atom.n_sect];
1960 @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]);
1961 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
1962 const extra = atom.getExtra(macho_file);
1963 try atom.writeRelocs(macho_file, buffer[off..][0..atom.size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
1964 }
1965}
1966
1967pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void {
1968 const tracy = trace(@src());
1969 defer tracy.end();
1970
1971 const ctx = &self.compact_unwind_ctx;
1972
1973 for (self.unwind_records_indexes.items) |irec| {
1974 const rec = self.getUnwindRecord(irec);
1975 if (!rec.alive) continue;
1976
1977 ctx.rec_count += 1;
1978 ctx.reloc_count += 1;
1979 if (rec.getPersonality(macho_file)) |_| {
1980 ctx.reloc_count += 1;
1981 }
1982 if (rec.getLsdaAtom(macho_file)) |_| {
1983 ctx.reloc_count += 1;
1984 }
1985 }
1986}
1987
1988pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
17361989 const tracy = trace(@src());
17371990 defer tracy.end();
17381991
1739 for (self.symbols.items) |sym_index| {
1740 const sym = macho_file.getSymbol(sym_index);
1741 const file = sym.getFile(macho_file) orelse continue;
1992 const addReloc = struct {
1993 fn addReloc(offset: u32, cpu_arch: std.Target.Cpu.Arch) !macho.relocation_info {
1994 return .{
1995 .r_address = math.cast(i32, offset) orelse return error.Overflow,
1996 .r_symbolnum = 0,
1997 .r_pcrel = 0,
1998 .r_length = 3,
1999 .r_extern = 0,
2000 .r_type = switch (cpu_arch) {
2001 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2002 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2003 else => unreachable,
2004 },
2005 };
2006 }
2007 }.addReloc;
2008
2009 const nsect = macho_file.unwind_info_sect_index.?;
2010 const buffer = macho_file.sections.items(.out)[nsect].items;
2011 const relocs = macho_file.sections.items(.relocs)[nsect].items;
2012
2013 var rec_index: u32 = self.compact_unwind_ctx.rec_index;
2014 var reloc_index: u32 = self.compact_unwind_ctx.reloc_index;
2015
2016 for (self.unwind_records_indexes.items) |irec| {
2017 const rec = self.getUnwindRecord(irec);
2018 if (!rec.alive) continue;
2019
2020 var out: macho.compact_unwind_entry = .{
2021 .rangeStart = 0,
2022 .rangeLength = rec.length,
2023 .compactUnwindEncoding = rec.enc.enc,
2024 .personalityFunction = 0,
2025 .lsda = 0,
2026 };
2027 defer rec_index += 1;
2028
2029 const offset = rec_index * @sizeOf(macho.compact_unwind_entry);
2030
2031 {
2032 // Function address
2033 const atom = rec.getAtom(macho_file);
2034 const addr = rec.getAtomAddress(macho_file);
2035 out.rangeStart = addr;
2036 var reloc = try addReloc(offset, macho_file.options.cpu_arch.?);
2037 reloc.r_symbolnum = atom.out_n_sect + 1;
2038 relocs[reloc_index] = reloc;
2039 reloc_index += 1;
2040 }
2041
2042 // Personality function
2043 if (rec.getPersonality(macho_file)) |sym| {
2044 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
2045 var reloc = try addReloc(offset + 16, macho_file.options.cpu_arch.?);
2046 reloc.r_symbolnum = r_symbolnum;
2047 reloc.r_extern = 1;
2048 relocs[reloc_index] = reloc;
2049 reloc_index += 1;
2050 }
2051
2052 // LSDA address
2053 if (rec.getLsdaAtom(macho_file)) |atom| {
2054 const addr = rec.getLsdaAddress(macho_file);
2055 out.lsda = addr;
2056 var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?);
2057 reloc.r_symbolnum = atom.out_n_sect + 1;
2058 relocs[reloc_index] = reloc;
2059 reloc_index += 1;
2060 }
2061
2062 @memcpy(buffer[offset..][0..@sizeOf(macho.compact_unwind_entry)], mem.asBytes(&out));
2063 }
2064}
2065
2066pub fn writeSymtab(self: Object, macho_file: *MachO) void {
2067 const tracy = trace(@src());
2068 defer tracy.end();
2069
2070 var n_strx = self.output_symtab_ctx.stroff;
2071 for (self.symbols.items, 0..) |sym, i| {
2072 const ref = self.getSymbolRef(@intCast(i), macho_file);
2073 const file = ref.getFile(macho_file) orelse continue;
17422074 if (file.getIndex() != self.index) continue;
17432075 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
1744 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1745 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
1746 ctx.strtab.appendAssumeCapacity(0);
1747 const out_sym = &ctx.symtab.items[idx];
2076 const out_sym = &macho_file.symtab.items[idx];
17482077 out_sym.n_strx = n_strx;
17492078 sym.setOutputSym(macho_file, out_sym);
2079 const name = sym.getName(macho_file);
2080 @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name);
2081 n_strx += @intCast(name.len);
2082 macho_file.strtab.items[n_strx] = 0;
2083 n_strx += 1;
17502084 }
17512085
17522086 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1753 try self.writeStabs(macho_file, ctx);
2087 try self.writeStabs(n_strx, macho_file);
17542088}
17552089
1756pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
2090pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
17572091 const writeFuncStab = struct {
17582092 inline fn writeFuncStab(
17592093 n_strx: u32,
......@@ -1761,7 +2095,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
17612095 n_value: u64,
17622096 size: u64,
17632097 index: u32,
1764 context: anytype,
2098 context: *MachO,
17652099 ) void {
17662100 context.symtab.items[index] = .{
17672101 .n_strx = 0,
......@@ -1795,6 +2129,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
17952129 }.writeFuncStab;
17962130
17972131 var index = self.output_symtab_ctx.istab;
2132 var n_strx = stroff;
17982133
17992134 if (self.compile_unit) |cu| {
18002135 const comp_dir = cu.getCompDir(self);
......@@ -1802,10 +2137,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18022137
18032138 // Open scope
18042139 // N_SO comp_dir
1805 var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1806 ctx.strtab.appendSliceAssumeCapacity(comp_dir);
1807 ctx.strtab.appendAssumeCapacity(0);
1808 ctx.symtab.items[index] = .{
2140 macho_file.symtab.items[index] = .{
18092141 .n_strx = n_strx,
18102142 .n_type = macho.N_SO,
18112143 .n_sect = 0,
......@@ -1813,11 +2145,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18132145 .n_value = 0,
18142146 };
18152147 index += 1;
2148 @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
2149 n_strx += @intCast(comp_dir.len);
2150 macho_file.strtab.items[n_strx] = 0;
2151 n_strx += 1;
18162152 // N_SO tu_name
1817 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1818 ctx.strtab.appendSliceAssumeCapacity(tu_name);
1819 ctx.strtab.appendAssumeCapacity(0);
1820 ctx.symtab.items[index] = .{
2153 macho_file.symtab.items[index] = .{
18212154 .n_strx = n_strx,
18222155 .n_type = macho.N_SO,
18232156 .n_sect = 0,
......@@ -1825,19 +2158,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18252158 .n_value = 0,
18262159 };
18272160 index += 1;
2161 @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name);
2162 n_strx += @intCast(tu_name.len);
2163 macho_file.strtab.items[n_strx] = 0;
2164 n_strx += 1;
18282165 // N_OSO path
1829 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1830 if (self.in_archive) |ar| {
1831 ctx.strtab.appendSliceAssumeCapacity(ar.path);
1832 ctx.strtab.appendAssumeCapacity('(');
1833 ctx.strtab.appendSliceAssumeCapacity(self.path);
1834 ctx.strtab.appendAssumeCapacity(')');
1835 ctx.strtab.appendAssumeCapacity(0);
1836 } else {
1837 ctx.strtab.appendSliceAssumeCapacity(self.path);
1838 ctx.strtab.appendAssumeCapacity(0);
1839 }
1840 ctx.symtab.items[index] = .{
2166 macho_file.symtab.items[index] = .{
18412167 .n_strx = n_strx,
18422168 .n_type = macho.N_OSO,
18432169 .n_sect = 0,
......@@ -1845,10 +2171,27 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18452171 .n_value = self.mtime,
18462172 };
18472173 index += 1;
2174 if (self.in_archive) |ar| {
2175 @memcpy(macho_file.strtab.items[n_strx..][0..ar.path.len], ar.path);
2176 n_strx += @intCast(ar.path.len);
2177 macho_file.strtab.items[n_strx] = '(';
2178 n_strx += 1;
2179 @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path);
2180 n_strx += @intCast(self.path.len);
2181 macho_file.strtab.items[n_strx] = ')';
2182 n_strx += 1;
2183 macho_file.strtab.items[n_strx] = 0;
2184 n_strx += 1;
2185 } else {
2186 @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path);
2187 n_strx += @intCast(self.path.len);
2188 macho_file.strtab.items[n_strx] = 0;
2189 n_strx += 1;
2190 }
18482191
1849 for (self.symbols.items) |sym_index| {
1850 const sym = macho_file.getSymbol(sym_index);
1851 const file = sym.getFile(macho_file) orelse continue;
2192 for (self.symbols.items, 0..) |sym, i| {
2193 const ref = self.getSymbolRef(@intCast(i), macho_file);
2194 const file = ref.getFile(macho_file) orelse continue;
18522195 if (file.getIndex() != self.index) continue;
18532196 if (!sym.flags.output_symtab) continue;
18542197 if (macho_file.base.isObject()) {
......@@ -1858,17 +2201,17 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18582201 const sect = macho_file.sections.items(.header)[sym.out_n_sect];
18592202 const sym_n_strx = n_strx: {
18602203 const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
1861 const osym = ctx.symtab.items[symtab_index];
2204 const osym = macho_file.symtab.items[symtab_index];
18622205 break :n_strx osym.n_strx;
18632206 };
18642207 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
18652208 const sym_n_value = sym.getAddress(.{}, macho_file);
18662209 const sym_size = sym.getSize(macho_file);
18672210 if (sect.isCode()) {
1868 writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx);
2211 writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file);
18692212 index += 4;
18702213 } else if (sym.visibility == .global) {
1871 ctx.symtab.items[index] = .{
2214 macho_file.symtab.items[index] = .{
18722215 .n_strx = sym_n_strx,
18732216 .n_type = macho.N_GSYM,
18742217 .n_sect = sym_n_sect,
......@@ -1877,7 +2220,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18772220 };
18782221 index += 1;
18792222 } else {
1880 ctx.symtab.items[index] = .{
2223 macho_file.symtab.items[index] = .{
18812224 .n_strx = sym_n_strx,
18822225 .n_type = macho.N_STSYM,
18832226 .n_sect = sym_n_sect,
......@@ -1890,7 +2233,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18902233
18912234 // Close scope
18922235 // N_SO
1893 ctx.symtab.items[index] = .{
2236 macho_file.symtab.items[index] = .{
18942237 .n_strx = 0,
18952238 .n_type = macho.N_SO,
18962239 .n_sect = 0,
......@@ -1901,12 +2244,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19012244 assert(self.hasSymbolStabs());
19022245
19032246 for (self.stab_files.items) |sf| {
2247 const comp_dir = sf.getCombDir(self);
2248 const tu_name = sf.getTuName(self);
2249 const oso_path = sf.getOsoPath(self);
2250
19042251 // Open scope
19052252 // N_SO comp_dir
1906 var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1907 ctx.strtab.appendSliceAssumeCapacity(sf.getCompDir(self));
1908 ctx.strtab.appendAssumeCapacity(0);
1909 ctx.symtab.items[index] = .{
2253 macho_file.symtab.items[index] = .{
19102254 .n_strx = n_strx,
19112255 .n_type = macho.N_SO,
19122256 .n_sect = 0,
......@@ -1914,11 +2258,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19142258 .n_value = 0,
19152259 };
19162260 index += 1;
2261 @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
2262 n_strx += @intCast(comp_dir.len);
2263 macho_file.strtab.items[n_strx] = 0;
2264 n_strx += 1;
19172265 // N_SO tu_name
1918 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1919 ctx.strtab.appendSliceAssumeCapacity(sf.getTuName(self));
1920 ctx.strtab.appendAssumeCapacity(0);
1921 ctx.symtab.items[index] = .{
2266 macho_file.symtab.items[index] = .{
19222267 .n_strx = n_strx,
19232268 .n_type = macho.N_SO,
19242269 .n_sect = 0,
......@@ -1926,11 +2271,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19262271 .n_value = 0,
19272272 };
19282273 index += 1;
2274 @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name);
2275 n_strx += @intCast(tu_name.len);
2276 macho_file.strtab.items[n_strx] = 0;
2277 n_strx += 1;
19292278 // N_OSO path
1930 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1931 ctx.strtab.appendSliceAssumeCapacity(sf.getOsoPath(self));
1932 ctx.strtab.appendAssumeCapacity(0);
1933 ctx.symtab.items[index] = .{
2279 macho_file.symtab.items[index] = .{
19342280 .n_strx = n_strx,
19352281 .n_type = macho.N_OSO,
19362282 .n_sect = 0,
......@@ -1938,6 +2284,10 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19382284 .n_value = sf.getOsoModTime(self),
19392285 };
19402286 index += 1;
2287 @memcpy(macho_file.strtab.items[n_strx..][0..oso_path.len], oso_path);
2288 n_strx += @intCast(oso_path.len);
2289 macho_file.strtab.items[n_strx] = 0;
2290 n_strx += 1;
19412291
19422292 for (sf.stabs.items) |stab| {
19432293 const sym = stab.getSymbol(macho_file) orelse continue;
......@@ -1946,17 +2296,17 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19462296 if (!sym.flags.output_symtab) continue;
19472297 const sym_n_strx = n_strx: {
19482298 const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
1949 const osym = ctx.symtab.items[symtab_index];
2299 const osym = macho_file.symtab.items[symtab_index];
19502300 break :n_strx osym.n_strx;
19512301 };
19522302 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
19532303 const sym_n_value = sym.getAddress(.{}, macho_file);
19542304 const sym_size = sym.getSize(macho_file);
19552305 if (stab.is_func) {
1956 writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx);
2306 writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file);
19572307 index += 4;
19582308 } else if (sym.visibility == .global) {
1959 ctx.symtab.items[index] = .{
2309 macho_file.symtab.items[index] = .{
19602310 .n_strx = sym_n_strx,
19612311 .n_type = macho.N_GSYM,
19622312 .n_sect = sym_n_sect,
......@@ -1965,7 +2315,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19652315 };
19662316 index += 1;
19672317 } else {
1968 ctx.symtab.items[index] = .{
2318 macho_file.symtab.items[index] = .{
19692319 .n_strx = sym_n_strx,
19702320 .n_type = macho.N_STSYM,
19712321 .n_sect = sym_n_sect,
......@@ -1978,7 +2328,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19782328
19792329 // Close scope
19802330 // N_SO
1981 ctx.symtab.items[index] = .{
2331 macho_file.symtab.items[index] = .{
19822332 .n_strx = 0,
19832333 .n_type = macho.N_SO,
19842334 .n_sect = 0,
......@@ -1990,36 +2340,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19902340 }
19912341}
19922342
1993fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 {
1994 const gpa = macho_file.base.comp.gpa;
1995 const slice = self.sections.slice();
1996 assert(index < slice.items(.header).len);
1997 const sect = slice.items(.header)[index];
1998 const handle = macho_file.getFileHandle(self.file_handle);
1999 const size = math.cast(usize, sect.size) orelse return error.Overflow;
2000 const buffer = try gpa.alloc(u8, size);
2001 errdefer gpa.free(buffer);
2002 const amt = try handle.preadAll(buffer, sect.offset + self.offset);
2003 if (amt != buffer.len) return error.InputOutput;
2004 return buffer;
2005}
2006
2007pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
2008 assert(buffer.len == atom.size);
2009 const slice = self.sections.slice();
2010 const handle = macho_file.getFileHandle(self.file_handle);
2011 const sect = slice.items(.header)[atom.n_sect];
2012 const amt = try handle.preadAll(buffer, sect.offset + self.offset + atom.off);
2013 if (amt != buffer.len) return error.InputOutput;
2014}
2015
2016pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
2017 if (!atom.flags.relocs) return &[0]Relocation{};
2018 const extra = atom.getExtra(macho_file).?;
2019 const relocs = self.sections.items(.relocs)[atom.n_sect];
2020 return relocs.items[extra.rel_index..][0..extra.rel_count];
2021}
2022
20232343fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
20242344 const off: u32 = @intCast(self.strtab.items.len);
20252345 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
......@@ -2073,6 +2393,143 @@ pub fn asFile(self: *Object) File {
20732393 return .{ .object = self };
20742394}
20752395
2396const AddAtomArgs = struct {
2397 name: MachO.String,
2398 n_sect: u8,
2399 off: u64,
2400 size: u64,
2401 alignment: u32,
2402};
2403
2404fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
2405 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
2406 const atom = try self.atoms.addOne(allocator);
2407 atom.* = .{
2408 .file = self.index,
2409 .atom_index = atom_index,
2410 .name = args.name,
2411 .n_sect = args.n_sect,
2412 .size = args.size,
2413 .off = args.off,
2414 .extra = try self.addAtomExtra(allocator, .{}),
2415 .alignment = args.alignment,
2416 };
2417 return atom_index;
2418}
2419
2420pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom {
2421 if (atom_index == 0) return null;
2422 assert(atom_index < self.atoms.items.len);
2423 return &self.atoms.items[atom_index];
2424}
2425
2426pub fn getAtoms(self: *Object) []const Atom.Index {
2427 return self.atoms_indexes.items;
2428}
2429
2430fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
2431 const fields = @typeInfo(Atom.Extra).Struct.fields;
2432 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
2433 return self.addAtomExtraAssumeCapacity(extra);
2434}
2435
2436fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
2437 const index = @as(u32, @intCast(self.atoms_extra.items.len));
2438 const fields = @typeInfo(Atom.Extra).Struct.fields;
2439 inline for (fields) |field| {
2440 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
2441 u32 => @field(extra, field.name),
2442 else => @compileError("bad field type"),
2443 });
2444 }
2445 return index;
2446}
2447
2448pub fn getAtomExtra(self: Object, index: u32) Atom.Extra {
2449 const fields = @typeInfo(Atom.Extra).Struct.fields;
2450 var i: usize = index;
2451 var result: Atom.Extra = undefined;
2452 inline for (fields) |field| {
2453 @field(result, field.name) = switch (field.type) {
2454 u32 => self.atoms_extra.items[i],
2455 else => @compileError("bad field type"),
2456 };
2457 i += 1;
2458 }
2459 return result;
2460}
2461
2462pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
2463 assert(index > 0);
2464 const fields = @typeInfo(Atom.Extra).Struct.fields;
2465 inline for (fields, 0..) |field, i| {
2466 self.atoms_extra.items[index + i] = switch (field.type) {
2467 u32 => @field(extra, field.name),
2468 else => @compileError("bad field type"),
2469 };
2470 }
2471}
2472
2473fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
2474 try self.symbols.ensureUnusedCapacity(allocator, 1);
2475 return self.addSymbolAssumeCapacity();
2476}
2477
2478fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
2479 const index: Symbol.Index = @intCast(self.symbols.items.len);
2480 const symbol = self.symbols.addOneAssumeCapacity();
2481 symbol.* = .{ .file = self.index };
2482 return index;
2483}
2484
2485pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
2486 const global_index = self.globals.items[index];
2487 if (macho_file.resolver.get(global_index)) |ref| return ref;
2488 return .{ .index = index, .file = self.index };
2489}
2490
2491pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {
2492 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2493 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
2494 return self.addSymbolExtraAssumeCapacity(extra);
2495}
2496
2497fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 {
2498 const index = @as(u32, @intCast(self.symbols_extra.items.len));
2499 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2500 inline for (fields) |field| {
2501 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
2502 u32 => @field(extra, field.name),
2503 else => @compileError("bad field type"),
2504 });
2505 }
2506 return index;
2507}
2508
2509pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra {
2510 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2511 var i: usize = index;
2512 var result: Symbol.Extra = undefined;
2513 inline for (fields) |field| {
2514 @field(result, field.name) = switch (field.type) {
2515 u32 => self.symbols_extra.items[i],
2516 else => @compileError("bad field type"),
2517 };
2518 i += 1;
2519 }
2520 return result;
2521}
2522
2523pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void {
2524 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2525 inline for (fields, 0..) |field, i| {
2526 self.symbols_extra.items[index + i] = switch (field.type) {
2527 u32 => @field(extra, field.name),
2528 else => @compileError("bad field type"),
2529 };
2530 }
2531}
2532
20762533fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index {
20772534 try self.unwind_records.ensureUnusedCapacity(allocator, 1);
20782535 return self.addUnwindRecordAssumeCapacity();
......@@ -2124,10 +2581,11 @@ fn formatAtoms(
21242581 _ = unused_fmt_string;
21252582 _ = options;
21262583 const object = ctx.object;
2584 const macho_file = ctx.macho_file;
21272585 try writer.writeAll(" atoms\n");
2128 for (object.atoms.items) |atom_index| {
2129 const atom = ctx.macho_file.getAtom(atom_index).?;
2130 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
2586 for (object.getAtoms()) |atom_index| {
2587 const atom = object.getAtom(atom_index) orelse continue;
2588 try writer.print(" {}\n", .{atom.fmt(macho_file)});
21312589 }
21322590}
21332591
......@@ -2214,10 +2672,26 @@ fn formatSymtab(
22142672 _ = unused_fmt_string;
22152673 _ = options;
22162674 const object = ctx.object;
2675 const macho_file = ctx.macho_file;
22172676 try writer.writeAll(" symbols\n");
2218 for (object.symbols.items) |index| {
2219 const sym = ctx.macho_file.getSymbol(index);
2220 try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});
2677 for (object.symbols.items, 0..) |sym, i| {
2678 const ref = object.getSymbolRef(@intCast(i), macho_file);
2679 if (ref.getFile(macho_file) == null) {
2680 // TODO any better way of handling this?
2681 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
2682 } else {
2683 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
2684 }
2685 }
2686 for (object.stab_files.items) |sf| {
2687 try writer.print(" stabs({s},{s},{s})\n", .{
2688 sf.getCompDir(object),
2689 sf.getTuName(object),
2690 sf.getOsoPath(object),
2691 });
2692 for (sf.stabs.items) |stab| {
2693 try writer.print(" {}", .{stab.fmt(object)});
2694 }
22212695 }
22222696}
22232697
......@@ -2286,8 +2760,47 @@ const StabFile = struct {
22862760 is_func: bool = true,
22872761 symbol: ?Symbol.Index = null,
22882762
2289 fn getSymbol(stab: Stab, macho_file: *MachO) ?*Symbol {
2290 return if (stab.symbol) |s| macho_file.getSymbol(s) else null;
2763 fn getSymbol(stab: Stab, object: *const Object) ?*Symbol {
2764 const index = stab.index orelse return null;
2765 return &object.symbols.items[index];
2766 }
2767
2768 pub fn format(
2769 stab: Stab,
2770 comptime unused_fmt_string: []const u8,
2771 options: std.fmt.FormatOptions,
2772 writer: anytype,
2773 ) !void {
2774 _ = stab;
2775 _ = unused_fmt_string;
2776 _ = options;
2777 _ = writer;
2778 @compileError("do not format stabs directly");
2779 }
2780
2781 const StabFormatContext = struct { Stab, *const Object };
2782
2783 pub fn fmt(stab: Stab, object: *const Object) std.fmt.Formatter(format2) {
2784 return .{ .data = .{ stab, object } };
2785 }
2786
2787 fn format2(
2788 ctx: StabFormatContext,
2789 comptime unused_fmt_string: []const u8,
2790 options: std.fmt.FormatOptions,
2791 writer: anytype,
2792 ) !void {
2793 _ = unused_fmt_string;
2794 _ = options;
2795 const stab, const object = ctx;
2796 const sym = stab.getSymbol(object).?;
2797 if (stab.is_func) {
2798 try writer.print("func({d})", .{stab.index.?});
2799 } else if (sym.visibility == .global) {
2800 try writer.print("gsym({d})", .{stab.index.?});
2801 } else {
2802 try writer.print("stsym({d})", .{stab.index.?});
2803 }
22912804 }
22922805 };
22932806};
......@@ -2310,6 +2823,13 @@ const InArchive = struct {
23102823 size: u32,
23112824};
23122825
2826const CompactUnwindCtx = struct {
2827 rec_index: u32 = 0,
2828 rec_count: u32 = 0,
2829 reloc_index: u32 = 0,
2830 reloc_count: u32 = 0,
2831};
2832
23132833const x86_64 = struct {
23142834 fn parseRelocs(
23152835 self: *const Object,