Skip to content

Commit

Permalink
macho: store open file descriptors globally
Browse files Browse the repository at this point in the history
This means we hand out indexes into this array to object files
thereby allowing a single file descriptor per archive rather than
be required to clone it per each object file that resides within.
  • Loading branch information
kubkon committed Feb 5, 2024
1 parent c184cf0 commit b39baa4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 26 deletions.
28 changes: 25 additions & 3 deletions src/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal_object_index: ?File.Index = null,
objects: std.ArrayListUnmanaged(File.Index) = .{},
dylibs: std.ArrayListUnmanaged(File.Index) = .{},
files: std.MultiArrayList(File.Entry) = .{},
file_handles: std.ArrayListUnmanaged(std.fs.File) = .{},

segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
sections: std.MultiArrayList(Section) = .{},
Expand Down Expand Up @@ -110,6 +111,11 @@ fn createEmpty(gpa: Allocator, options: Options, thread_pool: *ThreadPool) !*Mac
pub fn deinit(self: *MachO) void {
const gpa = self.base.allocator;

for (self.file_handles.items) |file| {
file.close();
}
self.file_handles.deinit(gpa);

self.symbols.deinit(gpa);
self.symbols_extra.deinit(gpa);
self.globals.deinit(gpa);
Expand Down Expand Up @@ -667,6 +673,8 @@ fn parseObject(self: *MachO, obj: LinkObject) !bool {

const gpa = self.base.allocator;
const file = try std.fs.cwd().openFile(obj.path, .{});
errdefer file.close();
const fh = try self.addFileHandle(file);

const header = file.reader().readStruct(macho.mach_header_64) catch return false;
try file.seekTo(0);
Expand All @@ -681,7 +689,7 @@ fn parseObject(self: *MachO, obj: LinkObject) !bool {
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .object = .{
.path = try gpa.dupe(u8, obj.path),
.file = file,
.file_handle = fh,
.index = index,
.mtime = mtime,
} });
Expand All @@ -700,7 +708,8 @@ fn parseArchive(self: *MachO, obj: LinkObject) !bool {

const gpa = self.base.allocator;
const file = try std.fs.cwd().openFile(obj.path, .{});
defer file.close();
errdefer file.close();
const fh = try self.addFileHandle(file);

const fat_arch: ?fat.Arch = if (fat.isFatLibrary(file)) blk: {
break :blk self.parseFatLibrary(obj.path, file) catch |err| switch (err) {
Expand All @@ -717,7 +726,7 @@ fn parseArchive(self: *MachO, obj: LinkObject) !bool {

var archive = Archive{};
defer archive.deinit(gpa);
try archive.parse(self, obj.path, file, fat_arch);
try archive.parse(self, obj.path, fh, fat_arch);

var has_parse_error = false;
for (archive.objects.items) |extracted| {
Expand Down Expand Up @@ -2756,6 +2765,19 @@ pub fn getInternalObject(self: *MachO) ?*InternalObject {
return self.getFile(index).?.internal;
}

pub fn addFileHandle(self: *MachO, file: std.fs.File) !u32 {
const gpa = self.base.allocator;
const index: u32 = @intCast(self.file_handles.items.len);
const fh = try self.file_handles.addOne(gpa);
fh.* = file;
return index;
}

pub fn getFileHandle(self: MachO, index: u32) std.fs.File {
assert(index < self.file_handles.items.len);
return self.file_handles.items[index];
}

pub fn addAtom(self: *MachO) !Atom.Index {
const index = @as(Atom.Index, @intCast(self.atoms.items.len));
const atom = try self.atoms.addOne(self.base.allocator);
Expand Down
5 changes: 3 additions & 2 deletions src/MachO/Archive.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
self.objects.deinit(allocator);
}

pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, file: std.fs.File, fat_arch: ?fat.Arch) !void {
pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, file_handle: u32, fat_arch: ?fat.Arch) !void {
const gpa = macho_file.base.allocator;

const file = macho_file.getFileHandle(file_handle);
const offset = if (fat_arch) |ar| ar.offset else 0;
const size = if (fat_arch) |ar| ar.size else (try file.stat()).size;
try file.seekTo(offset);
Expand Down Expand Up @@ -116,7 +117,7 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, file: std.fs.
.offset = offset + pos,
},
.path = name,
.file = try std.fs.cwd().openFile(path, .{}),
.file_handle = file_handle,
.index = undefined,
.alive = false,
.mtime = hdr.date() catch 0,
Expand Down
3 changes: 2 additions & 1 deletion src/MachO/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ pub fn getCode(self: Atom, macho_file: *MachO, buffer: []u8) !void {
.dylib => unreachable,
.object => |x| {
const slice = x.sections.slice();
const file = macho_file.getFileHandle(x.file_handle);
const offset = if (x.archive) |ar| ar.offset else 0;
const sect = slice.items(.header)[self.n_sect];
const amt = try x.file.preadAll(buffer, sect.offset + offset + self.off);
const amt = try file.preadAll(buffer, sect.offset + offset + self.off);
if (amt != buffer.len) return error.InputOutput;
},
.internal => |x| {
Expand Down
44 changes: 24 additions & 20 deletions src/MachO/Object.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
archive: ?Archive = null,
path: []const u8,
file: std.fs.File,
file_handle: u32,
mtime: u64,
index: File.Index,

Expand Down Expand Up @@ -38,7 +38,6 @@ const Archive = struct {
};

pub fn deinit(self: *Object, allocator: Allocator) void {
self.file.close();
allocator.free(self.path);
if (self.archive) |*ar| allocator.free(ar.path);
for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
Expand Down Expand Up @@ -70,18 +69,19 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {

const gpa = macho_file.base.allocator;
const offset = if (self.archive) |ar| ar.offset else 0;
const file = macho_file.getFileHandle(self.file_handle);

var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
{
const amt = try self.file.preadAll(&header_buffer, offset);
const amt = try file.preadAll(&header_buffer, offset);
if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
}
self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;

const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
defer gpa.free(lc_buffer);
{
const amt = try self.file.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
const amt = try file.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
if (amt != self.header.?.sizeofcmds) return error.InputOutput;
}

Expand All @@ -108,14 +108,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const cmd = lc.cast(macho.symtab_command).?;
try self.strtab.resize(gpa, cmd.strsize);
{
const amt = try self.file.preadAll(self.strtab.items, cmd.stroff + offset);
const amt = try file.preadAll(self.strtab.items, cmd.stroff + offset);
if (amt != self.strtab.items.len) return error.InputOutput;
}

const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
defer gpa.free(symtab_buffer);
{
const amt = try self.file.preadAll(symtab_buffer, cmd.symoff + offset);
const amt = try file.preadAll(symtab_buffer, cmd.symoff + offset);
if (amt != symtab_buffer.len) return error.InputOutput;
}
const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
Expand All @@ -133,7 +133,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const buffer = try gpa.alloc(u8, cmd.datasize);
defer gpa.free(buffer);
{
const amt = try self.file.preadAll(buffer, offset + cmd.dataoff);
const amt = try file.preadAll(buffer, offset + cmd.dataoff);
if (amt != buffer.len) return error.InputOutput;
}
const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
Expand Down Expand Up @@ -662,7 +662,7 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
const relocs = slice.items(.relocs)[sect_id];

// TODO: read into buffer directly
const data = try self.getSectionData(gpa, sect_id);
const data = try self.getSectionData(sect_id, macho_file);
defer gpa.free(data);

try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
Expand Down Expand Up @@ -765,7 +765,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
};

const gpa = macho_file.base.allocator;
const data = try self.getSectionData(gpa, sect_id);
const data = try self.getSectionData(sect_id, macho_file);
defer gpa.free(data);
const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
Expand Down Expand Up @@ -984,11 +984,11 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {

if (debug_info_index == null or debug_abbrev_index == null) return;

const debug_info = try self.getSectionData(gpa, @intCast(debug_info_index.?));
const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file);
defer gpa.free(debug_info);
const debug_abbrev = try self.getSectionData(gpa, @intCast(debug_abbrev_index.?));
const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file);
defer gpa.free(debug_abbrev);
const debug_str = if (debug_str_index) |index| try self.getSectionData(gpa, @intCast(index)) else &[0]u8{};
const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
defer gpa.free(debug_str);

var dwarf_info = DwarfInfo{};
Expand Down Expand Up @@ -1533,14 +1533,16 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO) void {
}
}

pub fn getSectionData(self: *const Object, allocator: Allocator, index: u32) ![]u8 {
pub fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 {
const gpa = macho_file.base.allocator;
const slice = self.sections.slice();
assert(index < slice.items(.header).len);
const sect = slice.items(.header)[index];
const file = macho_file.getFileHandle(self.file_handle);
const offset = if (self.archive) |ar| ar.offset else 0;
const buffer = try allocator.alloc(u8, sect.size);
errdefer allocator.free(buffer);
const amt = try self.file.preadAll(buffer, sect.offset + offset);
const buffer = try gpa.alloc(u8, sect.size);
errdefer gpa.free(buffer);
const amt = try file.preadAll(buffer, sect.offset + offset);
if (amt != buffer.len) return error.InputOutput;
return buffer;
}
Expand Down Expand Up @@ -1814,16 +1816,17 @@ const x86_64 = struct {
) !void {
const gpa = macho_file.base.allocator;

const file = macho_file.getFileHandle(self.file_handle);
const offset = if (self.archive) |ar| ar.offset else 0;
const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
defer gpa.free(relocs_buffer);
{
const amt = try self.file.preadAll(relocs_buffer, sect.reloff + offset);
const amt = try file.preadAll(relocs_buffer, sect.reloff + offset);
if (amt != relocs_buffer.len) return error.InputOutput;
}
const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];

const code = try self.getSectionData(gpa, @intCast(n_sect));
const code = try self.getSectionData(@intCast(n_sect), macho_file);
defer gpa.free(code);

try out.ensureTotalCapacityPrecise(gpa, relocs.len);
Expand Down Expand Up @@ -1972,16 +1975,17 @@ const aarch64 = struct {
) !void {
const gpa = macho_file.base.allocator;

const file = macho_file.getFileHandle(self.file_handle);
const offset = if (self.archive) |ar| ar.offset else 0;
const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
defer gpa.free(relocs_buffer);
{
const amt = try self.file.preadAll(relocs_buffer, sect.reloff + offset);
const amt = try file.preadAll(relocs_buffer, sect.reloff + offset);
if (amt != relocs_buffer.len) return error.InputOutput;
}
const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];

const code = try self.getSectionData(gpa, @intCast(n_sect));
const code = try self.getSectionData(@intCast(n_sect), macho_file);
defer gpa.free(code);

try out.ensureTotalCapacityPrecise(gpa, relocs.len);
Expand Down

0 comments on commit b39baa4

Please sign in to comment.