diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index d43f6154d9..f01f366fdd 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -16,6 +16,7 @@ const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); const MachO = @import("../MachO.zig"); +const MatchingSection = MachO.MatchingSection; file: fs.File, name: []const u8, @@ -421,6 +422,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! // We only care about defined symbols, so filter every other out. const sorted_nlists = sorted_all_nlists.items[0..iundefsym]; + const dead_strip = blk: { + const dead_strip = macho_file.base.options.gc_sections orelse break :blk false; + if (dead_strip or macho_file.base.options.optimize_mode != .Debug) + break :blk self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; + break :blk false; + }; + for (seg.sections.items) |sect, id| { const sect_id = @intCast(u8, id); 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) ! }; macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null; - // Since there is no symbol to refer to this atom, we create + if (dead_strip) blk: { + if (filtered_nlists.len == 0) break :blk; // nothing to split + + // If the first nlist does not match the start of the section, + // then we need to encapsulate the memory range [section start, first symbol) + // as a temporary symbol and insert the matching Atom. + const first_nlist = filtered_nlists[0].nlist; + if (first_nlist.n_value > sect.addr) {} + } + + // If there is no symbol to refer to this atom, we create // a temp one, unless we already did that when working out the relocations // of other atoms. - const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { - const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len); + const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { + const local_sym_index = @intCast(u32, macho_file.locals.items.len); try macho_file.locals.append(allocator, .{ .n_strx = 0, .n_type = macho.N_SECT, .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1), .n_desc = 0, - .n_value = 0, + .n_value = sect.addr, }); - try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index); - break :blk atom_local_sym_index; + try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index); + break :blk local_sym_index; }; - const alignment = try math.powi(u32, 2, sect.@"align"); - const aligned_size = mem.alignForwardGeneric(u64, sect.size, alignment); - const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align"); - - if (code) |cc| { - assert(!is_zerofill); - mem.copy(u8, atom.code.items, cc); - } - - try atom.parseRelocs(relocs, .{ - .base_addr = sect.addr, - .allocator = allocator, - .object = self, - .macho_file = macho_file, - }); - - if (macho_file.has_dices) { - const dices = filterDice(self.data_in_code_entries, sect.addr, sect.addr + sect.size); - try atom.dices.ensureTotalCapacity(allocator, dices.len); - - for (dices) |dice| { - atom.dices.appendAssumeCapacity(.{ - .offset = dice.offset - (math.cast(u32, sect.addr) orelse return error.Overflow), - .length = dice.length, - .kind = dice.kind, - }); - } - } - - // Since this is atom gets a helper local temporary symbol that didn't exist - // in the object file which encompasses the entire section, we need traverse - // the filtered symbols and note which symbol is contained within so that - // we can properly allocate addresses down the line. - // While we're at it, we need to update segment,section mapping of each symbol too. - try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len); - - for (filtered_nlists) |nlist_with_index| { - const nlist = nlist_with_index.nlist; - const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; - const local = &macho_file.locals.items[local_sym_index]; - local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1); - - const stab: ?Atom.Stab = if (self.debug_info) |di| blk: { - // TODO there has to be a better to handle this. - for (di.inner.func_list.items) |func| { - if (func.pc_range) |range| { - if (nlist.n_value >= range.start and nlist.n_value < range.end) { - break :blk Atom.Stab{ - .function = range.end - range.start, - }; - } - } - } - // TODO - // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; - break :blk .static; - } else null; - - atom.contained.appendAssumeCapacity(.{ - .local_sym_index = local_sym_index, - .offset = nlist.n_value - sect.addr, - .stab = stab, - }); - } + const atom = try self.parseIntoAtom( + allocator, + local_sym_index, + sect.size, + sect.@"align", + code, + relocs, + filtered_nlists, + match, + macho_file, + ); if (!self.start_atoms.contains(match)) { try self.start_atoms.putNoClobber(allocator, match, atom); @@ -554,6 +519,232 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! } } +// const Context = struct { +// allocator: *Allocator, +// object: *Object, +// macho_file: *MachO, +// match: MachO.MatchingSection, +// }; + +// const AtomParser = struct { +// section: macho.section_64, +// code: []u8, +// relocs: []macho.relocation_info, +// nlists: []NlistWithIndex, +// index: u32 = 0, + +// fn peek(self: AtomParser) ?NlistWithIndex { +// return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null; +// } + +// fn lessThanBySeniority(context: Context, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { +// if (!MachO.symbolIsExt(rhs.nlist)) { +// return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); +// } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) { +// return !MachO.symbolIsExt(lhs.nlist); +// } else { +// return false; +// } +// } + +// pub fn next(self: *AtomParser, context: Context) !?*Atom { +// if (self.index == self.nlists.len) return null; + +// const tracy = trace(@src()); +// defer tracy.end(); + +// var aliases = std.ArrayList(NlistWithIndex).init(context.allocator); +// defer aliases.deinit(); + +// const next_nlist: ?NlistWithIndex = blk: while (true) { +// const curr_nlist = self.nlists[self.index]; +// try aliases.append(curr_nlist); + +// if (self.peek()) |next_nlist| { +// if (curr_nlist.nlist.n_value == next_nlist.nlist.n_value) { +// self.index += 1; +// continue; +// } +// break :blk next_nlist; +// } +// break :blk null; +// } else null; + +// for (aliases.items) |*nlist_with_index| { +// nlist_with_index.index = context.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable; +// } + +// if (aliases.items.len > 1) { +// // Bubble-up senior symbol as the main link to the atom. +// sort.sort( +// NlistWithIndex, +// aliases.items, +// context, +// AtomParser.lessThanBySeniority, +// ); +// } + +// const senior_nlist = aliases.pop(); +// const senior_sym = &context.macho_file.locals.items[senior_nlist.index]; +// senior_sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1); + +// const start_addr = senior_nlist.nlist.n_value - self.section.addr; +// const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; + +// const code = self.code[start_addr..end_addr]; +// const size = code.len; + +// const max_align = self.section.@"align"; +// const actual_align = if (senior_nlist.nlist.n_value > 0) +// math.min(@ctz(u64, senior_nlist.nlist.n_value), max_align) +// else +// max_align; + +// const stab: ?Atom.Stab = if (context.object.debug_info) |di| blk: { +// // TODO there has to be a better to handle this. +// for (di.inner.func_list.items) |func| { +// if (func.pc_range) |range| { +// if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) { +// break :blk Atom.Stab{ +// .function = range.end - range.start, +// }; +// } +// } +// } +// // TODO +// // if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global; +// break :blk .static; +// } else null; + +// const atom = try context.macho_file.createEmptyAtom(senior_nlist.index, size, actual_align); +// atom.stab = stab; + +// const is_zerofill = blk: { +// const section_type = commands.sectionType(self.section); +// break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; +// }; +// if (!is_zerofill) { +// mem.copy(u8, atom.code.items, code); +// } + +// try atom.aliases.ensureTotalCapacity(context.allocator, aliases.items.len); +// for (aliases.items) |alias| { +// atom.aliases.appendAssumeCapacity(alias.index); +// const sym = &context.macho_file.locals.items[alias.index]; +// sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1); +// } + +// try atom.parseRelocs(self.relocs, .{ +// .base_addr = self.section.addr, +// .base_offset = start_addr, +// .allocator = context.allocator, +// .object = context.object, +// .macho_file = context.macho_file, +// }); + +// if (context.macho_file.has_dices) { +// const dices = filterDice( +// context.object.data_in_code_entries.items, +// senior_nlist.nlist.n_value, +// senior_nlist.nlist.n_value + size, +// ); +// try atom.dices.ensureTotalCapacity(context.allocator, dices.len); + +// for (dices) |dice| { +// atom.dices.appendAssumeCapacity(.{ +// .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value), +// .length = dice.length, +// .kind = dice.kind, +// }); +// } +// } + +// self.index += 1; + +// return atom; +// } +// }; + +fn parseIntoAtom( + self: *Object, + allocator: Allocator, + local_sym_index: u32, + size: u64, + alignment: u32, + code: ?[]const u8, + relocs: []const macho.relocation_info, + nlists: []const NlistWithIndex, + match: MatchingSection, + macho_file: *MachO, +) !*Atom { + const sym = macho_file.locals.items[local_sym_index]; + const align_pow_2 = try math.powi(u32, 2, alignment); + const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2); + const atom = try macho_file.createEmptyAtom(local_sym_index, aligned_size, alignment); + + if (code) |cc| { + mem.copy(u8, atom.code.items, cc); + } + + try atom.parseRelocs(relocs, .{ + .base_addr = sym.n_value, + .allocator = allocator, + .object = self, + .macho_file = macho_file, + }); + + if (macho_file.has_dices) { + const dices = filterDice(self.data_in_code_entries, sym.n_value, sym.n_value + size); + try atom.dices.ensureTotalCapacity(allocator, dices.len); + + for (dices) |dice| { + atom.dices.appendAssumeCapacity(.{ + .offset = dice.offset - (math.cast(u32, sym.n_value) orelse return error.Overflow), + .length = dice.length, + .kind = dice.kind, + }); + } + } + + // Since this is atom gets a helper local temporary symbol that didn't exist + // in the object file which encompasses the entire section, we need traverse + // the filtered symbols and note which symbol is contained within so that + // we can properly allocate addresses down the line. + // While we're at it, we need to update segment,section mapping of each symbol too. + try atom.contained.ensureTotalCapacity(allocator, nlists.len); + + for (nlists) |nlist_with_index| { + const nlist = nlist_with_index.nlist; + const sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; + const this_sym = &macho_file.locals.items[sym_index]; + this_sym.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1); + + const stab: ?Atom.Stab = if (self.debug_info) |di| blk: { + // TODO there has to be a better to handle this. + for (di.inner.func_list.items) |func| { + if (func.pc_range) |range| { + if (nlist.n_value >= range.start and nlist.n_value < range.end) { + break :blk Atom.Stab{ + .function = range.end - range.start, + }; + } + } + } + // TODO + // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; + break :blk .static; + } else null; + + atom.contained.appendAssumeCapacity(.{ + .local_sym_index = sym_index, + .offset = nlist.n_value - sym.n_value, + .stab = stab, + }); + } + + return atom; +} + fn parseSymtab(self: *Object) void { const index = self.symtab_cmd_index orelse return; const symtab = self.load_commands.items[index].symtab; @@ -563,7 +754,7 @@ fn parseSymtab(self: *Object) void { self.strtab = self.contents[symtab.stroff..][0..symtab.strsize]; } -pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void { +fn parseDebugInfo(self: *Object, allocator: Allocator) !void { log.debug("parsing debug info in '{s}'", .{self.name}); var debug_info = blk: { @@ -595,7 +786,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void { } } -pub fn parseDataInCode(self: *Object) void { +fn parseDataInCode(self: *Object) void { const index = self.data_in_code_cmd_index orelse return; const data_in_code = self.load_commands.items[index].linkedit_data; const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];