macho: move helper functions to libstd

Helper functions such as `commands.sectionName`, etc. should really
belong in `std.macho.section_64` extern struct.
This commit is contained in:
Jakub Konka 2021-12-10 11:46:44 +01:00
parent 77836e08a2
commit 81e7d8505c
6 changed files with 86 additions and 117 deletions

View File

@ -1,3 +1,5 @@
const std = @import("std");
pub const mach_header = extern struct {
magic: u32,
cputype: cpu_type_t,
@ -9,14 +11,14 @@ pub const mach_header = extern struct {
};
pub const mach_header_64 = extern struct {
magic: u32,
cputype: cpu_type_t,
cpusubtype: cpu_subtype_t,
filetype: u32,
ncmds: u32,
sizeofcmds: u32,
flags: u32,
reserved: u32,
magic: u32 = MH_MAGIC_64,
cputype: cpu_type_t = 0,
cpusubtype: cpu_subtype_t = 0,
filetype: u32 = 0,
ncmds: u32 = 0,
sizeofcmds: u32 = 0,
flags: u32 = 0,
reserved: u32 = 0,
};
pub const fat_header = extern struct {
@ -630,6 +632,10 @@ pub const segment_command_64 = extern struct {
/// number of sections in segment
nsects: u32 = 0,
flags: u32 = 0,
pub fn segName(seg: segment_command_64) []const u8 {
return parseName(&seg.segname);
}
};
/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
@ -728,8 +734,46 @@ pub const section_64 = extern struct {
/// reserved
reserved3: u32 = 0,
pub fn sectName(sect: section_64) []const u8 {
return parseName(&sect.sectname);
}
pub fn segName(sect: section_64) []const u8 {
return parseName(&sect.segname);
}
pub fn type_(sect: section_64) u8 {
return @truncate(u8, sect.flags & 0xff);
}
pub fn attrs(sect: section_64) u32 {
return sect.flags & 0xffffff00;
}
pub fn isCode(sect: section_64) bool {
const attr = sect.attrs();
return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
}
pub fn isDebug(sect: section_64) bool {
return sect.attrs() & S_ATTR_DEBUG != 0;
}
pub fn isDontDeadStrip(sect: section_64) bool {
return sect.attrs() & S_ATTR_NO_DEAD_STRIP != 0;
}
pub fn isDontDeadStripIfReferencesLive(sect: section_64) bool {
return sect.attrs() & S_ATTR_LIVE_SUPPORT != 0;
}
};
fn parseName(name: *const [16]u8) []const u8 {
const len = std.mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
return name[0..len];
}
pub const nlist = extern struct {
n_strx: u32,
n_type: u8,

View File

@ -1324,10 +1324,10 @@ pub const MatchingSection = struct {
};
pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
const segname = commands.segmentName(sect);
const sectname = commands.sectionName(sect);
const segname = sect.segName();
const sectname = sect.sectName();
const res: ?MatchingSection = blk: {
switch (commands.sectionType(sect)) {
switch (sect.type_()) {
macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
if (self.text_const_section_index == null) {
self.text_const_section_index = try self.initSection(
@ -1579,7 +1579,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
};
},
macho.S_REGULAR => {
if (commands.sectionIsCode(sect)) {
if (sect.isCode()) {
if (self.text_section_index == null) {
self.text_section_index = try self.initSection(
self.text_segment_cmd_index.?,
@ -1599,7 +1599,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
.sect = self.text_section_index.?,
};
}
if (commands.sectionIsDebug(sect)) {
if (sect.isDebug()) {
// TODO debug attributes
if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
@ -1889,10 +1889,7 @@ fn allocateLocals(self: *MachO) !void {
const sect = seg.sections.items[match.sect];
var base_vaddr = sect.addr;
log.debug("allocating local symbols in {s},{s}", .{
commands.segmentName(sect),
commands.sectionName(sect),
});
log.debug("allocating local symbols in {s},{s}", .{ sect.segName(), sect.sectName() });
while (true) {
const alignment = try math.powi(u32, 2, atom.alignment);
@ -1987,7 +1984,7 @@ fn writeAllAtoms(self: *MachO) !void {
defer buffer.deinit();
try buffer.ensureTotalCapacity(try math.cast(usize, sect.size));
log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
while (atom.prev) |prev| {
atom = prev;
@ -2035,7 +2032,7 @@ fn writeAtoms(self: *MachO) !void {
const sect = seg.sections.items[match.sect];
var atom: *Atom = entry.value_ptr.*;
log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
while (atom.prev) |prev| {
atom = prev;
@ -3005,7 +3002,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
};
}
log.debug("{s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
while (true) {
const alignment = try math.powi(u32, 2, atom.alignment);
@ -3049,8 +3046,8 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
const seg = &self.load_commands.items[match.seg].Segment;
const sect = &seg.sections.items[match.sect];
log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
commands.segmentName(sect.*),
commands.sectionName(sect.*),
sect.segName(),
sect.sectName(),
metadata.size,
metadata.alignment,
});
@ -4507,8 +4504,8 @@ fn initSection(
const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
commands.segmentName(sect),
commands.sectionName(sect),
sect.segName(),
sect.sectName(),
off,
off + size,
});
@ -4596,8 +4593,8 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
moved_sect.addr += offset_amt;
log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
commands.segmentName(moved_sect.*),
commands.sectionName(moved_sect.*),
moved_sect.segName(),
moved_sect.sectName(),
moved_sect.offset,
moved_sect.offset + moved_sect.size,
moved_sect.addr,
@ -4670,8 +4667,8 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
moved_sect.addr += offset_amt;
log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
commands.segmentName(moved_sect.*),
commands.sectionName(moved_sect.*),
moved_sect.segName(),
moved_sect.sectName(),
moved_sect.offset,
moved_sect.offset + moved_sect.size,
moved_sect.addr,
@ -5784,9 +5781,8 @@ fn writeLoadCommands(self: *MachO) !void {
/// Writes Mach-O file header.
fn writeHeader(self: *MachO) !void {
var header = commands.emptyHeader(.{
.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL,
});
var header: macho.mach_header_64 = .{};
header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
switch (self.base.options.target.cpu.arch) {
.aarch64 => {
@ -5961,10 +5957,7 @@ fn snapshotState(self: *MachO) !void {
for (self.section_ordinals.keys()) |key| {
const seg = self.load_commands.items[key.seg].Segment;
const sect = seg.sections.items[key.sect];
const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{
commands.segmentName(sect),
commands.sectionName(sect),
});
const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
try nodes.append(.{
.address = sect.addr,
.tag = .section_start,
@ -6037,7 +6030,7 @@ fn snapshotState(self: *MachO) !void {
const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
const match_seg = self.load_commands.items[match.seg].Segment;
const match_sect = match_seg.sections.items[match.sect];
break :is_tlv commands.sectionType(match_sect) == macho.S_THREAD_LOCAL_VARIABLES;
break :is_tlv match_sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
};
if (is_tlv) {
const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
@ -6206,8 +6199,8 @@ fn logSectionOrdinals(self: MachO) void {
i + 1,
match.seg,
match.sect,
commands.segmentName(sect),
commands.sectionName(sect),
sect.segName(),
sect.sectName(),
});
}
}

View File

@ -4,7 +4,6 @@ const std = @import("std");
const build_options = @import("build_options");
const aarch64 = @import("../../arch/aarch64/bits.zig");
const assert = std.debug.assert;
const commands = @import("commands.zig");
const log = std.log.scoped(.link);
const macho = std.macho;
const math = std.math;
@ -492,7 +491,7 @@ fn addPtrBindingOrRebase(
const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
const seg = context.macho_file.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
const sect_type = commands.sectionType(sect);
const sect_type = sect.type_();
const should_rebase = rebase: {
if (rel.r_length != 3) break :rebase false;
@ -707,7 +706,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
const seg = macho_file.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
};
if (is_tlv) {
// For TLV relocations, the value specified as a relocation is the displacement from the

View File

@ -241,8 +241,8 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
commands.segmentName(sect),
commands.sectionName(sect),
sect.segName(),
sect.sectName(),
off,
off + size,
});
@ -670,9 +670,8 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
}
fn writeHeader(self: *DebugSymbols) !void {
var header = commands.emptyHeader(.{
.filetype = macho.MH_DSYM,
});
var header: macho.mach_header_64 = .{};
header.filetype = macho.MH_DSYM;
switch (self.base.base.options.target.cpu.arch) {
.aarch64 => {

View File

@ -11,14 +11,11 @@ const macho = std.macho;
const math = std.math;
const mem = std.mem;
const sort = std.sort;
const commands = @import("commands.zig");
const segmentName = commands.segmentName;
const sectionName = commands.sectionName;
const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
const LoadCommand = commands.LoadCommand;
const LoadCommand = @import("commands.zig").LoadCommand;
const MachO = @import("../MachO.zig");
file: fs.File,
@ -278,8 +275,8 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
var seg = cmd.Segment;
for (seg.sections.items) |*sect, j| {
const index = @intCast(u16, j);
const segname = segmentName(sect.*);
const sectname = sectionName(sect.*);
const segname = sect.segName();
const sectname = sect.sectName();
if (mem.eql(u8, segname, "__DWARF")) {
if (mem.eql(u8, sectname, "__debug_info")) {
self.dwarf_debug_info_index = index;
@ -424,10 +421,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
for (seg.sections.items) |sect, id| {
const sect_id = @intCast(u8, id);
log.debug("putting section '{s},{s}' as an Atom", .{
segmentName(sect),
sectionName(sect),
});
log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });
// Get matching segment/section in the final artifact.
const match = (try macho_file.getMatchingSection(sect)) orelse {
@ -479,7 +473,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align");
const is_zerofill = blk: {
const section_type = commands.sectionType(sect);
const section_type = sect.type_();
break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
};
if (!is_zerofill) {

View File

@ -12,28 +12,6 @@ const MachO = @import("../MachO.zig");
const makeStaticString = MachO.makeStaticString;
const padToIdeal = MachO.padToIdeal;
pub const HeaderArgs = struct {
magic: u32 = macho.MH_MAGIC_64,
cputype: macho.cpu_type_t = 0,
cpusubtype: macho.cpu_subtype_t = 0,
filetype: u32 = 0,
flags: u32 = 0,
reserved: u32 = 0,
};
pub fn emptyHeader(args: HeaderArgs) macho.mach_header_64 {
return .{
.magic = args.magic,
.cputype = args.cputype,
.cpusubtype = args.cpusubtype,
.filetype = args.filetype,
.ncmds = 0,
.sizeofcmds = 0,
.flags = args.flags,
.reserved = args.reserved,
};
}
pub const LoadCommand = union(enum) {
Segment: SegmentCommand,
DyldInfoOnly: macho.dyld_info_command,
@ -357,44 +335,6 @@ pub fn createLoadDylibCommand(
return dylib_cmd;
}
fn parseName(name: *const [16]u8) []const u8 {
const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
return name[0..len];
}
pub fn segmentName(sect: macho.section_64) []const u8 {
return parseName(&sect.segname);
}
pub fn sectionName(sect: macho.section_64) []const u8 {
return parseName(&sect.sectname);
}
pub fn sectionType(sect: macho.section_64) u8 {
return @truncate(u8, sect.flags & 0xff);
}
pub fn sectionAttrs(sect: macho.section_64) u32 {
return sect.flags & 0xffffff00;
}
pub fn sectionIsCode(sect: macho.section_64) bool {
const attr = sectionAttrs(sect);
return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0;
}
pub fn sectionIsDebug(sect: macho.section_64) bool {
return sectionAttrs(sect) & macho.S_ATTR_DEBUG != 0;
}
pub fn sectionIsDontDeadStrip(sect: macho.section_64) bool {
return sectionAttrs(sect) & macho.S_ATTR_NO_DEAD_STRIP != 0;
}
pub fn sectionIsDontDeadStripIfReferencesLive(sect: macho.section_64) bool {
return sectionAttrs(sect) & macho.S_ATTR_LIVE_SUPPORT != 0;
}
fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
var stream = io.fixedBufferStream(buffer);
var given = try LoadCommand.read(allocator, stream.reader());