authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-18 11:03:26+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-19 12:18:35+01:00
logf33b644c4ff776f9e0947d592ac7a5df452710b6
treeb4eb64c47680f6afaaeab96d7c0cd2bfc6f61769
parentb090451646904006ac41b2b99e532489d89ea837

macho: redo how we allocate within a segment

Firstly, we preallocate segments offset and sizes, and then when adding sections, we find the free space within each segment. Currently, this applies to any segment that is not __LINKEDIT segment since this requires special treatment.

2 files changed, 118 insertions(+), 136 deletions(-)

src/link/MachO.zig+79-135
...@@ -37,6 +37,10 @@ page_size: u16,...@@ -37,6 +37,10 @@ page_size: u16,
3737
38/// Mach-O header38/// Mach-O header
39header: ?macho.mach_header_64 = null,39header: ?macho.mach_header_64 = null,
40/// We commit 0x1000 = 4096 bytes of space to the header and
41/// the table of load commands. This should be plenty for any
42/// potential future extensions.
43header_pad: u16 = 0x1000,
4044
41/// Table of all load commands45/// Table of all load commands
42load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},46load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
...@@ -75,7 +79,7 @@ code_signature_cmd_index: ?u16 = null,...@@ -75,7 +79,7 @@ code_signature_cmd_index: ?u16 = null,
7579
76/// Index into __TEXT,__text section.80/// Index into __TEXT,__text section.
77text_section_index: ?u16 = null,81text_section_index: ?u16 = null,
78/// Index into __TEXT,__got section.82/// Index into __TEXT,__ziggot section.
79got_section_index: ?u16 = null,83got_section_index: ?u16 = null,
80/// The absolute address of the entry point.84/// The absolute address of the entry point.
81entry_addr: ?u64 = null,85entry_addr: ?u64 = null,
...@@ -150,8 +154,8 @@ pub const PieFixup = struct {...@@ -150,8 +154,8 @@ pub const PieFixup = struct {
150};154};
151155
152/// `alloc_num / alloc_den` is the factor of padding when allocating.156/// `alloc_num / alloc_den` is the factor of padding when allocating.
153const alloc_num = 4;157pub const alloc_num = 4;
154const alloc_den = 3;158pub const alloc_den = 3;
155159
156/// Default path to dyld160/// Default path to dyld
157/// TODO instead of hardcoding it, we should probably look through some env vars and search paths161/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
...@@ -1295,15 +1299,23 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1295,15 +1299,23 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1295 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);1299 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1296 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;1300 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1297 const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;1301 const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
1302
1303 const program_code_size_hint = self.base.options.program_code_size_hint;
1304 const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
1305 const ideal_size = self.header_pad + program_code_size_hint + offset_table_size_hint;
1306 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);
1307
1308 log.debug("found __TEXT segment free space 0x{x} to 0x{x}\n", .{ 0, needed_size });
1309
1298 try self.load_commands.append(self.base.allocator, .{1310 try self.load_commands.append(self.base.allocator, .{
1299 .Segment = SegmentCommand.empty(.{1311 .Segment = SegmentCommand.empty(.{
1300 .cmd = macho.LC_SEGMENT_64,1312 .cmd = macho.LC_SEGMENT_64,
1301 .cmdsize = @sizeOf(macho.segment_command_64),1313 .cmdsize = @sizeOf(macho.segment_command_64),
1302 .segname = makeStaticString("__TEXT"),1314 .segname = makeStaticString("__TEXT"),
1303 .vmaddr = 0x100000000, // always starts at 4GB1315 .vmaddr = 0x100000000, // always starts at 4GB
1304 .vmsize = 0,1316 .vmsize = needed_size,
1305 .fileoff = 0,1317 .fileoff = 0,
1306 .filesize = 0,1318 .filesize = needed_size,
1307 .maxprot = maxprot,1319 .maxprot = maxprot,
1308 .initprot = initprot,1320 .initprot = initprot,
1309 .nsects = 0,1321 .nsects = 0,
...@@ -1316,31 +1328,31 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1316,31 +1328,31 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1316 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1328 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1317 self.text_section_index = @intCast(u16, text_segment.sections.items.len);1329 self.text_section_index = @intCast(u16, text_segment.sections.items.len);
13181330
1319 const program_code_size_hint = self.base.options.program_code_size_hint;1331 const alignment: u32 = switch (self.base.options.target.cpu.arch) {
1320 const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size);1332 .x86_64 => 0,
1321 const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly?1333 .aarch64 => 2,
1334 else => unreachable, // unhandled architecture type
1335 };
1336 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1337 const needed_size = self.base.options.program_code_size_hint;
1338 const off = self.header_pad;
13221339
1323 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });1340 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });
13241341
1325 try text_segment.sections.append(self.base.allocator, .{1342 try text_segment.addSection(self.base.allocator, .{
1326 .sectname = makeStaticString("__text"),1343 .sectname = makeStaticString("__text"),
1327 .segname = makeStaticString("__TEXT"),1344 .segname = makeStaticString("__TEXT"),
1328 .addr = text_segment.inner.vmaddr + off,1345 .addr = text_segment.inner.vmaddr + off,
1329 .size = file_size,1346 .size = @intCast(u32, needed_size),
1330 .offset = off,1347 .offset = off,
1331 .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_641348 .@"align" = alignment,
1332 .reloff = 0,1349 .reloff = 0,
1333 .nreloc = 0,1350 .nreloc = 0,
1334 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,1351 .flags = flags,
1335 .reserved1 = 0,1352 .reserved1 = 0,
1336 .reserved2 = 0,1353 .reserved2 = 0,
1337 .reserved3 = 0,1354 .reserved3 = 0,
1338 });1355 });
1339
1340 text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
1341 text_segment.inner.filesize = file_size + off;
1342 text_segment.inner.cmdsize += @sizeOf(macho.section_64);
1343 text_segment.inner.nsects += 1;
1344 self.cmd_table_dirty = true;1356 self.cmd_table_dirty = true;
1345 }1357 }
1346 if (self.got_section_index == null) {1358 if (self.got_section_index == null) {
...@@ -1348,48 +1360,48 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1348,48 +1360,48 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1348 const text_section = &text_segment.sections.items[self.text_section_index.?];1360 const text_section = &text_segment.sections.items[self.text_section_index.?];
1349 self.got_section_index = @intCast(u16, text_segment.sections.items.len);1361 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
13501362
1351 const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1363 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1352 // TODO looking for free space should be done *within* a segment it belongs to1364 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1353 const off = @intCast(u32, text_section.offset + text_section.size);1365 const off = text_segment.findFreeSpace(needed_size, @sizeOf(u64));
13541366
1355 log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });1367 // TODO Audit this. Is it possible to not find free space? We need to grow __TEXT then.
1368 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize);
13561369
1357 try text_segment.sections.append(self.base.allocator, .{1370 log.debug("found __ziggot section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });
1358 .sectname = makeStaticString("__got"),1371
1372 try text_segment.addSection(self.base.allocator, .{
1373 .sectname = makeStaticString("__ziggot"),
1359 .segname = makeStaticString("__TEXT"),1374 .segname = makeStaticString("__TEXT"),
1360 .addr = text_section.addr + text_section.size,1375 .addr = text_segment.inner.vmaddr + off,
1361 .size = file_size,1376 .size = needed_size,
1362 .offset = off,1377 .offset = off,
1363 .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0,1378 .@"align" = @sizeOf(u64),
1364 .reloff = 0,1379 .reloff = 0,
1365 .nreloc = 0,1380 .nreloc = 0,
1366 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,1381 .flags = flags,
1367 .reserved1 = 0,1382 .reserved1 = 0,
1368 .reserved2 = 0,1383 .reserved2 = 0,
1369 .reserved3 = 0,1384 .reserved3 = 0,
1370 });1385 });
1371
1372 const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size);
1373 text_segment.inner.vmsize += added_size;
1374 text_segment.inner.filesize += added_size;
1375 text_segment.inner.cmdsize += @sizeOf(macho.section_64);
1376 text_segment.inner.nsects += 1;
1377 self.cmd_table_dirty = true;1386 self.cmd_table_dirty = true;
1378 }1387 }
1379 if (self.linkedit_segment_cmd_index == null) {1388 if (self.linkedit_segment_cmd_index == null) {
1380 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);1389 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1381 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1390
1382 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;1391 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1383 const initprot = macho.VM_PROT_READ;1392 const initprot = macho.VM_PROT_READ;
1384 const off = text_segment.inner.fileoff + text_segment.inner.filesize;1393 const address_and_offset = self.nextSegmentAddressAndOffset();
1394
1395 log.debug("found __LINKEDIT segment free space at 0x{x}\n", .{address_and_offset.offset});
1396
1385 try self.load_commands.append(self.base.allocator, .{1397 try self.load_commands.append(self.base.allocator, .{
1386 .Segment = SegmentCommand.empty(.{1398 .Segment = SegmentCommand.empty(.{
1387 .cmd = macho.LC_SEGMENT_64,1399 .cmd = macho.LC_SEGMENT_64,
1388 .cmdsize = @sizeOf(macho.segment_command_64),1400 .cmdsize = @sizeOf(macho.segment_command_64),
1389 .segname = makeStaticString("__LINKEDIT"),1401 .segname = makeStaticString("__LINKEDIT"),
1390 .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize,1402 .vmaddr = address_and_offset.address,
1391 .vmsize = 0,1403 .vmsize = 0,
1392 .fileoff = off,1404 .fileoff = address_and_offset.offset,
1393 .filesize = 0,1405 .filesize = 0,
1394 .maxprot = maxprot,1406 .maxprot = maxprot,
1395 .initprot = initprot,1407 .initprot = initprot,
...@@ -1397,7 +1409,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1397,7 +1409,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1397 .flags = 0,1409 .flags = 0,
1398 }),1410 }),
1399 });1411 });
1400 self.linkedit_segment_next_offset = @intCast(u32, off);1412 self.linkedit_segment_next_offset = @intCast(u32, address_and_offset.offset);
1401 self.cmd_table_dirty = true;1413 self.cmd_table_dirty = true;
1402 }1414 }
1403 if (self.dyld_info_cmd_index == null) {1415 if (self.dyld_info_cmd_index == null) {
...@@ -1631,9 +1643,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -1631,9 +1643,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
16311643
1632 const expand_text_section = block_placement == null or block_placement.?.next == null;1644 const expand_text_section = block_placement == null or block_placement.?.next == null;
1633 if (expand_text_section) {1645 if (expand_text_section) {
1634 const text_capacity = self.allocatedSize(text_section.offset);
1635 const needed_size = (vaddr + new_block_size) - text_section.addr;1646 const needed_size = (vaddr + new_block_size) - text_section.addr;
1636 assert(needed_size <= text_capacity); // TODO must move the entire text section.1647 assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section.
16371648
1638 self.last_text_block = text_block;1649 self.last_text_block = text_block;
1639 text_section.size = needed_size;1650 text_section.size = needed_size;
...@@ -1692,95 +1703,28 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {...@@ -1692,95 +1703,28 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
1692 return self.makeString(new_name);1703 return self.makeString(new_name);
1693}1704}
16941705
1695fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {1706const NextSegmentAddressAndOffset = struct {
1696 const hdr_size: u64 = @sizeOf(macho.mach_header_64);1707 address: u64,
1697 if (start < hdr_size) return hdr_size;1708 offset: u64,
1698 const end = start + satMul(size, alloc_num) / alloc_den;1709};
1699 {
1700 const off = @sizeOf(macho.mach_header_64);
1701 var tight_size: u64 = 0;
1702 for (self.load_commands.items) |cmd| {
1703 tight_size += cmd.cmdsize();
1704 }
1705 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1706 const test_end = off + increased_size;
1707 if (end > off and start < test_end) {
1708 return test_end;
1709 }
1710 }
1711 if (self.text_segment_cmd_index) |text_index| {
1712 const text_segment = self.load_commands.items[text_index].Segment;
1713 for (text_segment.sections.items) |section| {
1714 const increased_size = satMul(section.size, alloc_num) / alloc_den;
1715 const test_end = section.offset + increased_size;
1716 if (end > section.offset and start < test_end) {
1717 return test_end;
1718 }
1719 }
1720 }
1721 if (self.dyld_info_cmd_index) |dyld_info_index| {
1722 const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly;
1723 const tight_size = dyld_info.export_size;
1724 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1725 const test_end = dyld_info.export_off + increased_size;
1726 if (end > dyld_info.export_off and start < test_end) {
1727 return test_end;
1728 }
1729 }
1730 if (self.symtab_cmd_index) |symtab_index| {
1731 const symtab = self.load_commands.items[symtab_index].Symtab;
1732 {
1733 const tight_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
1734 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1735 const test_end = symtab.symoff + increased_size;
1736 if (end > symtab.symoff and start < test_end) {
1737 return test_end;
1738 }
1739 }
1740 {
1741 const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
1742 const test_end = symtab.stroff + increased_size;
1743 if (end > symtab.stroff and start < test_end) {
1744 return test_end;
1745 }
1746 }
1747 }
1748 return null;
1749}
17501710
1751fn allocatedSize(self: *MachO, start: u64) u64 {1711fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
1752 if (start == 0)1712 const prev_segment_idx = blk: {
1753 return 0;1713 if (self.data_segment_cmd_index) |idx| {
1754 var min_pos: u64 = std.math.maxInt(u64);1714 break :blk idx;
1755 {1715 } else if (self.text_segment_cmd_index) |idx| {
1756 const off = @sizeOf(macho.mach_header_64);1716 break :blk idx;
1757 if (off > start and off < min_pos) min_pos = off;1717 } else {
1758 }1718 unreachable; // unhandled LC_SEGMENT_64 load command before __TEXT
1759 if (self.text_segment_cmd_index) |text_index| {
1760 const text_segment = self.load_commands.items[text_index].Segment;
1761 for (text_segment.sections.items) |section| {
1762 if (section.offset <= start) continue;
1763 if (section.offset < min_pos) min_pos = section.offset;
1764 }1719 }
1765 }1720 };
1766 if (self.dyld_info_cmd_index) |dyld_info_index| {1721 const prev_segment = self.load_commands.items[prev_segment_idx].Segment;
1767 const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly;1722 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
1768 if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off;1723 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
1769 }1724 return .{
1770 if (self.symtab_cmd_index) |symtab_index| {1725 .address = address,
1771 const symtab = self.load_commands.items[symtab_index].Symtab;1726 .offset = offset,
1772 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;1727 };
1773 if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
1774 }
1775 return min_pos - start;
1776}
1777
1778fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
1779 var start: u64 = 0;
1780 while (self.detectAllocCollision(start, object_size)) |item_end| {
1781 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
1782 }
1783 return start;
1784}1728}
17851729
1786fn writeOffsetTableEntry(self: *MachO, index: usize) !void {1730fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
...@@ -2068,12 +2012,6 @@ fn writeHeader(self: *MachO) !void {...@@ -2068,12 +2012,6 @@ fn writeHeader(self: *MachO) !void {
2068 try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0);2012 try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0);
2069}2013}
20702014
2071/// Saturating multiplication
2072fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2073 const T = @TypeOf(a, b);
2074 return std.math.mul(T, a, b) catch std.math.maxInt(T);
2075}
2076
2077/// Parse MachO contents from existing binary file.2015/// Parse MachO contents from existing binary file.
2078/// TODO This method is incomplete and currently parses only the header2016/// TODO This method is incomplete and currently parses only the header
2079/// plus the load commands.2017/// plus the load commands.
...@@ -2148,7 +2086,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {...@@ -2148,7 +2086,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
2148 self.header = header;2086 self.header = header;
2149}2087}
21502088
2151fn parseAndCmpName(name: []const u8, needle: []const u8) bool {2089pub fn parseAndCmpName(name: []const u8, needle: []const u8) bool {
2152 const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len;2090 const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len;
2153 return mem.eql(u8, name[0..len], needle);2091 return mem.eql(u8, name[0..len], needle);
2154}2092}
...@@ -2204,3 +2142,9 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {...@@ -2204,3 +2142,9 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
2204 var stream = std.io.fixedBufferStream(buffer);2142 var stream = std.io.fixedBufferStream(buffer);
2205 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);2143 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
2206}2144}
2145
2146/// Saturating multiplication
2147pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2148 const T = @TypeOf(a, b);
2149 return std.math.mul(T, a, b) catch std.math.maxInt(T);
2150}
src/link/MachO/commands.zig+39-1
...@@ -7,7 +7,12 @@ const macho = std.macho;...@@ -7,7 +7,12 @@ const macho = std.macho;
7const testing = std.testing;7const testing = std.testing;
88
9const Allocator = std.mem.Allocator;9const Allocator = std.mem.Allocator;
10const makeName = @import("../MachO.zig").makeStaticString;10const MachO = @import("../MachO.zig");
11const makeName = MachO.makeStaticString;
12const alloc_num = MachO.alloc_num;
13const alloc_den = MachO.alloc_den;
14const parseAndCmpName = MachO.parseAndCmpName;
15const satMul = MachO.satMul;
1116
12pub const LoadCommand = union(enum) {17pub const LoadCommand = union(enum) {
13 Segment: SegmentCommand,18 Segment: SegmentCommand,
...@@ -155,6 +160,39 @@ pub const SegmentCommand = struct {...@@ -155,6 +160,39 @@ pub const SegmentCommand = struct {
155 return .{ .inner = inner };160 return .{ .inner = inner };
156 }161 }
157162
163 pub fn addSection(self: *SegmentCommand, alloc: *Allocator, section: macho.section_64) !void {
164 try self.sections.append(alloc, section);
165 self.inner.cmdsize += @sizeOf(macho.section_64);
166 self.inner.nsects += 1;
167 }
168
169 fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
170 const end = start + satMul(size, alloc_num) / alloc_den;
171 for (self.sections.items) |section| {
172 const increased_size = satMul(section.size, alloc_num) / alloc_den;
173 const test_end = section.offset + increased_size;
174 if (end > section.offset and start < test_end) {
175 return test_end;
176 }
177 }
178 return null;
179 }
180
181 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u16) u32 {
182 var start: u64 = blk: {
183 if (parseAndCmpName(self.inner.segname[0..], "__TEXT")) {
184 if (self.sections.items.len > 0) {
185 break :blk self.sections.items[0].offset;
186 }
187 }
188 break :blk 0;
189 };
190 while (self.detectAllocCollision(start, object_size)) |item_end| {
191 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
192 }
193 return @intCast(u32, start);
194 }
195
158 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {196 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
159 const inner = try reader.readStruct(macho.segment_command_64);197 const inner = try reader.readStruct(macho.segment_command_64);
160 var segment = SegmentCommand{198 var segment = SegmentCommand{