| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const build_options = @import("build_options"); | 2 | const build_options = @import("build_options"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| | 4 | const dwarf = std.dwarf; |
| 4 | const fs = std.fs; | 5 | const fs = std.fs; |
| 5 | const log = std.log.scoped(.link); | 6 | const log = std.log.scoped(.link); |
| 6 | const macho = std.macho; | 7 | const macho = std.macho; |
| ... | @@ -18,6 +19,7 @@ const CodeSignature = @import("CodeSignature.zig"); | ... | @@ -18,6 +19,7 @@ const CodeSignature = @import("CodeSignature.zig"); |
| 18 | const Compilation = @import("../../Compilation.zig"); | 19 | const Compilation = @import("../../Compilation.zig"); |
| 19 | const Dylib = @import("Dylib.zig"); | 20 | const Dylib = @import("Dylib.zig"); |
| 20 | const MachO = @import("../MachO.zig"); | 21 | const MachO = @import("../MachO.zig"); |
| | 22 | const Object = @import("Object.zig"); |
| 21 | const SymbolWithLoc = MachO.SymbolWithLoc; | 23 | const SymbolWithLoc = MachO.SymbolWithLoc; |
| 22 | const Trie = @import("Trie.zig"); | 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,20 +620,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 618 | if (macho_file.base.options.entitlements) |path| { | 620 | if (macho_file.base.options.entitlements) |path| { |
| 619 | try codesig.addEntitlements(arena, path); | 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 | break :blk codesig; | 624 | break :blk codesig; |
| 623 | } else null; | 625 | } else null; |
| 624 | | 626 | |
| 625 | var headers_buf = std.ArrayList(u8).init(arena); | 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 | try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64)); | 630 | try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64)); |
| 629 | try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len); | 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 | if (codesig) |*csig| { | 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,9 +966,9 @@ fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) |
| 964 | seg.vmsize = 0; | 966 | seg.vmsize = 0; |
| 965 | | 967 | |
| 966 | try writeDyldInfoData(macho_file, ncmds, lc_writer); | 968 | try writeDyldInfoData(macho_file, ncmds, lc_writer); |
| 967 | try macho_file.writeFunctionStarts(ncmds, lc_writer); | 969 | try writeFunctionStarts(macho_file, ncmds, lc_writer); |
| 968 | try macho_file.writeDataInCode(ncmds, lc_writer); | 970 | try writeDataInCode(macho_file, ncmds, lc_writer); |
| 969 | try macho_file.writeSymtabs(ncmds, lc_writer); | 971 | try writeSymtabs(macho_file, ncmds, lc_writer); |
| 970 | | 972 | |
| 971 | seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size); | 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,3 +1282,660 @@ fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) ! |
| 1280 | try macho_file.base.file.?.pwriteAll(&buf, file_offset); | 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 | } |