authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-03 21:04:37+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-22 16:58:20+02:00
logd042b88c112aa919386bc76294225d4f7bd9a7b3
tree1ac0efc9bf4399cce17cffb4d80710d74f8b44fe
parentb81c48d9856141a4a380bd8569825919e5bd8cfc

macho: put parsing into an atom into a helper


1 files changed, 263 insertions(+), 72 deletions(-)

src/link/MachO/Object.zig+263-72
...@@ -16,6 +16,7 @@ const trace = @import("../../tracy.zig").trace;...@@ -16,6 +16,7 @@ const trace = @import("../../tracy.zig").trace;
16const Allocator = mem.Allocator;16const Allocator = mem.Allocator;
17const Atom = @import("Atom.zig");17const Atom = @import("Atom.zig");
18const MachO = @import("../MachO.zig");18const MachO = @import("../MachO.zig");
19const MatchingSection = MachO.MatchingSection;
1920
20file: fs.File,21file: fs.File,
21name: []const u8,22name: []const u8,
...@@ -421,6 +422,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -421,6 +422,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
421 // We only care about defined symbols, so filter every other out.422 // We only care about defined symbols, so filter every other out.
422 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];423 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];
423424
425 const dead_strip = blk: {
426 const dead_strip = macho_file.base.options.gc_sections orelse break :blk false;
427 if (dead_strip or macho_file.base.options.optimize_mode != .Debug)
428 break :blk self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
429 break :blk false;
430 };
431
424 for (seg.sections.items) |sect, id| {432 for (seg.sections.items) |sect, id| {
425 const sect_id = @intCast(u8, id);433 const sect_id = @intCast(u8, id);
426 log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });434 log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });
...@@ -459,85 +467,42 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -459,85 +467,42 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
459 };467 };
460 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;468 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
461469
462 // Since there is no symbol to refer to this atom, we create470 if (dead_strip) blk: {
471 if (filtered_nlists.len == 0) break :blk; // nothing to split
472
473 // If the first nlist does not match the start of the section,
474 // then we need to encapsulate the memory range [section start, first symbol)
475 // as a temporary symbol and insert the matching Atom.
476 const first_nlist = filtered_nlists[0].nlist;
477 if (first_nlist.n_value > sect.addr) {}
478 }
479
480 // If there is no symbol to refer to this atom, we create
463 // a temp one, unless we already did that when working out the relocations481 // a temp one, unless we already did that when working out the relocations
464 // of other atoms.482 // of other atoms.
465 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {483 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
466 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);484 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
467 try macho_file.locals.append(allocator, .{485 try macho_file.locals.append(allocator, .{
468 .n_strx = 0,486 .n_strx = 0,
469 .n_type = macho.N_SECT,487 .n_type = macho.N_SECT,
470 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),488 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
471 .n_desc = 0,489 .n_desc = 0,
472 .n_value = 0,490 .n_value = sect.addr,
473 });491 });
474 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);492 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
475 break :blk atom_local_sym_index;493 break :blk local_sym_index;
476 };494 };
477 const alignment = try math.powi(u32, 2, sect.@"align");495 const atom = try self.parseIntoAtom(
478 const aligned_size = mem.alignForwardGeneric(u64, sect.size, alignment);496 allocator,
479 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align");497 local_sym_index,
480498 sect.size,
481 if (code) |cc| {499 sect.@"align",
482 assert(!is_zerofill);500 code,
483 mem.copy(u8, atom.code.items, cc);501 relocs,
484 }502 filtered_nlists,
485503 match,
486 try atom.parseRelocs(relocs, .{504 macho_file,
487 .base_addr = sect.addr,505 );
488 .allocator = allocator,
489 .object = self,
490 .macho_file = macho_file,
491 });
492
493 if (macho_file.has_dices) {
494 const dices = filterDice(self.data_in_code_entries, sect.addr, sect.addr + sect.size);
495 try atom.dices.ensureTotalCapacity(allocator, dices.len);
496
497 for (dices) |dice| {
498 atom.dices.appendAssumeCapacity(.{
499 .offset = dice.offset - (math.cast(u32, sect.addr) orelse return error.Overflow),
500 .length = dice.length,
501 .kind = dice.kind,
502 });
503 }
504 }
505
506 // Since this is atom gets a helper local temporary symbol that didn't exist
507 // in the object file which encompasses the entire section, we need traverse
508 // the filtered symbols and note which symbol is contained within so that
509 // we can properly allocate addresses down the line.
510 // While we're at it, we need to update segment,section mapping of each symbol too.
511 try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len);
512
513 for (filtered_nlists) |nlist_with_index| {
514 const nlist = nlist_with_index.nlist;
515 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
516 const local = &macho_file.locals.items[local_sym_index];
517 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
518
519 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
520 // TODO there has to be a better to handle this.
521 for (di.inner.func_list.items) |func| {
522 if (func.pc_range) |range| {
523 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
524 break :blk Atom.Stab{
525 .function = range.end - range.start,
526 };
527 }
528 }
529 }
530 // TODO
531 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
532 break :blk .static;
533 } else null;
534
535 atom.contained.appendAssumeCapacity(.{
536 .local_sym_index = local_sym_index,
537 .offset = nlist.n_value - sect.addr,
538 .stab = stab,
539 });
540 }
541506
542 if (!self.start_atoms.contains(match)) {507 if (!self.start_atoms.contains(match)) {
543 try self.start_atoms.putNoClobber(allocator, match, atom);508 try self.start_atoms.putNoClobber(allocator, match, atom);
...@@ -554,6 +519,232 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -554,6 +519,232 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
554 }519 }
555}520}
556521
522// const Context = struct {
523// allocator: *Allocator,
524// object: *Object,
525// macho_file: *MachO,
526// match: MachO.MatchingSection,
527// };
528
529// const AtomParser = struct {
530// section: macho.section_64,
531// code: []u8,
532// relocs: []macho.relocation_info,
533// nlists: []NlistWithIndex,
534// index: u32 = 0,
535
536// fn peek(self: AtomParser) ?NlistWithIndex {
537// return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
538// }
539
540// fn lessThanBySeniority(context: Context, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
541// if (!MachO.symbolIsExt(rhs.nlist)) {
542// return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));
543// } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) {
544// return !MachO.symbolIsExt(lhs.nlist);
545// } else {
546// return false;
547// }
548// }
549
550// pub fn next(self: *AtomParser, context: Context) !?*Atom {
551// if (self.index == self.nlists.len) return null;
552
553// const tracy = trace(@src());
554// defer tracy.end();
555
556// var aliases = std.ArrayList(NlistWithIndex).init(context.allocator);
557// defer aliases.deinit();
558
559// const next_nlist: ?NlistWithIndex = blk: while (true) {
560// const curr_nlist = self.nlists[self.index];
561// try aliases.append(curr_nlist);
562
563// if (self.peek()) |next_nlist| {
564// if (curr_nlist.nlist.n_value == next_nlist.nlist.n_value) {
565// self.index += 1;
566// continue;
567// }
568// break :blk next_nlist;
569// }
570// break :blk null;
571// } else null;
572
573// for (aliases.items) |*nlist_with_index| {
574// nlist_with_index.index = context.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
575// }
576
577// if (aliases.items.len > 1) {
578// // Bubble-up senior symbol as the main link to the atom.
579// sort.sort(
580// NlistWithIndex,
581// aliases.items,
582// context,
583// AtomParser.lessThanBySeniority,
584// );
585// }
586
587// const senior_nlist = aliases.pop();
588// const senior_sym = &context.macho_file.locals.items[senior_nlist.index];
589// senior_sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);
590
591// const start_addr = senior_nlist.nlist.n_value - self.section.addr;
592// const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
593
594// const code = self.code[start_addr..end_addr];
595// const size = code.len;
596
597// const max_align = self.section.@"align";
598// const actual_align = if (senior_nlist.nlist.n_value > 0)
599// math.min(@ctz(u64, senior_nlist.nlist.n_value), max_align)
600// else
601// max_align;
602
603// const stab: ?Atom.Stab = if (context.object.debug_info) |di| blk: {
604// // TODO there has to be a better to handle this.
605// for (di.inner.func_list.items) |func| {
606// if (func.pc_range) |range| {
607// if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) {
608// break :blk Atom.Stab{
609// .function = range.end - range.start,
610// };
611// }
612// }
613// }
614// // TODO
615// // if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global;
616// break :blk .static;
617// } else null;
618
619// const atom = try context.macho_file.createEmptyAtom(senior_nlist.index, size, actual_align);
620// atom.stab = stab;
621
622// const is_zerofill = blk: {
623// const section_type = commands.sectionType(self.section);
624// break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
625// };
626// if (!is_zerofill) {
627// mem.copy(u8, atom.code.items, code);
628// }
629
630// try atom.aliases.ensureTotalCapacity(context.allocator, aliases.items.len);
631// for (aliases.items) |alias| {
632// atom.aliases.appendAssumeCapacity(alias.index);
633// const sym = &context.macho_file.locals.items[alias.index];
634// sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);
635// }
636
637// try atom.parseRelocs(self.relocs, .{
638// .base_addr = self.section.addr,
639// .base_offset = start_addr,
640// .allocator = context.allocator,
641// .object = context.object,
642// .macho_file = context.macho_file,
643// });
644
645// if (context.macho_file.has_dices) {
646// const dices = filterDice(
647// context.object.data_in_code_entries.items,
648// senior_nlist.nlist.n_value,
649// senior_nlist.nlist.n_value + size,
650// );
651// try atom.dices.ensureTotalCapacity(context.allocator, dices.len);
652
653// for (dices) |dice| {
654// atom.dices.appendAssumeCapacity(.{
655// .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value),
656// .length = dice.length,
657// .kind = dice.kind,
658// });
659// }
660// }
661
662// self.index += 1;
663
664// return atom;
665// }
666// };
667
668fn parseIntoAtom(
669 self: *Object,
670 allocator: Allocator,
671 local_sym_index: u32,
672 size: u64,
673 alignment: u32,
674 code: ?[]const u8,
675 relocs: []const macho.relocation_info,
676 nlists: []const NlistWithIndex,
677 match: MatchingSection,
678 macho_file: *MachO,
679) !*Atom {
680 const sym = macho_file.locals.items[local_sym_index];
681 const align_pow_2 = try math.powi(u32, 2, alignment);
682 const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2);
683 const atom = try macho_file.createEmptyAtom(local_sym_index, aligned_size, alignment);
684
685 if (code) |cc| {
686 mem.copy(u8, atom.code.items, cc);
687 }
688
689 try atom.parseRelocs(relocs, .{
690 .base_addr = sym.n_value,
691 .allocator = allocator,
692 .object = self,
693 .macho_file = macho_file,
694 });
695
696 if (macho_file.has_dices) {
697 const dices = filterDice(self.data_in_code_entries, sym.n_value, sym.n_value + size);
698 try atom.dices.ensureTotalCapacity(allocator, dices.len);
699
700 for (dices) |dice| {
701 atom.dices.appendAssumeCapacity(.{
702 .offset = dice.offset - (math.cast(u32, sym.n_value) orelse return error.Overflow),
703 .length = dice.length,
704 .kind = dice.kind,
705 });
706 }
707 }
708
709 // Since this is atom gets a helper local temporary symbol that didn't exist
710 // in the object file which encompasses the entire section, we need traverse
711 // the filtered symbols and note which symbol is contained within so that
712 // we can properly allocate addresses down the line.
713 // While we're at it, we need to update segment,section mapping of each symbol too.
714 try atom.contained.ensureTotalCapacity(allocator, nlists.len);
715
716 for (nlists) |nlist_with_index| {
717 const nlist = nlist_with_index.nlist;
718 const sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
719 const this_sym = &macho_file.locals.items[sym_index];
720 this_sym.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
721
722 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
723 // TODO there has to be a better to handle this.
724 for (di.inner.func_list.items) |func| {
725 if (func.pc_range) |range| {
726 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
727 break :blk Atom.Stab{
728 .function = range.end - range.start,
729 };
730 }
731 }
732 }
733 // TODO
734 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
735 break :blk .static;
736 } else null;
737
738 atom.contained.appendAssumeCapacity(.{
739 .local_sym_index = sym_index,
740 .offset = nlist.n_value - sym.n_value,
741 .stab = stab,
742 });
743 }
744
745 return atom;
746}
747
557fn parseSymtab(self: *Object) void {748fn parseSymtab(self: *Object) void {
558 const index = self.symtab_cmd_index orelse return;749 const index = self.symtab_cmd_index orelse return;
559 const symtab = self.load_commands.items[index].symtab;750 const symtab = self.load_commands.items[index].symtab;
...@@ -563,7 +754,7 @@ fn parseSymtab(self: *Object) void {...@@ -563,7 +754,7 @@ fn parseSymtab(self: *Object) void {
563 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];754 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
564}755}
565756
566pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {757fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
567 log.debug("parsing debug info in '{s}'", .{self.name});758 log.debug("parsing debug info in '{s}'", .{self.name});
568759
569 var debug_info = blk: {760 var debug_info = blk: {
...@@ -595,7 +786,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {...@@ -595,7 +786,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
595 }786 }
596}787}
597788
598pub fn parseDataInCode(self: *Object) void {789fn parseDataInCode(self: *Object) void {
599 const index = self.data_in_code_cmd_index orelse return;790 const index = self.data_in_code_cmd_index orelse return;
600 const data_in_code = self.load_commands.items[index].linkedit_data;791 const data_in_code = self.load_commands.items[index].linkedit_data;
601 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];792 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];