authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-04 12:56:14+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:47+02:00
log5b3c4691e628cb288e6595974781ffbadb717c28
tree14a665cc9085cc5f9c324cdf8cd85e3e96af29e2
parent453c16d8acead9bc5ef19155474ba8af37d151cf

zld: put relocs in a TextBlock


3 files changed, 185 insertions(+), 126 deletions(-)

src/link/MachO/Object.zig+103-85
......@@ -9,13 +9,13 @@ const log = std.log.scoped(.object);
99const macho = std.macho;
1010const mem = std.mem;
1111const reloc = @import("reloc.zig");
12const parseName = @import("Zld.zig").parseName;
1312
1413const Allocator = mem.Allocator;
1514const Arch = std.Target.Cpu.Arch;
1615const Relocation = reloc.Relocation;
1716const Symbol = @import("Symbol.zig");
18const TextBlock = @import("Zld.zig").TextBlock;
17const TextBlock = Zld.TextBlock;
18const Zld = @import("Zld.zig");
1919
2020usingnamespace @import("commands.zig");
2121
......@@ -74,43 +74,6 @@ pub const Section = struct {
7474 allocator.free(relocs);
7575 }
7676 }
77
78 pub fn segname(self: Section) []const u8 {
79 return parseName(&self.inner.segname);
80 }
81
82 pub fn sectname(self: Section) []const u8 {
83 return parseName(&self.inner.sectname);
84 }
85
86 pub fn flags(self: Section) u32 {
87 return self.inner.flags;
88 }
89
90 pub fn sectionType(self: Section) u8 {
91 return @truncate(u8, self.flags() & 0xff);
92 }
93
94 pub fn sectionAttrs(self: Section) u32 {
95 return self.flags() & 0xffffff00;
96 }
97
98 pub fn isCode(self: Section) bool {
99 const attr = self.sectionAttrs();
100 return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0;
101 }
102
103 pub fn isDebug(self: Section) bool {
104 return self.sectionAttrs() & macho.S_ATTR_DEBUG != 0;
105 }
106
107 pub fn dontDeadStrip(self: Section) bool {
108 return self.sectionAttrs() & macho.S_ATTR_NO_DEAD_STRIP != 0;
109 }
110
111 pub fn dontDeadStripIfReferencesLive(self: Section) bool {
112 return self.sectionAttrs() & macho.S_ATTR_LIVE_SUPPORT != 0;
113 }
11477};
11578
11679const DebugInfo = struct {
......@@ -272,7 +235,6 @@ pub fn parse(self: *Object) !void {
272235 try self.parseSymtab();
273236 try self.parseDataInCode();
274237 try self.parseInitializers();
275 try self.parseDummy();
276238}
277239
278240pub fn readLoadCommands(self: *Object, reader: anytype) !void {
......@@ -288,8 +250,8 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
288250 var seg = cmd.Segment;
289251 for (seg.sections.items) |*sect, j| {
290252 const index = @intCast(u16, j);
291 const segname = parseName(&sect.segname);
292 const sectname = parseName(&sect.sectname);
253 const segname = segmentName(sect.*);
254 const sectname = sectionName(sect.*);
293255 if (mem.eql(u8, segname, "__DWARF")) {
294256 if (mem.eql(u8, sectname, "__debug_info")) {
295257 self.dwarf_debug_info_index = index;
......@@ -351,7 +313,7 @@ pub fn parseSections(self: *Object) !void {
351313 try self.sections.ensureCapacity(self.allocator, seg.sections.items.len);
352314
353315 for (seg.sections.items) |sect| {
354 log.debug("parsing section '{s},{s}'", .{ parseName(&sect.segname), parseName(&sect.sectname) });
316 log.debug("parsing section '{s},{s}'", .{ segmentName(sect), sectionName(sect) });
355317 // Read sections' code
356318 var code = try self.allocator.alloc(u8, @intCast(usize, sect.size));
357319 _ = try self.file.?.preadAll(code, sect.offset);
......@@ -381,47 +343,91 @@ pub fn parseSections(self: *Object) !void {
381343 }
382344}
383345
384fn cmpNlist(_: void, lhs: macho.nlist_64, rhs: macho.nlist_64) bool {
385 return lhs.n_value < rhs.n_value;
386}
346pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
347 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
387348
388fn filterSymsInSection(symbols: []macho.nlist_64, sect_id: u8) []macho.nlist_64 {
389 var start: usize = 0;
390 var end: usize = symbols.len;
349 log.warn("analysing {s}", .{self.name.?});
391350
392 while (true) {
393 var change = false;
394 if (symbols[start].n_sect != sect_id) {
395 start += 1;
396 change = true;
351 const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
352
353 const SymWithIndex = struct {
354 nlist: macho.nlist_64,
355 index: u32,
356
357 pub fn cmp(_: void, lhs: @This(), rhs: @This()) bool {
358 return lhs.nlist.n_value < rhs.nlist.n_value;
397359 }
398 if (symbols[end - 1].n_sect != sect_id) {
399 end -= 1;
400 change = true;
360
361 fn filterSymsInSection(symbols: []@This(), sect_id: u8) []@This() {
362 var start: usize = 0;
363 var end: usize = symbols.len;
364
365 while (true) {
366 var change = false;
367 if (symbols[start].nlist.n_sect != sect_id) {
368 start += 1;
369 change = true;
370 }
371 if (symbols[end - 1].nlist.n_sect != sect_id) {
372 end -= 1;
373 change = true;
374 }
375
376 if (start == end) break;
377 if (!change) break;
378 }
379
380 return symbols[start..end];
401381 }
402382
403 if (start == end) break;
404 if (!change) break;
405 }
383 fn filterRelocs(relocs: []macho.relocation_info, start: u64, end: u64) []macho.relocation_info {
384 if (relocs.len == 0) return relocs;
406385
407 return symbols[start..end];
408}
386 var start_id: usize = 0;
387 var end_id: usize = relocs.len;
409388
410pub fn parseDummy(self: *Object) !void {
411 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
389 while (true) {
390 var change = false;
391 if (relocs[start_id].r_address > end) {
392 start_id += 1;
393 change = true;
394 }
395 if (relocs[end_id - 1].r_address < start) {
396 end_id -= 1;
397 change = true;
398 }
412399
413 log.warn("analysing {s}", .{self.name.?});
400 if (start_id == end_id) break;
401 if (!change) break;
402 }
414403
415 const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
404 return relocs[start_id..end_id];
405 }
406 };
407
408 const nlists = self.symtab.items[dysymtab.ilocalsym..dysymtab.iundefsym];
416409
417 var sorted_syms = std.ArrayList(macho.nlist_64).init(self.allocator);
410 var sorted_syms = std.ArrayList(SymWithIndex).init(self.allocator);
418411 defer sorted_syms.deinit();
419 try sorted_syms.appendSlice(self.symtab.items[dysymtab.ilocalsym..dysymtab.iundefsym]);
412 try sorted_syms.ensureTotalCapacity(nlists.len);
420413
421 std.sort.sort(macho.nlist_64, sorted_syms.items, {}, cmpNlist);
414 for (nlists) |nlist, index| {
415 sorted_syms.appendAssumeCapacity(.{
416 .nlist = nlist,
417 .index = @intCast(u32, index + dysymtab.ilocalsym),
418 });
419 }
420
421 std.sort.sort(SymWithIndex, sorted_syms.items, {}, SymWithIndex.cmp);
422422
423423 for (seg.sections.items) |sect, sect_id| {
424 log.warn("section {s},{s}", .{ parseName(&sect.segname), parseName(&sect.sectname) });
424 log.warn("section {s},{s}", .{ segmentName(sect), sectionName(sect) });
425
426 const match = (try zld.getMatchingSection(sect)) orelse {
427 log.warn("unhandled section", .{});
428 continue;
429 };
430
425431 // Read code
426432 var code = try self.allocator.alloc(u8, @intCast(usize, sect.size));
427433 defer self.allocator.free(code);
......@@ -431,16 +437,25 @@ pub fn parseDummy(self: *Object) !void {
431437 const raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
432438 defer self.allocator.free(raw_relocs);
433439 _ = try self.file.?.preadAll(raw_relocs, sect.reloff);
440 const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);
434441
435 const relocs = try reloc.parse(
436 self.allocator,
437 self.arch.?,
438 code,
439 mem.bytesAsSlice(macho.relocation_info, raw_relocs),
440 );
442 const alignment = sect.@"align";
441443
442444 if (self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
443 const syms = filterSymsInSection(sorted_syms.items, @intCast(u8, sect_id + 1));
445 const syms = SymWithIndex.filterSymsInSection(sorted_syms.items, @intCast(u8, sect_id + 1));
446
447 if (syms.len == 0) {
448 // One large text block referenced by section offsets only
449 log.warn("TextBlock", .{});
450 log.warn(" | referenced by section offsets", .{});
451 log.warn(" | start_addr = {}", .{sect.addr});
452 log.warn(" | end_addr = {}", .{sect.size});
453 log.warn(" | size = {}", .{sect.size});
454 log.warn(" | alignment = 0x{x}", .{alignment});
455 log.warn(" | segment_id = {}", .{match.seg});
456 log.warn(" | section_id = {}", .{match.sect});
457 log.warn(" | relocs: {any}", .{relocs});
458 }
444459
445460 var indices = std.ArrayList(u32).init(self.allocator);
446461 defer indices.deinit();
......@@ -450,32 +465,35 @@ pub fn parseDummy(self: *Object) !void {
450465 const curr = syms[i];
451466 try indices.append(i);
452467
453 const next: ?macho.nlist_64 = if (i + 1 < syms.len)
468 const next: ?SymWithIndex = if (i + 1 < syms.len)
454469 syms[i + 1]
455470 else
456471 null;
457472
458473 if (next) |n| {
459 if (curr.n_value == n.n_value) {
474 if (curr.nlist.n_value == n.nlist.n_value) {
460475 continue;
461476 }
462477 }
463478
464 const start_addr = curr.n_value - sect.addr;
465 const end_addr = if (next) |n| n.n_value - sect.addr else sect.size;
466 const alignment = sect.@"align";
479 const start_addr = curr.nlist.n_value - sect.addr;
480 const end_addr = if (next) |n| n.nlist.n_value - sect.addr else sect.size;
467481
468482 const tb_code = code[start_addr..end_addr];
469483 const size = tb_code.len;
470484
471485 log.warn("TextBlock", .{});
472486 for (indices.items) |id| {
473 log.warn(" | symbol {s}", .{self.getString(syms[id].n_strx)});
487 const sym = self.symbols.items[syms[id].index];
488 log.warn(" | symbol = {s}", .{sym.name});
474489 }
475 log.warn(" | start_addr = 0x{x}", .{start_addr});
476 log.warn(" | end_addr = 0x{x}", .{end_addr});
490 log.warn(" | start_addr = {}", .{start_addr});
491 log.warn(" | end_addr = {}", .{end_addr});
477492 log.warn(" | size = {}", .{size});
478493 log.warn(" | alignment = 0x{x}", .{alignment});
494 log.warn(" | segment_id = {}", .{match.seg});
495 log.warn(" | section_id = {}", .{match.sect});
496 log.warn(" | relocs: {any}", .{SymWithIndex.filterRelocs(relocs, start_addr, end_addr)});
479497
480498 indices.clearRetainingCapacity();
481499 }
src/link/MachO/Zld.zig+44-41
......@@ -234,6 +234,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
234234 try self.parseInputFiles(files, args.syslibroot);
235235 try self.parseLibs(args.libs, args.syslibroot);
236236 try self.resolveSymbols();
237 try self.parseTextBlocks();
237238 try self.resolveStubsAndGotEntries();
238239 try self.updateMetadata();
239240 try self.sortSections();
......@@ -322,10 +323,10 @@ fn mapAndUpdateSections(
322323
323324 log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{
324325 object.name.?,
325 parseName(&source_sect.inner.segname),
326 parseName(&source_sect.inner.sectname),
327 parseName(&target_sect.segname),
328 parseName(&target_sect.sectname),
326 segmentName(source_sect.inner),
327 sectionName(source_sect.inner),
328 segmentName(target_sect.*),
329 sectionName(target_sect.*),
329330 offset,
330331 offset + size,
331332 });
......@@ -343,12 +344,12 @@ fn updateMetadata(self: *Zld) !void {
343344 for (self.objects.items) |object| {
344345 // Find ideal section alignment and update section mappings
345346 for (object.sections.items) |sect, sect_id| {
346 const match = (try self.getMatchingSection(sect)) orelse {
347 const match = (try self.getMatchingSection(sect.inner)) orelse {
347348 log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{
348349 object.name.?,
349 sect.flags(),
350 sect.segname(),
351 sect.sectname(),
350 sect.inner.flags,
351 segmentName(sect.inner),
352 sectionName(sect.inner),
352353 });
353354 continue;
354355 };
......@@ -441,15 +442,15 @@ const MatchingSection = struct {
441442 sect: u16,
442443};
443444
444fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection {
445pub fn getMatchingSection(self: *Zld, sect: macho.section_64) !?MatchingSection {
445446 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
446447 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
447448 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
448 const segname = sect.segname();
449 const sectname = sect.sectname();
449 const segname = segmentName(sect);
450 const sectname = sectionName(sect);
450451
451452 const res: ?MatchingSection = blk: {
452 switch (sect.sectionType()) {
453 switch (sectionType(sect)) {
453454 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
454455 if (self.text_const_section_index == null) {
455456 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
......@@ -649,7 +650,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection {
649650 };
650651 },
651652 macho.S_REGULAR => {
652 if (sect.isCode()) {
653 if (sectionIsCode(sect)) {
653654 if (self.text_section_index == null) {
654655 self.text_section_index = @intCast(u16, text_seg.sections.items.len);
655656 try text_seg.addSection(self.allocator, "__text", .{
......@@ -662,11 +663,11 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection {
662663 .sect = self.text_section_index.?,
663664 };
664665 }
665 if (sect.isDebug()) {
666 if (sectionIsDebug(sect)) {
666667 // TODO debug attributes
667668 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
668669 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
669 sect.flags(), segname, sectname,
670 sect.flags, segname, sectname,
670671 });
671672 }
672673 break :blk null;
......@@ -829,7 +830,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection {
829830
830831 if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) {
831832 log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{
832 sect.flags(), segname, sectname,
833 sect.flags, segname, sectname,
833834 });
834835 }
835836
......@@ -956,8 +957,8 @@ fn sortSections(self: *Zld) !void {
956957
957958 log.debug("remapping in {s}: '{s},{s}': {} => {}", .{
958959 object.name.?,
959 parseName(&sect.inner.segname),
960 parseName(&sect.inner.sectname),
960 segmentName(sect.inner),
961 sectionName(sect.inner),
961962 target_map.section_id,
962963 new_index,
963964 });
......@@ -1086,8 +1087,8 @@ fn allocateSymbol(self: *Zld, symbol: *Symbol) !void {
10861087 const source_sect = &object.sections.items[reg.section];
10871088 const target_map = source_sect.target_map orelse {
10881089 log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{
1089 parseName(&source_sect.inner.segname),
1090 parseName(&source_sect.inner.sectname),
1090 segmentName(source_sect.inner),
1091 sectionName(source_sect.inner),
10911092 symbol.name,
10921093 });
10931094 return;
......@@ -1464,7 +1465,7 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
14641465fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
14651466 log.debug("resolving symbols in '{s}'", .{object.name});
14661467
1467 for (object.symtab.items) |sym| {
1468 for (object.symtab.items) |sym, sym_id| {
14681469 const sym_name = object.getString(sym.n_strx);
14691470
14701471 if (Symbol.isStab(sym)) {
......@@ -1497,6 +1498,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
14971498 .file = object,
14981499 },
14991500 };
1501 const index = @intCast(u32, self.locals.items.len);
15001502 try self.locals.append(self.allocator, symbol);
15011503 try object.symbols.append(self.allocator, symbol);
15021504 continue;
......@@ -1665,6 +1667,12 @@ fn resolveSymbols(self: *Zld) !void {
16651667 if (has_undefined) return error.UndefinedSymbolReference;
16661668}
16671669
1670fn parseTextBlocks(self: *Zld) !void {
1671 for (self.objects.items) |object| {
1672 try object.parseTextBlocks(self);
1673 }
1674}
1675
16681676fn resolveStubsAndGotEntries(self: *Zld) !void {
16691677 for (self.objects.items) |object| {
16701678 log.debug("resolving stubs and got entries from {s}", .{object.name});
......@@ -1718,11 +1726,11 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
17181726 log.debug("relocating object {s}", .{object.name});
17191727
17201728 for (object.sections.items) |sect| {
1721 if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or
1722 sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue;
1729 if (sectionType(sect.inner) == macho.S_MOD_INIT_FUNC_POINTERS or
1730 sectionType(sect.inner) == macho.S_MOD_TERM_FUNC_POINTERS) continue;
17231731
1724 const segname = parseName(&sect.inner.segname);
1725 const sectname = parseName(&sect.inner.sectname);
1732 const segname = segmentName(sect.inner);
1733 const sectname = sectionName(sect.inner);
17261734
17271735 log.debug("relocating section '{s},{s}'", .{ segname, sectname });
17281736
......@@ -1759,7 +1767,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
17591767 args.source_target_sect_addr = source_sect.inner.addr;
17601768 }
17611769
1762 const flags = @truncate(u8, target_sect.flags & 0xff);
1770 const sect_type = sectionType(target_sect);
17631771 const should_rebase = rebase: {
17641772 if (!unsigned.is_64bit) break :rebase false;
17651773
......@@ -1780,8 +1788,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
17801788 };
17811789
17821790 if (!is_right_segment) break :rebase false;
1783 if (flags != macho.S_LITERAL_POINTERS and
1784 flags != macho.S_REGULAR)
1791 if (sect_type != macho.S_LITERAL_POINTERS and
1792 sect_type != macho.S_REGULAR)
17851793 {
17861794 break :rebase false;
17871795 }
......@@ -1804,7 +1812,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
18041812
18051813 // TLV is handled via a separate offset mechanism.
18061814 // Calculate the offset to the initializer.
1807 if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1815 if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
18081816 // TODO we don't want to save offset to tlv_bootstrap
18091817 if (mem.eql(u8, object.symbols.items[rel.target.symbol].name, "__tlv_bootstrap")) break :tlv;
18101818
......@@ -1858,13 +1866,13 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
18581866 target_sect_off + sect.code.len,
18591867 });
18601868
1861 if (target_sect.flags == macho.S_ZEROFILL or
1862 target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or
1863 target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES)
1869 if (sectionType(target_sect) == macho.S_ZEROFILL or
1870 sectionType(target_sect) == macho.S_THREAD_LOCAL_ZEROFILL or
1871 sectionType(target_sect) == macho.S_THREAD_LOCAL_VARIABLES)
18641872 {
18651873 log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
1866 parseName(&target_sect.segname),
1867 parseName(&target_sect.sectname),
1874 segmentName(target_sect),
1875 sectionName(target_sect),
18681876 target_sect_off,
18691877 target_sect_off + sect.code.len,
18701878 });
......@@ -1926,8 +1934,8 @@ fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.T
19261934 log.debug(" | section offset", .{});
19271935 const source_sect = object.sections.items[sect_id];
19281936 log.debug(" | section '{s},{s}'", .{
1929 parseName(&source_sect.inner.segname),
1930 parseName(&source_sect.inner.sectname),
1937 segmentName(source_sect.inner),
1938 sectionName(source_sect.inner),
19311939 });
19321940 const target_map = source_sect.target_map orelse unreachable;
19331941 const target_seg = self.load_commands.items[target_map.segment_id].Segment;
......@@ -2999,8 +3007,3 @@ fn writeHeader(self: *Zld) !void {
29993007
30003008 try self.file.?.pwriteAll(mem.asBytes(&header), 0);
30013009}
3002
3003pub fn parseName(name: *const [16]u8) []const u8 {
3004 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3005 return name[0..len];
3006}
src/link/MachO/commands.zig+38
......@@ -425,6 +425,44 @@ fn makeStaticString(bytes: []const u8) [16]u8 {
425425 return buf;
426426}
427427
428fn parseName(name: *const [16]u8) []const u8 {
429 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
430 return name[0..len];
431}
432
433pub fn segmentName(sect: macho.section_64) []const u8 {
434 return parseName(&sect.segname);
435}
436
437pub fn sectionName(sect: macho.section_64) []const u8 {
438 return parseName(&sect.sectname);
439}
440
441pub fn sectionType(sect: macho.section_64) u8 {
442 return @truncate(u8, sect.flags & 0xff);
443}
444
445pub fn sectionAttrs(sect: macho.section_64) u32 {
446 return sect.flags & 0xffffff00;
447}
448
449pub fn sectionIsCode(sect: macho.section_64) bool {
450 const attr = sectionAttrs(sect);
451 return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0;
452}
453
454pub fn sectionIsDebug(sect: macho.section_64) bool {
455 return sectionAttrs(sect) & macho.S_ATTR_DEBUG != 0;
456}
457
458pub fn sectionIsDontDeadStrip(sect: macho.section_64) bool {
459 return sectionAttrs(sect) & macho.S_ATTR_NO_DEAD_STRIP != 0;
460}
461
462pub fn sectionIsDontDeadStripIfReferencesLive(sect: macho.section_64) bool {
463 return sectionAttrs(sect) & macho.S_ATTR_LIVE_SUPPORT != 0;
464}
465
428466fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void {
429467 var stream = io.fixedBufferStream(buffer);
430468 var given = try LoadCommand.read(allocator, stream.reader());