macho: fix allocating sections within segment when parsing objects

This commit is contained in:
Jakub Konka
2021-08-31 23:05:01 +02:00
parent 2831d6e9b8
commit 50db993119
4 changed files with 552 additions and 604 deletions

View File

@@ -601,35 +601,35 @@ pub const segment_command = extern struct {
/// command and their size is reflected in cmdsize.
pub const segment_command_64 = extern struct {
/// LC_SEGMENT_64
cmd: u32,
cmd: u32 = LC_SEGMENT_64,
/// includes sizeof section_64 structs
cmdsize: u32,
cmdsize: u32 = @sizeOf(segment_command_64),
/// segment name
segname: [16]u8,
/// memory address of this segment
vmaddr: u64,
vmaddr: u64 = 0,
/// memory size of this segment
vmsize: u64,
vmsize: u64 = 0,
/// file offset of this segment
fileoff: u64,
fileoff: u64 = 0,
/// amount to map from the file
filesize: u64,
filesize: u64 = 0,
/// maximum VM protection
maxprot: vm_prot_t,
maxprot: vm_prot_t = VM_PROT_NONE,
/// initial VM protection
initprot: vm_prot_t,
initprot: vm_prot_t = VM_PROT_NONE,
/// number of sections in segment
nsects: u32,
flags: u32,
nsects: u32 = 0,
flags: u32 = 0,
};
/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
@@ -700,34 +700,34 @@ pub const section_64 = extern struct {
segname: [16]u8,
/// memory address of this section
addr: u64,
addr: u64 = 0,
/// size in bytes of this section
size: u64,
size: u64 = 0,
/// file offset of this section
offset: u32,
offset: u32 = 0,
/// section alignment (power of 2)
@"align": u32,
@"align": u32 = 0,
/// file offset of relocation entries
reloff: u32,
reloff: u32 = 0,
/// number of relocation entries
nreloc: u32,
nreloc: u32 = 0,
/// flags (section type and attributes
flags: u32,
flags: u32 = S_REGULAR,
/// reserved (for offset or index)
reserved1: u32,
reserved1: u32 = 0,
/// reserved (for count or sizeof)
reserved2: u32,
reserved2: u32 = 0,
/// reserved
reserved3: u32,
reserved3: u32 = 0,
};
pub const nlist = extern struct {

File diff suppressed because it is too large Load Diff

View File

@@ -5,23 +5,26 @@ const assert = std.debug.assert;
const fs = std.fs;
const log = std.log.scoped(.dsym);
const macho = std.macho;
const math = std.math;
const mem = std.mem;
const DW = std.dwarf;
const leb = std.leb;
const Allocator = mem.Allocator;
const build_options = @import("build_options");
const commands = @import("commands.zig");
const trace = @import("../../tracy.zig").trace;
const LoadCommand = commands.LoadCommand;
const Module = @import("../../Module.zig");
const Type = @import("../../type.zig").Type;
const link = @import("../../link.zig");
const MachO = @import("../MachO.zig");
const SrcFn = MachO.SrcFn;
const TextBlock = MachO.TextBlock;
const SegmentCommand = commands.SegmentCommand;
const SrcFn = MachO.SrcFn;
const makeStaticString = MachO.makeStaticString;
const padToIdeal = MachO.padToIdeal;
usingnamespace @import("commands.zig");
const page_size: u16 = 0x1000;
base: *MachO,
@@ -185,107 +188,86 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
try self.load_commands.append(allocator, .{
.Segment = SegmentCommand.empty("__DWARF", .{
.vmaddr = vmaddr,
.vmsize = needed_size,
.fileoff = off,
.filesize = needed_size,
}),
.Segment = .{
.inner = .{
.segname = makeStaticString("__DWARF"),
.vmaddr = vmaddr,
.vmsize = needed_size,
.fileoff = off,
.filesize = needed_size,
},
},
});
self.load_commands_dirty = true;
}
if (self.debug_str_section_index == null) {
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len);
assert(self.debug_string_table.items.len == 0);
try dwarf_segment.addSection(allocator, "__debug_str", .{
.addr = dwarf_segment.inner.vmaddr,
.size = @intCast(u32, self.debug_string_table.items.len),
.offset = @intCast(u32, dwarf_segment.inner.fileoff),
.@"align" = 1,
});
self.load_commands_dirty = true;
self.debug_str_section_index = try self.allocateSection(
"__debug_str",
@intCast(u32, self.debug_string_table.items.len),
0,
);
self.debug_string_table_dirty = true;
}
if (self.debug_info_section_index == null) {
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
self.debug_info_section_index = @intCast(u16, dwarf_segment.sections.items.len);
const file_size_hint = 200;
const p_align = 1;
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
log.debug("found __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
try dwarf_segment.addSection(allocator, "__debug_info", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
});
self.load_commands_dirty = true;
self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0);
self.debug_info_header_dirty = true;
}
if (self.debug_abbrev_section_index == null) {
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
self.debug_abbrev_section_index = @intCast(u16, dwarf_segment.sections.items.len);
const file_size_hint = 128;
const p_align = 1;
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
log.debug("found __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
try dwarf_segment.addSection(allocator, "__debug_abbrev", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
});
self.load_commands_dirty = true;
self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0);
self.debug_abbrev_section_dirty = true;
}
if (self.debug_aranges_section_index == null) {
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
self.debug_aranges_section_index = @intCast(u16, dwarf_segment.sections.items.len);
const file_size_hint = 160;
const p_align = 16;
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
log.debug("found __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
try dwarf_segment.addSection(allocator, "__debug_aranges", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
});
self.load_commands_dirty = true;
self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4);
self.debug_aranges_section_dirty = true;
}
if (self.debug_line_section_index == null) {
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
self.debug_line_section_index = @intCast(u16, dwarf_segment.sections.items.len);
const file_size_hint = 250;
const p_align = 1;
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
log.debug("found __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
try dwarf_segment.addSection(allocator, "__debug_line", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
});
self.load_commands_dirty = true;
self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
self.debug_line_header_dirty = true;
}
}
fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
var sect = macho.section_64{
.sectname = makeStaticString(sectname),
.segname = seg.inner.segname,
.size = @intCast(u32, size),
.@"align" = alignment,
};
const alignment_pow_2 = try math.powi(u32, 2, alignment);
const off = seg.findFreeSpace(size, alignment_pow_2, null);
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),
off,
off + size,
});
sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
sect.offset = @intCast(u32, off);
const index = @intCast(u16, seg.sections.items.len);
try seg.sections.append(self.base.base.allocator, sect);
seg.inner.cmdsize += @sizeOf(macho.section_64);
seg.inner.nsects += 1;
// TODO
// const match = MatchingSection{
// .seg = segment_id,
// .sect = index,
// };
// _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
// try self.block_free_lists.putNoClobber(self.base.allocator, match, .{});
self.load_commands_dirty = true;
return index;
}
pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {
// TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
// Zig source code.
@@ -611,15 +593,18 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
}
fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
var cmd = SegmentCommand.empty("", .{
.cmdsize = base_cmd.inner.cmdsize,
.vmaddr = base_cmd.inner.vmaddr,
.vmsize = base_cmd.inner.vmsize,
.maxprot = base_cmd.inner.maxprot,
.initprot = base_cmd.inner.initprot,
.nsects = base_cmd.inner.nsects,
.flags = base_cmd.inner.flags,
});
var cmd = SegmentCommand{
.inner = .{
.segname = undefined,
.cmdsize = base_cmd.inner.cmdsize,
.vmaddr = base_cmd.inner.vmaddr,
.vmsize = base_cmd.inner.vmsize,
.maxprot = base_cmd.inner.maxprot,
.initprot = base_cmd.inner.initprot,
.nsects = base_cmd.inner.nsects,
.flags = base_cmd.inner.flags,
},
};
mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects);
@@ -689,7 +674,7 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {
}
fn writeHeader(self: *DebugSymbols) !void {
var header = emptyHeader(.{
var header = commands.emptyHeader(.{
.filetype = macho.MH_DSYM,
});

View File

@@ -9,6 +9,7 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const MachO = @import("../MachO.zig");
const makeStaticString = MachO.makeStaticString;
const padToIdeal = MachO.padToIdeal;
pub const HeaderArgs = struct {
@@ -217,75 +218,6 @@ pub const SegmentCommand = struct {
inner: macho.segment_command_64,
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
const SegmentOptions = struct {
cmdsize: u32 = @sizeOf(macho.segment_command_64),
vmaddr: u64 = 0,
vmsize: u64 = 0,
fileoff: u64 = 0,
filesize: u64 = 0,
maxprot: macho.vm_prot_t = macho.VM_PROT_NONE,
initprot: macho.vm_prot_t = macho.VM_PROT_NONE,
nsects: u32 = 0,
flags: u32 = 0,
};
pub fn empty(comptime segname: []const u8, opts: SegmentOptions) SegmentCommand {
return .{
.inner = .{
.cmd = macho.LC_SEGMENT_64,
.cmdsize = opts.cmdsize,
.segname = makeStaticString(segname),
.vmaddr = opts.vmaddr,
.vmsize = opts.vmsize,
.fileoff = opts.fileoff,
.filesize = opts.filesize,
.maxprot = opts.maxprot,
.initprot = opts.initprot,
.nsects = opts.nsects,
.flags = opts.flags,
},
};
}
const SectionOptions = struct {
addr: u64 = 0,
size: u64 = 0,
offset: u32 = 0,
@"align": u32 = 0,
reloff: u32 = 0,
nreloc: u32 = 0,
flags: u32 = macho.S_REGULAR,
reserved1: u32 = 0,
reserved2: u32 = 0,
reserved3: u32 = 0,
};
pub fn addSection(
self: *SegmentCommand,
alloc: *Allocator,
comptime sectname: []const u8,
opts: SectionOptions,
) !void {
var section = macho.section_64{
.sectname = makeStaticString(sectname),
.segname = undefined,
.addr = opts.addr,
.size = opts.size,
.offset = opts.offset,
.@"align" = opts.@"align",
.reloff = opts.reloff,
.nreloc = opts.nreloc,
.flags = opts.flags,
.reserved1 = opts.reserved1,
.reserved2 = opts.reserved2,
.reserved3 = opts.reserved3,
};
mem.copy(u8, &section.segname, &self.inner.segname);
try self.sections.append(alloc, section);
self.inner.cmdsize += @sizeOf(macho.section_64);
self.inner.nsects += 1;
}
pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
const inner = try reader.readStruct(macho.segment_command_64);
var segment = SegmentCommand{
@@ -427,13 +359,6 @@ pub fn createLoadDylibCommand(
return dylib_cmd;
}
fn makeStaticString(bytes: []const u8) [16]u8 {
var buf = [_]u8{0} ** 16;
assert(bytes.len <= buf.len);
mem.copy(u8, &buf, bytes);
return buf;
}
fn parseName(name: *const [16]u8) []const u8 {
const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
return name[0..len];
@@ -513,34 +438,26 @@ test "read-write segment command" {
0x00, 0x00, 0x00, 0x00, // reserved3
};
var cmd = SegmentCommand{
.inner = .{
.cmd = macho.LC_SEGMENT_64,
.inner = macho.segment_command_64.new(.{
.cmdsize = 152,
.segname = makeStaticString("__TEXT"),
.vmaddr = 4294967296,
.vmsize = 294912,
.fileoff = 0,
.filesize = 294912,
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,
.initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,
.nsects = 1,
.flags = 0,
},
}),
};
try cmd.sections.append(gpa, .{
try cmd.sections.append(gpa, macho.section_64.new(.{
.sectname = makeStaticString("__text"),
.segname = makeStaticString("__TEXT"),
.addr = 4294983680,
.size = 448,
.offset = 16384,
.@"align" = 2,
.reloff = 0,
.nreloc = 0,
.flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
.reserved1 = 0,
.reserved2 = 0,
.reserved3 = 0,
});
}));
defer cmd.deinit(gpa);
try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });