macho: re-enable parsing sections into atoms

However, make it default only when building in release modes since
it's a prelude to advanced dead code stripping not very useful in
debug.
This commit is contained in:
Jakub Konka 2021-07-23 11:51:48 +02:00
parent a4feb97cdf
commit 1beda818e1
3 changed files with 273 additions and 273 deletions

View File

@ -965,60 +965,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
try self.allocateDataSegment();
self.allocateLinkeditSegment();
try self.allocateTextBlocks();
// log.warn("locals", .{});
// for (self.locals.items) |sym, id| {
// log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
// }
// log.warn("globals", .{});
// for (self.globals.items) |sym, id| {
// log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
// }
// log.warn("tentatives", .{});
// for (self.tentatives.items) |sym, id| {
// log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
// }
// log.warn("undefines", .{});
// for (self.undefs.items) |sym, id| {
// log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
// }
// log.warn("imports", .{});
// for (self.imports.items) |sym, id| {
// log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
// }
// log.warn("symbol resolver", .{});
// for (self.symbol_resolver.keys()) |key| {
// log.warn(" {s} => {}", .{ key, self.symbol_resolver.get(key).? });
// }
// log.warn("mappings", .{});
// for (self.objects.items) |object, id| {
// const object_id = @intCast(u16, id);
// log.warn(" in object {s}", .{object.name.?});
// for (object.symtab.items) |sym, sym_id| {
// if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| {
// log.warn(" | {d} => {d}", .{ sym_id, local_id });
// } else {
// log.warn(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) });
// }
// }
// }
// var it = self.blocks.iterator();
// while (it.next()) |entry| {
// const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
// const sect = seg.sections.items[entry.key_ptr.sect];
// log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
// log.warn(" {}", .{sect});
// entry.value_ptr.*.print(self);
// }
self.printSymtabAndTextBlock();
try self.flushZld();
}
@ -2086,6 +2033,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
.n_value = sym.n_value,
});
try object.symbol_mapping.putNoClobber(self.base.allocator, sym_id, local_sym_index);
try object.reverse_symbol_mapping.putNoClobber(self.base.allocator, local_sym_index, sym_id);
// If the symbol's scope is not local aka translation unit, then we need work out
// if we should save the symbol as a global, or potentially flag the error.
@ -5916,3 +5864,70 @@ fn createSectionOrdinal(self: *MachO, match: MatchingSection) !void {
try self.section_ordinals.append(self.base.allocator, match);
try self.section_to_ordinal.putNoClobber(self.base.allocator, match, ordinal);
}
fn printSymtabAndTextBlock(self: *MachO) void {
log.debug("locals", .{});
for (self.locals.items) |sym, id| {
log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.debug("globals", .{});
for (self.globals.items) |sym, id| {
log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.debug("tentatives", .{});
for (self.tentatives.items) |sym, id| {
log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.debug("undefines", .{});
for (self.undefs.items) |sym, id| {
log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.debug("imports", .{});
for (self.imports.items) |sym, id| {
log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
{
log.debug("symbol resolver", .{});
var it = self.symbol_resolver.keyIterator();
while (it.next()) |key_ptr| {
const sym_name = self.getString(key_ptr.*);
log.debug(" {s} => {}", .{ sym_name, self.symbol_resolver.get(key_ptr.*).? });
}
}
log.debug("mappings", .{});
for (self.objects.items) |object| {
log.debug(" in object {s}", .{object.name.?});
for (object.symtab.items) |sym, sym_id| {
if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| {
log.debug(" | {d} => {d}", .{ sym_id, local_id });
} else {
log.debug(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) });
}
}
}
{
var it = self.blocks.iterator();
while (it.next()) |entry| {
const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
const sect = seg.sections.items[entry.key_ptr.sect];
var block: *TextBlock = entry.value_ptr.*;
log.debug("\n\n{s},{s} contents:", .{ commands.segmentName(sect), commands.sectionName(sect) });
log.debug("{}", .{sect});
log.debug("{}", .{block});
while (block.prev) |prev| {
block = prev;
log.debug("{}", .{block});
}
}
}
}

View File

@ -55,7 +55,11 @@ mtime: ?u64 = null,
text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
// TODO symbol mapping and its inverse can probably be simple arrays
// instead of hash maps.
symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
const DebugInfo = struct {
inner: dwarf.DwarfInfo,
@ -164,6 +168,7 @@ pub fn deinit(self: *Object) void {
self.text_blocks.deinit(self.allocator);
self.sections_as_symbols.deinit(self.allocator);
self.symbol_mapping.deinit(self.allocator);
self.reverse_symbol_mapping.deinit(self.allocator);
if (self.debug_info) |*db| {
db.deinit(self.allocator);
@ -367,7 +372,7 @@ const TextBlockParser = struct {
} else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) {
return !MachO.symbolIsExt(lhs.nlist);
} else {
return true;
return false;
}
}
@ -392,15 +397,7 @@ const TextBlockParser = struct {
} else null;
for (aliases.items) |*nlist_with_index| {
nlist_with_index.index = self.symbol_mapping.get(nlist_with_index.index);
const sym = self.object.symbols.items[nlist_with_index.index];
if (sym.payload != .regular) {
log.err("expected a regular symbol, found {s}", .{sym.payload});
log.err(" when remapping {s}", .{self.macho_file.getString(sym.strx)});
return error.SymbolIsNotRegular;
}
assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.
nlist_with_index.index = sym.payload.regular.local_sym_index;
nlist_with_index.index = self.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
}
if (aliases.items.len > 1) {
@ -409,15 +406,13 @@ const TextBlockParser = struct {
NlistWithIndex,
aliases.items,
SeniorityContext{ .object = self.object },
@This().lessThanBySeniority,
TextBlockParser.lessThanBySeniority,
);
}
const senior_nlist = aliases.pop();
const senior_sym = self.macho_file.locals.items[senior_nlist.index];
assert(senior_sym.payload == .regular);
senior_sym.payload.regular.segment_id = self.match.seg;
senior_sym.payload.regular.section_id = self.match.sect;
const senior_sym = &self.macho_file.locals.items[senior_nlist.index];
senior_sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable;
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;
@ -442,33 +437,29 @@ const TextBlockParser = struct {
}
}
}
if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global;
// TODO
// if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global;
break :blk .static;
} else null;
const block = try self.allocator.create(TextBlock);
errdefer self.allocator.destroy(block);
block.* = TextBlock.init(self.allocator);
const block = try self.macho_file.base.allocator.create(TextBlock);
block.* = TextBlock.empty;
block.local_sym_index = senior_nlist.index;
block.stab = stab;
block.code = try self.allocator.dupe(u8, code);
block.size = size;
block.alignment = actual_align;
try self.macho_file.managed_blocks.append(self.macho_file.base.allocator, block);
if (aliases.items.len > 0) {
try block.aliases.ensureTotalCapacity(aliases.items.len);
for (aliases.items) |alias| {
block.aliases.appendAssumeCapacity(alias.index);
try block.code.appendSlice(self.macho_file.base.allocator, code);
const sym = self.macho_file.locals.items[alias.index];
const reg = &sym.payload.regular;
reg.segment_id = self.match.seg;
reg.section_id = self.match.sect;
}
try block.aliases.ensureTotalCapacity(self.macho_file.base.allocator, aliases.items.len);
for (aliases.items) |alias| {
block.aliases.appendAssumeCapacity(alias.index);
const sym = &self.macho_file.locals.items[alias.index];
sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable;
}
try block.parseRelocsFromObject(self.allocator, relocs, object, .{
try block.parseRelocsFromObject(self.macho_file.base.allocator, self.relocs, self.object, .{
.base_addr = start_addr,
.macho_file = self.macho_file,
});
@ -479,7 +470,7 @@ const TextBlockParser = struct {
senior_nlist.nlist.n_value,
senior_nlist.nlist.n_value + size,
);
try block.dices.ensureTotalCapacity(dices.len);
try block.dices.ensureTotalCapacity(self.macho_file.base.allocator, dices.len);
for (dices) |dice| {
block.dices.appendAssumeCapacity(.{
@ -518,10 +509,22 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
sort.sort(NlistWithIndex, sorted_all_nlists.items, {}, NlistWithIndex.lessThan);
const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
// Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
// have to infer the start of undef section in the symtab ourselves.
const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {
const dysymtab = self.load_commands.items[cmd_index].Dysymtab;
break :blk dysymtab.iundefsym;
} else blk: {
var iundefsym: usize = sorted_all_nlists.items.len;
while (iundefsym > 0) : (iundefsym -= 1) {
const nlist = sorted_all_nlists.items[iundefsym];
if (MachO.symbolIsSect(nlist.nlist)) break;
}
break :blk iundefsym;
};
// We only care about defined symbols, so filter every other out.
const sorted_nlists = sorted_all_nlists.items[dysymtab.ilocalsym..dysymtab.iundefsym];
const sorted_nlists = sorted_all_nlists.items[0..iundefsym];
for (seg.sections.items) |sect, id| {
const sect_id = @intCast(u8, id);
@ -550,11 +553,12 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
// Symbols within this section only.
const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);
// Is there any padding between symbols within the section?
// const is_splittable = self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
// TODO is it perhaps worth skip parsing subsections in Debug mode and not worry about
// duplicates at all? Need some benchmarks!
// const is_splittable = false;
// In release mode, if the object file was generated with dead code stripping optimisations,
// note it now and parse sections as atoms.
const is_splittable = blk: {
if (macho_file.base.options.optimize_mode == .Debug) break :blk false;
break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
};
macho_file.has_dices = blk: {
if (self.text_section_index) |index| {
@ -566,157 +570,152 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
};
macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
{
// next: {
// if (is_splittable) blocks: {
// if (filtered_nlists.len == 0) break :blocks;
next: {
if (is_splittable) blocks: {
if (filtered_nlists.len == 0) break :blocks;
// // If the first nlist does not match the start of the section,
// // then we need encapsulate the memory range [section start, first symbol)
// // as a temporary symbol and insert the matching TextBlock.
// const first_nlist = filtered_nlists[0].nlist;
// if (first_nlist.n_value > sect.addr) {
// const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
// const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
// self.name.?,
// segmentName(sect),
// sectionName(sect),
// });
// defer self.allocator.free(name);
// const symbol = try zld.allocator.create(Symbol);
// symbol.* = .{
// .strx = try zld.makeString(name),
// .payload = .{ .undef = .{} },
// };
// try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
// break :symbol symbol;
// };
// 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 TextBlock.
const first_nlist = filtered_nlists[0].nlist;
if (first_nlist.n_value > sect.addr) {
const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
self.name.?,
segmentName(sect),
sectionName(sect),
});
defer self.allocator.free(sym_name);
// const local_sym_index = @intCast(u32, zld.locals.items.len);
// symbol.payload = .{
// .regular = .{
// .linkage = .translation_unit,
// .address = sect.addr,
// .segment_id = match.seg,
// .section_id = match.sect,
// .file = self,
// .local_sym_index = local_sym_index,
// },
// };
// try zld.locals.append(zld.allocator, symbol);
const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);
try macho_file.locals.append(macho_file.base.allocator, .{
.n_strx = try macho_file.makeString(sym_name),
.n_type = macho.N_SECT,
.n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable,
.n_desc = 0,
.n_value = sect.addr,
});
try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index);
break :blk block_local_sym_index;
};
// const block_code = code[0 .. first_nlist.n_value - sect.addr];
// const block_size = block_code.len;
const block_code = code[0 .. first_nlist.n_value - sect.addr];
const block_size = block_code.len;
// const block = try self.allocator.create(TextBlock);
// errdefer self.allocator.destroy(block);
const block = try macho_file.base.allocator.create(TextBlock);
block.* = TextBlock.empty;
block.local_sym_index = block_local_sym_index;
block.size = block_size;
block.alignment = sect.@"align";
try macho_file.managed_blocks.append(macho_file.base.allocator, block);
// block.* = TextBlock.init(self.allocator);
// block.local_sym_index = local_sym_index;
// block.code = try self.allocator.dupe(u8, block_code);
// block.size = block_size;
// block.alignment = sect.@"align";
try block.code.appendSlice(macho_file.base.allocator, block_code);
// const block_relocs = filterRelocs(relocs, 0, block_size);
// if (block_relocs.len > 0) {
// try self.parseRelocs(zld, block_relocs, block, 0);
// }
try block.parseRelocsFromObject(self.allocator, relocs, self, .{
.base_addr = 0,
.macho_file = macho_file,
});
// if (zld.has_dices) {
// const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
// try block.dices.ensureTotalCapacity(dices.len);
if (macho_file.has_dices) {
const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len);
// for (dices) |dice| {
// block.dices.appendAssumeCapacity(.{
// .offset = dice.offset - try math.cast(u32, sect.addr),
// .length = dice.length,
// .kind = dice.kind,
// });
// }
// }
for (dices) |dice| {
block.dices.appendAssumeCapacity(.{
.offset = dice.offset - try math.cast(u32, sect.addr),
.length = dice.length,
.kind = dice.kind,
});
}
}
// // Update target section's metadata
// // TODO should we update segment's size here too?
// // How does it tie with incremental space allocs?
// const tseg = &zld.load_commands.items[match.seg].Segment;
// const tsect = &tseg.sections.items[match.sect];
// const new_alignment = math.max(tsect.@"align", block.alignment);
// const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
// const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
// tsect.size = new_size;
// tsect.@"align" = new_alignment;
// Update target section's metadata
// TODO should we update segment's size here too?
// How does it tie with incremental space allocs?
const tseg = &macho_file.load_commands.items[match.seg].Segment;
const tsect = &tseg.sections.items[match.sect];
const new_alignment = math.max(tsect.@"align", block.alignment);
const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
tsect.size = new_size;
tsect.@"align" = new_alignment;
// if (zld.blocks.getPtr(match)) |last| {
// last.*.next = block;
// block.prev = last.*;
// last.* = block;
// } else {
// try zld.blocks.putNoClobber(zld.allocator, match, block);
// }
if (macho_file.blocks.getPtr(match)) |last| {
last.*.next = block;
block.prev = last.*;
last.* = block;
} else {
try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block);
}
// try self.text_blocks.append(self.allocator, block);
// }
try self.text_blocks.append(self.allocator, block);
}
// var parser = TextBlockParser{
// .allocator = self.allocator,
// .section = sect,
// .code = code,
// .relocs = relocs,
// .object = self,
// .zld = zld,
// .nlists = filtered_nlists,
// .match = match,
// };
var parser = TextBlockParser{
.allocator = self.allocator,
.section = sect,
.code = code,
.relocs = relocs,
.object = self,
.macho_file = macho_file,
.nlists = filtered_nlists,
.match = match,
};
// while (try parser.next()) |block| {
// const sym = zld.locals.items[block.local_sym_index];
// const reg = &sym.payload.regular;
// if (reg.file) |file| {
// if (file != self) {
// log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
// block.deinit();
// self.allocator.destroy(block);
// continue;
// }
// }
while (try parser.next()) |block| {
const sym = macho_file.locals.items[block.local_sym_index];
const is_ext = blk: {
const orig_sym_id = self.reverse_symbol_mapping.get(block.local_sym_index) orelse unreachable;
break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]);
};
if (is_ext) {
if (macho_file.symbol_resolver.get(sym.n_strx)) |resolv| {
assert(resolv.where == .global);
const global_object = macho_file.objects.items[resolv.file];
if (global_object != self) {
log.debug("deduping definition of {s} in {s}", .{
macho_file.getString(sym.n_strx),
self.name.?,
});
log.debug(" already defined in {s}", .{global_object.name.?});
continue;
}
}
}
// if (reg.address == sect.addr) {
// if (self.sections_as_symbols.get(sect_id)) |alias| {
// // Add alias.
// const local_sym_index = @intCast(u32, zld.locals.items.len);
// const reg_alias = &alias.payload.regular;
// reg_alias.segment_id = match.seg;
// reg_alias.section_id = match.sect;
// reg_alias.local_sym_index = local_sym_index;
// try block.aliases.append(local_sym_index);
// try zld.locals.append(zld.allocator, alias);
// }
// }
if (sym.n_value == sect.addr) {
if (self.sections_as_symbols.get(sect_id)) |alias| {
// In x86_64 relocs, it can so happen that the compiler refers to the same
// atom by both the actual assigned symbol and the start of the section. In this
// case, we need to link the two together so add an alias.
try block.aliases.append(macho_file.base.allocator, alias);
}
}
// // Update target section's metadata
// // TODO should we update segment's size here too?
// // How does it tie with incremental space allocs?
// const tseg = &zld.load_commands.items[match.seg].Segment;
// const tsect = &tseg.sections.items[match.sect];
// const new_alignment = math.max(tsect.@"align", block.alignment);
// const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
// const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
// tsect.size = new_size;
// tsect.@"align" = new_alignment;
// Update target section's metadata
// TODO should we update segment's size here too?
// How does it tie with incremental space allocs?
const tseg = &macho_file.load_commands.items[match.seg].Segment;
const tsect = &tseg.sections.items[match.sect];
const new_alignment = math.max(tsect.@"align", block.alignment);
const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
tsect.size = new_size;
tsect.@"align" = new_alignment;
// if (zld.blocks.getPtr(match)) |last| {
// last.*.next = block;
// block.prev = last.*;
// last.* = block;
// } else {
// try zld.blocks.putNoClobber(zld.allocator, match, block);
// }
if (macho_file.blocks.getPtr(match)) |last| {
last.*.next = block;
block.prev = last.*;
last.* = block;
} else {
try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block);
}
// try self.text_blocks.append(self.allocator, block);
// }
try self.text_blocks.append(self.allocator, block);
}
// break :next;
// }
break :next;
}
// Since there is no symbol to refer to this block, we create
// a temp one, unless we already did that when working out the relocations
@ -757,7 +756,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
if (macho_file.has_dices) {
const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
try block.dices.ensureTotalCapacity(self.allocator, dices.len);
try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len);
for (dices) |dice| {
block.dices.appendAssumeCapacity(.{
@ -820,7 +819,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
block.prev = last.*;
last.* = block;
} else {
try macho_file.blocks.putNoClobber(self.allocator, match, block);
try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block);
}
try self.text_blocks.append(self.allocator, block);

View File

@ -75,6 +75,21 @@ pub const SymbolAtOffset = struct {
local_sym_index: u32,
offset: u64,
stab: ?Stab = null,
pub fn format(
self: SymbolAtOffset,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;
try std.fmt.format(writer, "{{ {d}: .offset = {d}", .{ self.local_sym_index, self.offset });
if (self.stab) |stab| {
try std.fmt.format(writer, ", .stab = {any}", .{stab});
}
try std.fmt.format(writer, " }}", .{});
}
};
pub const Stab = union(enum) {
@ -1150,53 +1165,24 @@ pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void {
}
}
pub fn print_this(self: *const TextBlock, macho_file: MachO) void {
log.warn("TextBlock", .{});
log.warn(" {}: {}", .{ self.local_sym_index, macho_file.locals.items[self.local_sym_index] });
pub fn format(self: TextBlock, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
_ = fmt;
_ = options;
try std.fmt.format(writer, "TextBlock {{ ", .{});
try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
try std.fmt.format(writer, ".aliases = {any}, ", .{self.aliases.items});
try std.fmt.format(writer, ".contained = {any}, ", .{self.contained.items});
try std.fmt.format(writer, ".code = {*}, ", .{self.code.items});
try std.fmt.format(writer, ".size = {d}, ", .{self.size});
try std.fmt.format(writer, ".alignment = {d}, ", .{self.alignment});
try std.fmt.format(writer, ".relocs = {any}, ", .{self.relocs.items});
try std.fmt.format(writer, ".rebases = {any}, ", .{self.rebases.items});
try std.fmt.format(writer, ".bindings = {any}, ", .{self.bindings.items});
try std.fmt.format(writer, ".dices = {any}, ", .{self.dices.items});
if (self.stab) |stab| {
log.warn(" stab: {}", .{stab});
try std.fmt.format(writer, ".stab = {any}, ", .{stab});
}
if (self.aliases.items.len > 0) {
log.warn(" aliases: {any}", .{self.aliases.items});
}
if (self.references.count() > 0) {
log.warn(" references: {any}", .{self.references.keys()});
}
if (self.contained) |contained| {
log.warn(" contained symbols:", .{});
for (contained) |sym_at_off| {
if (sym_at_off.stab) |stab| {
log.warn(" {}: {}, stab: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index, stab });
} else {
log.warn(" {}: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index });
}
}
}
log.warn(" code.len = {}", .{self.code.items.len});
if (self.relocs.items.len > 0) {
log.warn(" relocations:", .{});
for (self.relocs.items) |rel| {
log.warn(" {}", .{rel});
}
}
if (self.rebases.items.len > 0) {
log.warn(" rebases: {any}", .{self.rebases.items});
}
if (self.bindings.items.len > 0) {
log.warn(" bindings: {any}", .{self.bindings.items});
}
if (self.dices.items.len > 0) {
log.warn(" dices: {any}", .{self.dices.items});
}
log.warn(" size = {}", .{self.size});
log.warn(" align = {}", .{self.alignment});
}
pub fn print(self: *const TextBlock, macho_file: MachO) void {
if (self.prev) |prev| {
prev.print(macho_file);
}
self.print_this(macho_file);
try std.fmt.format(writer, "}}", .{});
}
const RelocIterator = struct {