| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const build_options = @import("build_options"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | const dwarf = std.dwarf; |
| 4 | 5 | const fs = std.fs; |
| 5 | 6 | const log = std.log.scoped(.link); |
| 6 | 7 | const macho = std.macho; |
| ... | ... | @@ -18,6 +19,7 @@ const CodeSignature = @import("CodeSignature.zig"); |
| 18 | 19 | const Compilation = @import("../../Compilation.zig"); |
| 19 | 20 | const Dylib = @import("Dylib.zig"); |
| 20 | 21 | const MachO = @import("../MachO.zig"); |
| 22 | const Object = @import("Object.zig"); |
| 21 | 23 | const SymbolWithLoc = MachO.SymbolWithLoc; |
| 22 | 24 | const Trie = @import("Trie.zig"); |
| 23 | 25 | |
| ... | ... | @@ -618,20 +620,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 618 | 620 | if (macho_file.base.options.entitlements) |path| { |
| 619 | 621 | try codesig.addEntitlements(arena, path); |
| 620 | 622 | } |
| 621 | | codesig_offset = try macho_file.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer); |
| 623 | codesig_offset = try writeCodeSignaturePadding(macho_file, &codesig, &ncmds, lc_writer); |
| 622 | 624 | break :blk codesig; |
| 623 | 625 | } else null; |
| 624 | 626 | |
| 625 | 627 | var headers_buf = std.ArrayList(u8).init(arena); |
| 626 | | try macho_file.writeSegmentHeaders(&ncmds, headers_buf.writer()); |
| 628 | try writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer()); |
| 627 | 629 | |
| 628 | 630 | try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64)); |
| 629 | 631 | try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len); |
| 630 | 632 | |
| 631 | | try macho_file.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len)); |
| 633 | try writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len)); |
| 632 | 634 | |
| 633 | 635 | if (codesig) |*csig| { |
| 634 | | try macho_file.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last |
| 636 | try writeCodeSignature(macho_file, csig, codesig_offset.?); // code signing always comes last |
| 635 | 637 | } |
| 636 | 638 | } |
| 637 | 639 | |
| ... | ... | @@ -964,9 +966,9 @@ fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) |
| 964 | 966 | seg.vmsize = 0; |
| 965 | 967 | |
| 966 | 968 | try writeDyldInfoData(macho_file, ncmds, lc_writer); |
| 967 | | try macho_file.writeFunctionStarts(ncmds, lc_writer); |
| 968 | | try macho_file.writeDataInCode(ncmds, lc_writer); |
| 969 | | try macho_file.writeSymtabs(ncmds, lc_writer); |
| 969 | try writeFunctionStarts(macho_file, ncmds, lc_writer); |
| 970 | try writeDataInCode(macho_file, ncmds, lc_writer); |
| 971 | try writeSymtabs(macho_file, ncmds, lc_writer); |
| 970 | 972 | |
| 971 | 973 | seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size); |
| 972 | 974 | } |
| ... | ... | @@ -1280,3 +1282,660 @@ fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) ! |
| 1280 | 1282 | try macho_file.base.file.?.pwriteAll(&buf, file_offset); |
| 1281 | 1283 | } |
| 1282 | 1284 | } |
| 1285 | |
| 1286 | const asc_u64 = std.sort.asc(u64); |
| 1287 | |
| 1288 | fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void { |
| 1289 | const tracy = trace(@src()); |
| 1290 | defer tracy.end(); |
| 1291 | |
| 1292 | const text_seg_index = macho_file.text_segment_cmd_index orelse return; |
| 1293 | const text_sect_index = macho_file.text_section_index orelse return; |
| 1294 | const text_seg = macho_file.segments.items[text_seg_index]; |
| 1295 | |
| 1296 | const gpa = macho_file.base.allocator; |
| 1297 | |
| 1298 | // We need to sort by address first |
| 1299 | var addresses = std.ArrayList(u64).init(gpa); |
| 1300 | defer addresses.deinit(); |
| 1301 | try addresses.ensureTotalCapacityPrecise(macho_file.globals.items.len); |
| 1302 | |
| 1303 | for (macho_file.globals.items) |global| { |
| 1304 | const sym = macho_file.getSymbol(global); |
| 1305 | if (sym.undf()) continue; |
| 1306 | if (sym.n_desc == MachO.N_DESC_GCED) continue; |
| 1307 | const sect_id = sym.n_sect - 1; |
| 1308 | if (sect_id != text_sect_index) continue; |
| 1309 | |
| 1310 | addresses.appendAssumeCapacity(sym.n_value); |
| 1311 | } |
| 1312 | |
| 1313 | std.sort.sort(u64, addresses.items, {}, asc_u64); |
| 1314 | |
| 1315 | var offsets = std.ArrayList(u32).init(gpa); |
| 1316 | defer offsets.deinit(); |
| 1317 | try offsets.ensureTotalCapacityPrecise(addresses.items.len); |
| 1318 | |
| 1319 | var last_off: u32 = 0; |
| 1320 | for (addresses.items) |addr| { |
| 1321 | const offset = @intCast(u32, addr - text_seg.vmaddr); |
| 1322 | const diff = offset - last_off; |
| 1323 | |
| 1324 | if (diff == 0) continue; |
| 1325 | |
| 1326 | offsets.appendAssumeCapacity(diff); |
| 1327 | last_off = offset; |
| 1328 | } |
| 1329 | |
| 1330 | var buffer = std.ArrayList(u8).init(gpa); |
| 1331 | defer buffer.deinit(); |
| 1332 | |
| 1333 | const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); |
| 1334 | try buffer.ensureTotalCapacity(max_size); |
| 1335 | |
| 1336 | for (offsets.items) |offset| { |
| 1337 | try std.leb.writeULEB128(buffer.writer(), offset); |
| 1338 | } |
| 1339 | |
| 1340 | const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?]; |
| 1341 | const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)); |
| 1342 | const needed_size = buffer.items.len; |
| 1343 | link_seg.filesize = offset + needed_size - link_seg.fileoff; |
| 1344 | |
| 1345 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 1346 | |
| 1347 | try macho_file.base.file.?.pwriteAll(buffer.items, offset); |
| 1348 | |
| 1349 | try lc_writer.writeStruct(macho.linkedit_data_command{ |
| 1350 | .cmd = .FUNCTION_STARTS, |
| 1351 | .cmdsize = @sizeOf(macho.linkedit_data_command), |
| 1352 | .dataoff = @intCast(u32, offset), |
| 1353 | .datasize = @intCast(u32, needed_size), |
| 1354 | }); |
| 1355 | ncmds.* += 1; |
| 1356 | } |
| 1357 | |
| 1358 | fn filterDataInCode( |
| 1359 | dices: []align(1) const macho.data_in_code_entry, |
| 1360 | start_addr: u64, |
| 1361 | end_addr: u64, |
| 1362 | ) []align(1) const macho.data_in_code_entry { |
| 1363 | const Predicate = struct { |
| 1364 | addr: u64, |
| 1365 | |
| 1366 | pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool { |
| 1367 | return dice.offset >= macho_file.addr; |
| 1368 | } |
| 1369 | }; |
| 1370 | |
| 1371 | const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr }); |
| 1372 | const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); |
| 1373 | |
| 1374 | return dices[start..end]; |
| 1375 | } |
| 1376 | |
| 1377 | fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void { |
| 1378 | const tracy = trace(@src()); |
| 1379 | defer tracy.end(); |
| 1380 | |
| 1381 | var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator); |
| 1382 | defer out_dice.deinit(); |
| 1383 | |
| 1384 | const text_sect_id = macho_file.text_section_index orelse return; |
| 1385 | const text_sect_header = macho_file.sections.items(.header)[text_sect_id]; |
| 1386 | |
| 1387 | for (macho_file.objects.items) |object| { |
| 1388 | const dice = object.parseDataInCode() orelse continue; |
| 1389 | try out_dice.ensureUnusedCapacity(dice.len); |
| 1390 | |
| 1391 | for (object.managed_atoms.items) |atom| { |
| 1392 | const sym = atom.getSymbol(macho_file); |
| 1393 | if (sym.n_desc == MachO.N_DESC_GCED) continue; |
| 1394 | |
| 1395 | const sect_id = sym.n_sect - 1; |
| 1396 | if (sect_id != macho_file.text_section_index.?) { |
| 1397 | continue; |
| 1398 | } |
| 1399 | |
| 1400 | const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue; |
| 1401 | const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow; |
| 1402 | const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); |
| 1403 | const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse |
| 1404 | return error.Overflow; |
| 1405 | |
| 1406 | for (filtered_dice) |single| { |
| 1407 | const offset = single.offset - source_addr + base; |
| 1408 | out_dice.appendAssumeCapacity(.{ |
| 1409 | .offset = offset, |
| 1410 | .length = single.length, |
| 1411 | .kind = single.kind, |
| 1412 | }); |
| 1413 | } |
| 1414 | } |
| 1415 | } |
| 1416 | |
| 1417 | const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?]; |
| 1418 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); |
| 1419 | const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry); |
| 1420 | seg.filesize = offset + needed_size - seg.fileoff; |
| 1421 | |
| 1422 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 1423 | |
| 1424 | try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset); |
| 1425 | try lc_writer.writeStruct(macho.linkedit_data_command{ |
| 1426 | .cmd = .DATA_IN_CODE, |
| 1427 | .cmdsize = @sizeOf(macho.linkedit_data_command), |
| 1428 | .dataoff = @intCast(u32, offset), |
| 1429 | .datasize = @intCast(u32, needed_size), |
| 1430 | }); |
| 1431 | ncmds.* += 1; |
| 1432 | } |
| 1433 | |
| 1434 | fn writeSymtabs(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void { |
| 1435 | var symtab_cmd = macho.symtab_command{ |
| 1436 | .cmdsize = @sizeOf(macho.symtab_command), |
| 1437 | .symoff = 0, |
| 1438 | .nsyms = 0, |
| 1439 | .stroff = 0, |
| 1440 | .strsize = 0, |
| 1441 | }; |
| 1442 | var dysymtab_cmd = macho.dysymtab_command{ |
| 1443 | .cmdsize = @sizeOf(macho.dysymtab_command), |
| 1444 | .ilocalsym = 0, |
| 1445 | .nlocalsym = 0, |
| 1446 | .iextdefsym = 0, |
| 1447 | .nextdefsym = 0, |
| 1448 | .iundefsym = 0, |
| 1449 | .nundefsym = 0, |
| 1450 | .tocoff = 0, |
| 1451 | .ntoc = 0, |
| 1452 | .modtaboff = 0, |
| 1453 | .nmodtab = 0, |
| 1454 | .extrefsymoff = 0, |
| 1455 | .nextrefsyms = 0, |
| 1456 | .indirectsymoff = 0, |
| 1457 | .nindirectsyms = 0, |
| 1458 | .extreloff = 0, |
| 1459 | .nextrel = 0, |
| 1460 | .locreloff = 0, |
| 1461 | .nlocrel = 0, |
| 1462 | }; |
| 1463 | var ctx = try writeSymtab(macho_file, &symtab_cmd); |
| 1464 | defer ctx.imports_table.deinit(); |
| 1465 | try writeDysymtab(macho_file, ctx, &dysymtab_cmd); |
| 1466 | try writeStrtab(macho_file, &symtab_cmd); |
| 1467 | try lc_writer.writeStruct(symtab_cmd); |
| 1468 | try lc_writer.writeStruct(dysymtab_cmd); |
| 1469 | ncmds.* += 2; |
| 1470 | } |
| 1471 | |
| 1472 | fn writeSymtab(macho_file: *MachO, lc: *macho.symtab_command) !SymtabCtx { |
| 1473 | const gpa = macho_file.base.allocator; |
| 1474 | |
| 1475 | var locals = std.ArrayList(macho.nlist_64).init(gpa); |
| 1476 | defer locals.deinit(); |
| 1477 | |
| 1478 | for (macho_file.locals.items) |sym, sym_id| { |
| 1479 | if (sym.n_strx == 0) continue; // no name, skip |
| 1480 | if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip |
| 1481 | const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null }; |
| 1482 | if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip |
| 1483 | if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip |
| 1484 | try locals.append(sym); |
| 1485 | } |
| 1486 | |
| 1487 | for (macho_file.objects.items) |object, object_id| { |
| 1488 | for (object.symtab.items) |sym, sym_id| { |
| 1489 | if (sym.n_strx == 0) continue; // no name, skip |
| 1490 | if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip |
| 1491 | const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) }; |
| 1492 | if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip |
| 1493 | if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip |
| 1494 | var out_sym = sym; |
| 1495 | out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(sym_loc)); |
| 1496 | try locals.append(out_sym); |
| 1497 | } |
| 1498 | |
| 1499 | if (!macho_file.base.options.strip) { |
| 1500 | try generateSymbolStabs(macho_file, object, &locals); |
| 1501 | } |
| 1502 | } |
| 1503 | |
| 1504 | var exports = std.ArrayList(macho.nlist_64).init(gpa); |
| 1505 | defer exports.deinit(); |
| 1506 | |
| 1507 | for (macho_file.globals.items) |global| { |
| 1508 | const sym = macho_file.getSymbol(global); |
| 1509 | if (sym.undf()) continue; // import, skip |
| 1510 | if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip |
| 1511 | var out_sym = sym; |
| 1512 | out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global)); |
| 1513 | try exports.append(out_sym); |
| 1514 | } |
| 1515 | |
| 1516 | var imports = std.ArrayList(macho.nlist_64).init(gpa); |
| 1517 | defer imports.deinit(); |
| 1518 | |
| 1519 | var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa); |
| 1520 | |
| 1521 | for (macho_file.globals.items) |global| { |
| 1522 | const sym = macho_file.getSymbol(global); |
| 1523 | if (sym.n_strx == 0) continue; // no name, skip |
| 1524 | if (!sym.undf()) continue; // not an import, skip |
| 1525 | const new_index = @intCast(u32, imports.items.len); |
| 1526 | var out_sym = sym; |
| 1527 | out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global)); |
| 1528 | try imports.append(out_sym); |
| 1529 | try imports_table.putNoClobber(global, new_index); |
| 1530 | } |
| 1531 | |
| 1532 | const nlocals = @intCast(u32, locals.items.len); |
| 1533 | const nexports = @intCast(u32, exports.items.len); |
| 1534 | const nimports = @intCast(u32, imports.items.len); |
| 1535 | const nsyms = nlocals + nexports + nimports; |
| 1536 | |
| 1537 | const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?]; |
| 1538 | const offset = mem.alignForwardGeneric( |
| 1539 | u64, |
| 1540 | seg.fileoff + seg.filesize, |
| 1541 | @alignOf(macho.nlist_64), |
| 1542 | ); |
| 1543 | const needed_size = nsyms * @sizeOf(macho.nlist_64); |
| 1544 | seg.filesize = offset + needed_size - seg.fileoff; |
| 1545 | |
| 1546 | var buffer = std.ArrayList(u8).init(gpa); |
| 1547 | defer buffer.deinit(); |
| 1548 | try buffer.ensureTotalCapacityPrecise(needed_size); |
| 1549 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items)); |
| 1550 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items)); |
| 1551 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items)); |
| 1552 | |
| 1553 | log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 1554 | try macho_file.base.file.?.pwriteAll(buffer.items, offset); |
| 1555 | |
| 1556 | lc.symoff = @intCast(u32, offset); |
| 1557 | lc.nsyms = nsyms; |
| 1558 | |
| 1559 | return SymtabCtx{ |
| 1560 | .nlocalsym = nlocals, |
| 1561 | .nextdefsym = nexports, |
| 1562 | .nundefsym = nimports, |
| 1563 | .imports_table = imports_table, |
| 1564 | }; |
| 1565 | } |
| 1566 | |
| 1567 | fn writeStrtab(macho_file: *MachO, lc: *macho.symtab_command) !void { |
| 1568 | const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?]; |
| 1569 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); |
| 1570 | const needed_size = macho_file.strtab.buffer.items.len; |
| 1571 | seg.filesize = offset + needed_size - seg.fileoff; |
| 1572 | |
| 1573 | log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 1574 | |
| 1575 | try macho_file.base.file.?.pwriteAll(macho_file.strtab.buffer.items, offset); |
| 1576 | |
| 1577 | lc.stroff = @intCast(u32, offset); |
| 1578 | lc.strsize = @intCast(u32, needed_size); |
| 1579 | } |
| 1580 | |
| 1581 | pub fn generateSymbolStabs( |
| 1582 | macho_file: *MachO, |
| 1583 | object: Object, |
| 1584 | locals: *std.ArrayList(macho.nlist_64), |
| 1585 | ) !void { |
| 1586 | assert(!macho_file.base.options.strip); |
| 1587 | |
| 1588 | log.debug("parsing debug info in '{s}'", .{object.name}); |
| 1589 | |
| 1590 | const gpa = macho_file.base.allocator; |
| 1591 | var debug_info = try object.parseDwarfInfo(); |
| 1592 | defer debug_info.deinit(gpa); |
| 1593 | try dwarf.openDwarfDebugInfo(&debug_info, gpa); |
| 1594 | |
| 1595 | // We assume there is only one CU. |
| 1596 | const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) { |
| 1597 | error.MissingDebugInfo => { |
| 1598 | // TODO audit cases with missing debug info and audit our dwarf.zig module. |
| 1599 | log.debug("invalid or missing debug info in {s}; skipping", .{object.name}); |
| 1600 | return; |
| 1601 | }, |
| 1602 | else => |e| return e, |
| 1603 | }; |
| 1604 | |
| 1605 | const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str, compile_unit.*); |
| 1606 | const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str, compile_unit.*); |
| 1607 | |
| 1608 | // Open scope |
| 1609 | try locals.ensureUnusedCapacity(3); |
| 1610 | locals.appendAssumeCapacity(.{ |
| 1611 | .n_strx = try macho_file.strtab.insert(gpa, tu_comp_dir), |
| 1612 | .n_type = macho.N_SO, |
| 1613 | .n_sect = 0, |
| 1614 | .n_desc = 0, |
| 1615 | .n_value = 0, |
| 1616 | }); |
| 1617 | locals.appendAssumeCapacity(.{ |
| 1618 | .n_strx = try macho_file.strtab.insert(gpa, tu_name), |
| 1619 | .n_type = macho.N_SO, |
| 1620 | .n_sect = 0, |
| 1621 | .n_desc = 0, |
| 1622 | .n_value = 0, |
| 1623 | }); |
| 1624 | locals.appendAssumeCapacity(.{ |
| 1625 | .n_strx = try macho_file.strtab.insert(gpa, object.name), |
| 1626 | .n_type = macho.N_OSO, |
| 1627 | .n_sect = 0, |
| 1628 | .n_desc = 1, |
| 1629 | .n_value = object.mtime, |
| 1630 | }); |
| 1631 | |
| 1632 | var stabs_buf: [4]macho.nlist_64 = undefined; |
| 1633 | |
| 1634 | for (object.managed_atoms.items) |atom| { |
| 1635 | const stabs = try generateSymbolStabsForSymbol( |
| 1636 | macho_file, |
| 1637 | atom.getSymbolWithLoc(), |
| 1638 | debug_info, |
| 1639 | &stabs_buf, |
| 1640 | ); |
| 1641 | try locals.appendSlice(stabs); |
| 1642 | |
| 1643 | for (atom.contained.items) |sym_at_off| { |
| 1644 | const sym_loc = SymbolWithLoc{ |
| 1645 | .sym_index = sym_at_off.sym_index, |
| 1646 | .file = atom.file, |
| 1647 | }; |
| 1648 | const contained_stabs = try generateSymbolStabsForSymbol( |
| 1649 | macho_file, |
| 1650 | sym_loc, |
| 1651 | debug_info, |
| 1652 | &stabs_buf, |
| 1653 | ); |
| 1654 | try locals.appendSlice(contained_stabs); |
| 1655 | } |
| 1656 | } |
| 1657 | |
| 1658 | // Close scope |
| 1659 | try locals.append(.{ |
| 1660 | .n_strx = 0, |
| 1661 | .n_type = macho.N_SO, |
| 1662 | .n_sect = 0, |
| 1663 | .n_desc = 0, |
| 1664 | .n_value = 0, |
| 1665 | }); |
| 1666 | } |
| 1667 | |
| 1668 | fn generateSymbolStabsForSymbol( |
| 1669 | macho_file: *MachO, |
| 1670 | sym_loc: SymbolWithLoc, |
| 1671 | debug_info: dwarf.DwarfInfo, |
| 1672 | buf: *[4]macho.nlist_64, |
| 1673 | ) ![]const macho.nlist_64 { |
| 1674 | const gpa = macho_file.base.allocator; |
| 1675 | const object = macho_file.objects.items[sym_loc.file.?]; |
| 1676 | const sym = macho_file.getSymbol(sym_loc); |
| 1677 | const sym_name = macho_file.getSymbolName(sym_loc); |
| 1678 | |
| 1679 | if (sym.n_strx == 0) return buf[0..0]; |
| 1680 | if (sym.n_desc == MachO.N_DESC_GCED) return buf[0..0]; |
| 1681 | if (macho_file.symbolIsTemp(sym_loc)) return buf[0..0]; |
| 1682 | |
| 1683 | const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0]; |
| 1684 | const size: ?u64 = size: { |
| 1685 | if (source_sym.tentative()) break :size null; |
| 1686 | for (debug_info.func_list.items) |func| { |
| 1687 | if (func.pc_range) |range| { |
| 1688 | if (source_sym.n_value >= range.start and source_sym.n_value < range.end) { |
| 1689 | break :size range.end - range.start; |
| 1690 | } |
| 1691 | } |
| 1692 | } |
| 1693 | break :size null; |
| 1694 | }; |
| 1695 | |
| 1696 | if (size) |ss| { |
| 1697 | buf[0] = .{ |
| 1698 | .n_strx = 0, |
| 1699 | .n_type = macho.N_BNSYM, |
| 1700 | .n_sect = sym.n_sect, |
| 1701 | .n_desc = 0, |
| 1702 | .n_value = sym.n_value, |
| 1703 | }; |
| 1704 | buf[1] = .{ |
| 1705 | .n_strx = try macho_file.strtab.insert(gpa, sym_name), |
| 1706 | .n_type = macho.N_FUN, |
| 1707 | .n_sect = sym.n_sect, |
| 1708 | .n_desc = 0, |
| 1709 | .n_value = sym.n_value, |
| 1710 | }; |
| 1711 | buf[2] = .{ |
| 1712 | .n_strx = 0, |
| 1713 | .n_type = macho.N_FUN, |
| 1714 | .n_sect = 0, |
| 1715 | .n_desc = 0, |
| 1716 | .n_value = ss, |
| 1717 | }; |
| 1718 | buf[3] = .{ |
| 1719 | .n_strx = 0, |
| 1720 | .n_type = macho.N_ENSYM, |
| 1721 | .n_sect = sym.n_sect, |
| 1722 | .n_desc = 0, |
| 1723 | .n_value = ss, |
| 1724 | }; |
| 1725 | return buf; |
| 1726 | } else { |
| 1727 | buf[0] = .{ |
| 1728 | .n_strx = try macho_file.strtab.insert(gpa, sym_name), |
| 1729 | .n_type = macho.N_STSYM, |
| 1730 | .n_sect = sym.n_sect, |
| 1731 | .n_desc = 0, |
| 1732 | .n_value = sym.n_value, |
| 1733 | }; |
| 1734 | return buf[0..1]; |
| 1735 | } |
| 1736 | } |
| 1737 | |
| 1738 | const SymtabCtx = struct { |
| 1739 | nlocalsym: u32, |
| 1740 | nextdefsym: u32, |
| 1741 | nundefsym: u32, |
| 1742 | imports_table: std.AutoHashMap(SymbolWithLoc, u32), |
| 1743 | }; |
| 1744 | |
| 1745 | fn writeDysymtab(macho_file: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void { |
| 1746 | const gpa = macho_file.base.allocator; |
| 1747 | const nstubs = @intCast(u32, macho_file.stubs_table.count()); |
| 1748 | const ngot_entries = @intCast(u32, macho_file.got_entries_table.count()); |
| 1749 | const nindirectsyms = nstubs * 2 + ngot_entries; |
| 1750 | const iextdefsym = ctx.nlocalsym; |
| 1751 | const iundefsym = iextdefsym + ctx.nextdefsym; |
| 1752 | |
| 1753 | const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?]; |
| 1754 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); |
| 1755 | const needed_size = nindirectsyms * @sizeOf(u32); |
| 1756 | seg.filesize = offset + needed_size - seg.fileoff; |
| 1757 | |
| 1758 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 1759 | |
| 1760 | var buf = std.ArrayList(u8).init(gpa); |
| 1761 | defer buf.deinit(); |
| 1762 | try buf.ensureTotalCapacity(needed_size); |
| 1763 | const writer = buf.writer(); |
| 1764 | |
| 1765 | if (macho_file.stubs_section_index) |sect_id| { |
| 1766 | const stubs = &macho_file.sections.items(.header)[sect_id]; |
| 1767 | stubs.reserved1 = 0; |
| 1768 | for (macho_file.stubs.items) |entry| { |
| 1769 | if (entry.sym_index == 0) continue; |
| 1770 | const atom_sym = entry.getSymbol(macho_file); |
| 1771 | if (atom_sym.n_desc == MachO.N_DESC_GCED) continue; |
| 1772 | const target_sym = macho_file.getSymbol(entry.target); |
| 1773 | assert(target_sym.undf()); |
| 1774 | try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?); |
| 1775 | } |
| 1776 | } |
| 1777 | |
| 1778 | if (macho_file.got_section_index) |sect_id| { |
| 1779 | const got = &macho_file.sections.items(.header)[sect_id]; |
| 1780 | got.reserved1 = nstubs; |
| 1781 | for (macho_file.got_entries.items) |entry| { |
| 1782 | if (entry.sym_index == 0) continue; |
| 1783 | const atom_sym = entry.getSymbol(macho_file); |
| 1784 | if (atom_sym.n_desc == MachO.N_DESC_GCED) continue; |
| 1785 | const target_sym = macho_file.getSymbol(entry.target); |
| 1786 | if (target_sym.undf()) { |
| 1787 | try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?); |
| 1788 | } else { |
| 1789 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); |
| 1790 | } |
| 1791 | } |
| 1792 | } |
| 1793 | |
| 1794 | if (macho_file.la_symbol_ptr_section_index) |sect_id| { |
| 1795 | const la_symbol_ptr = &macho_file.sections.items(.header)[sect_id]; |
| 1796 | la_symbol_ptr.reserved1 = nstubs + ngot_entries; |
| 1797 | for (macho_file.stubs.items) |entry| { |
| 1798 | if (entry.sym_index == 0) continue; |
| 1799 | const atom_sym = entry.getSymbol(macho_file); |
| 1800 | if (atom_sym.n_desc == MachO.N_DESC_GCED) continue; |
| 1801 | const target_sym = macho_file.getSymbol(entry.target); |
| 1802 | assert(target_sym.undf()); |
| 1803 | try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?); |
| 1804 | } |
| 1805 | } |
| 1806 | |
| 1807 | assert(buf.items.len == needed_size); |
| 1808 | try macho_file.base.file.?.pwriteAll(buf.items, offset); |
| 1809 | |
| 1810 | lc.nlocalsym = ctx.nlocalsym; |
| 1811 | lc.iextdefsym = iextdefsym; |
| 1812 | lc.nextdefsym = ctx.nextdefsym; |
| 1813 | lc.iundefsym = iundefsym; |
| 1814 | lc.nundefsym = ctx.nundefsym; |
| 1815 | lc.indirectsymoff = @intCast(u32, offset); |
| 1816 | lc.nindirectsyms = nindirectsyms; |
| 1817 | } |
| 1818 | |
| 1819 | fn writeCodeSignaturePadding( |
| 1820 | macho_file: *MachO, |
| 1821 | code_sig: *CodeSignature, |
| 1822 | ncmds: *u32, |
| 1823 | lc_writer: anytype, |
| 1824 | ) !u32 { |
| 1825 | const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?]; |
| 1826 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file |
| 1827 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 |
| 1828 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16); |
| 1829 | const needed_size = code_sig.estimateSize(offset); |
| 1830 | seg.filesize = offset + needed_size - seg.fileoff; |
| 1831 | seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size); |
| 1832 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 1833 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| 1834 | // except for code signature data. |
| 1835 | try macho_file.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1); |
| 1836 | |
| 1837 | try lc_writer.writeStruct(macho.linkedit_data_command{ |
| 1838 | .cmd = .CODE_SIGNATURE, |
| 1839 | .cmdsize = @sizeOf(macho.linkedit_data_command), |
| 1840 | .dataoff = @intCast(u32, offset), |
| 1841 | .datasize = @intCast(u32, needed_size), |
| 1842 | }); |
| 1843 | ncmds.* += 1; |
| 1844 | |
| 1845 | return @intCast(u32, offset); |
| 1846 | } |
| 1847 | |
| 1848 | fn writeCodeSignature(macho_file: *MachO, code_sig: *CodeSignature, offset: u32) !void { |
| 1849 | const seg = macho_file.segments.items[macho_file.text_segment_cmd_index.?]; |
| 1850 | |
| 1851 | var buffer = std.ArrayList(u8).init(macho_file.base.allocator); |
| 1852 | defer buffer.deinit(); |
| 1853 | try buffer.ensureTotalCapacityPrecise(code_sig.size()); |
| 1854 | try code_sig.writeAdhocSignature(macho_file.base.allocator, .{ |
| 1855 | .file = macho_file.base.file.?, |
| 1856 | .exec_seg_base = seg.fileoff, |
| 1857 | .exec_seg_limit = seg.filesize, |
| 1858 | .file_size = offset, |
| 1859 | .output_mode = macho_file.base.options.output_mode, |
| 1860 | }, buffer.writer()); |
| 1861 | assert(buffer.items.len == code_sig.size()); |
| 1862 | |
| 1863 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ |
| 1864 | offset, |
| 1865 | offset + buffer.items.len, |
| 1866 | }); |
| 1867 | |
| 1868 | try macho_file.base.file.?.pwriteAll(buffer.items, offset); |
| 1869 | } |
| 1870 | |
| 1871 | fn writeSegmentHeaders(macho_file: *MachO, ncmds: *u32, writer: anytype) !void { |
| 1872 | for (macho_file.segments.items) |seg, i| { |
| 1873 | const indexes = macho_file.getSectionIndexes(@intCast(u8, i)); |
| 1874 | var out_seg = seg; |
| 1875 | out_seg.cmdsize = @sizeOf(macho.segment_command_64); |
| 1876 | out_seg.nsects = 0; |
| 1877 | |
| 1878 | // Update section headers count; any section with size of 0 is excluded |
| 1879 | // since it doesn't have any data in the final binary file. |
| 1880 | for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| { |
| 1881 | if (header.size == 0) continue; |
| 1882 | out_seg.cmdsize += @sizeOf(macho.section_64); |
| 1883 | out_seg.nsects += 1; |
| 1884 | } |
| 1885 | |
| 1886 | if (out_seg.nsects == 0 and |
| 1887 | (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or |
| 1888 | mem.eql(u8, out_seg.segName(), "__DATA"))) continue; |
| 1889 | |
| 1890 | try writer.writeStruct(out_seg); |
| 1891 | for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| { |
| 1892 | if (header.size == 0) continue; |
| 1893 | try writer.writeStruct(header); |
| 1894 | } |
| 1895 | |
| 1896 | ncmds.* += 1; |
| 1897 | } |
| 1898 | } |
| 1899 | |
| 1900 | /// Writes Mach-O file header. |
| 1901 | fn writeHeader(macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 1902 | var header: macho.mach_header_64 = .{}; |
| 1903 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; |
| 1904 | |
| 1905 | switch (macho_file.base.options.target.cpu.arch) { |
| 1906 | .aarch64 => { |
| 1907 | header.cputype = macho.CPU_TYPE_ARM64; |
| 1908 | header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; |
| 1909 | }, |
| 1910 | .x86_64 => { |
| 1911 | header.cputype = macho.CPU_TYPE_X86_64; |
| 1912 | header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; |
| 1913 | }, |
| 1914 | else => return error.UnsupportedCpuArchitecture, |
| 1915 | } |
| 1916 | |
| 1917 | switch (macho_file.base.options.output_mode) { |
| 1918 | .Exe => { |
| 1919 | header.filetype = macho.MH_EXECUTE; |
| 1920 | }, |
| 1921 | .Lib => { |
| 1922 | // By this point, it can only be a dylib. |
| 1923 | header.filetype = macho.MH_DYLIB; |
| 1924 | header.flags |= macho.MH_NO_REEXPORTED_DYLIBS; |
| 1925 | }, |
| 1926 | else => unreachable, |
| 1927 | } |
| 1928 | |
| 1929 | if (macho_file.getSectionByName("__DATA", "__thread_vars")) |sect_id| { |
| 1930 | if (macho_file.sections.items(.header)[sect_id].size > 0) { |
| 1931 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; |
| 1932 | } |
| 1933 | } |
| 1934 | |
| 1935 | header.ncmds = ncmds; |
| 1936 | header.sizeofcmds = sizeofcmds; |
| 1937 | |
| 1938 | log.debug("writing Mach-O header {}", .{header}); |
| 1939 | |
| 1940 | try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0); |
| 1941 | } |