authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-14 01:08:51+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-14 01:08:51+01:00
log9ec0cf23ca5e26e7d6a8f0f053505b05b56bf645
tree6f5c3ec4a83091c25cf97f50a41c2307c04f5fe8
parent8addf53fb5046a65c6fd0c0d2e7894be60468132
parentc22bb3805821d7ffe60e048f1efe362aad703668
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18905 from ziglang/elf-mem-pressure

elf: reduce memory pressure

12 files changed, 961 insertions(+), 848 deletions(-)

CMakeLists.txt+1
...@@ -593,6 +593,7 @@ set(ZIG_STAGE2_SOURCES...@@ -593,6 +593,7 @@ set(ZIG_STAGE2_SOURCES
593 "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"593 "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"594 "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"
595 "${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig"595 "${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/Elf/relocatable.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"597 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"
597 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"598 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
598 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"599 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
src/link/Elf.zig+83-556
...@@ -35,6 +35,10 @@ llvm_object: ?*LlvmObject = null,...@@ -35,6 +35,10 @@ llvm_object: ?*LlvmObject = null,
35/// Index of each input file also encodes the priority or precedence of one input file35/// Index of each input file also encodes the priority or precedence of one input file
36/// over another.36/// over another.
37files: std.MultiArrayList(File.Entry) = .{},37files: std.MultiArrayList(File.Entry) = .{},
38/// Long-lived list of all file descriptors.
39/// We store them globally rather than per actual File so that we can re-use
40/// one file handle per every object file within an archive.
41file_handles: std.ArrayListUnmanaged(File.Handle) = .{},
38zig_object_index: ?File.Index = null,42zig_object_index: ?File.Index = null,
39linker_defined_index: ?File.Index = null,43linker_defined_index: ?File.Index = null,
40objects: std.ArrayListUnmanaged(File.Index) = .{},44objects: std.ArrayListUnmanaged(File.Index) = .{},
...@@ -444,6 +448,11 @@ pub fn deinit(self: *Elf) void {...@@ -444,6 +448,11 @@ pub fn deinit(self: *Elf) void {
444448
445 if (self.llvm_object) |llvm_object| llvm_object.deinit();449 if (self.llvm_object) |llvm_object| llvm_object.deinit();
446450
451 for (self.file_handles.items) |fh| {
452 fh.close();
453 }
454 self.file_handles.deinit(gpa);
455
447 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {456 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
448 .null => {},457 .null => {},
449 .zig_object => data.zig_object.deinit(gpa),458 .zig_object => data.zig_object.deinit(gpa),
...@@ -561,7 +570,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -561,7 +570,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
561 return null;570 return null;
562}571}
563572
564fn allocatedSize(self: *Elf, start: u64) u64 {573pub fn allocatedSize(self: *Elf, start: u64) u64 {
565 if (start == 0) return 0;574 if (start == 0) return 0;
566 var min_pos: u64 = std.math.maxInt(u64);575 var min_pos: u64 = std.math.maxInt(u64);
567 if (self.shdr_table_offset) |off| {576 if (self.shdr_table_offset) |off| {
...@@ -588,7 +597,7 @@ fn allocatedVirtualSize(self: *Elf, start: u64) u64 {...@@ -588,7 +597,7 @@ fn allocatedVirtualSize(self: *Elf, start: u64) u64 {
588 return min_pos - start;597 return min_pos - start;
589}598}
590599
591fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {600pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
592 var start: u64 = 0;601 var start: u64 = 0;
593 while (self.detectAllocCollision(start, object_size)) |item_end| {602 while (self.detectAllocCollision(start, object_size)) |item_end| {
594 start = mem.alignForward(u64, item_end, min_alignment);603 start = mem.alignForward(u64, item_end, min_alignment);
...@@ -1066,6 +1075,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1066,6 +1075,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1066 // --verbose-link1075 // --verbose-link
1067 if (comp.verbose_link) try self.dumpArgv(comp);1076 if (comp.verbose_link) try self.dumpArgv(comp);
10681077
1078 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1079 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
1080 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
1081
1069 const csu = try CsuObjects.init(arena, comp);1082 const csu = try CsuObjects.init(arena, comp);
1070 const compiler_rt_path: ?[]const u8 = blk: {1083 const compiler_rt_path: ?[]const u8 = blk: {
1071 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;1084 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
...@@ -1073,10 +1086,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1073,10 +1086,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1073 break :blk null;1086 break :blk null;
1074 };1087 };
10751088
1076 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1077 if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path);
1078 if (self.base.isObject()) return self.flushObject(comp, module_obj_path);
1079
1080 // Here we will parse input positional and library files (if referenced).1089 // Here we will parse input positional and library files (if referenced).
1081 // This will roughly match in any linker backend we support.1090 // This will roughly match in any linker backend we support.
1082 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);1091 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
...@@ -1240,16 +1249,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1240,16 +1249,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
12401249
1241 if (comp.link_errors.items.len > 0) return error.FlushFailure;1250 if (comp.link_errors.items.len > 0) return error.FlushFailure;
12421251
1243 // Init all objects
1244 for (self.objects.items) |index| {
1245 try self.file(index).?.object.init(self);
1246 }
1247 for (self.shared_objects.items) |index| {
1248 try self.file(index).?.shared_object.init(self);
1249 }
1250
1251 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1252
1253 // Dedup shared objects1252 // Dedup shared objects
1254 {1253 {
1255 var seen_dsos = std.StringHashMap(void).init(gpa);1254 var seen_dsos = std.StringHashMap(void).init(gpa);
...@@ -1382,222 +1381,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1382,222 +1381,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1382 if (comp.link_errors.items.len > 0) return error.FlushFailure;1381 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1383}1382}
13841383
1385pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
1386 const gpa = comp.gpa;
1387
1388 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
1389 defer positionals.deinit();
1390
1391 try positionals.ensureUnusedCapacity(comp.objects.len);
1392 positionals.appendSliceAssumeCapacity(comp.objects);
1393
1394 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1395 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1396 // in this set.
1397 for (comp.c_object_table.keys()) |key| {
1398 try positionals.append(.{ .path = key.status.success.object_path });
1399 }
1400
1401 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1402
1403 for (positionals.items) |obj| {
1404 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1405 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1406 else => |e| try self.reportParseError(
1407 obj.path,
1408 "unexpected error: parsing input file failed with error {s}",
1409 .{@errorName(e)},
1410 ),
1411 };
1412 }
1413
1414 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1415
1416 // First, we flush relocatable object file generated with our backends.
1417 if (self.zigObjectPtr()) |zig_object| {
1418 zig_object.resolveSymbols(self);
1419 zig_object.claimUnresolvedObject(self);
1420
1421 try self.initSymtab();
1422 try self.initShStrtab();
1423 try self.sortShdrs();
1424 try zig_object.addAtomsToRelaSections(self);
1425 try self.updateSectionSizesObject();
1426
1427 try self.allocateAllocSectionsObject();
1428 try self.allocateNonAllocSections();
1429
1430 if (build_options.enable_logging) {
1431 state_log.debug("{}", .{self.dumpState()});
1432 }
1433
1434 try self.writeSyntheticSectionsObject();
1435 try self.writeShdrTable();
1436 try self.writeElfHeader();
1437
1438 // TODO we can avoid reading in the file contents we just wrote if we give the linker
1439 // ability to write directly to a buffer.
1440 try zig_object.readFileContents(self);
1441 }
1442
1443 var files = std.ArrayList(File.Index).init(gpa);
1444 defer files.deinit();
1445 try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
1446 if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
1447 for (self.objects.items) |index| files.appendAssumeCapacity(index);
1448
1449 // Update ar symtab from parsed objects
1450 var ar_symtab: Archive.ArSymtab = .{};
1451 defer ar_symtab.deinit(gpa);
1452
1453 for (files.items) |index| {
1454 try self.file(index).?.updateArSymtab(&ar_symtab, self);
1455 }
1456
1457 ar_symtab.sort();
1458
1459 // Save object paths in filenames strtab.
1460 var ar_strtab: Archive.ArStrtab = .{};
1461 defer ar_strtab.deinit(gpa);
1462
1463 for (files.items) |index| {
1464 const file_ptr = self.file(index).?;
1465 try file_ptr.updateArStrtab(gpa, &ar_strtab);
1466 file_ptr.updateArSize();
1467 }
1468
1469 // Update file offsets of contributing objects.
1470 const total_size: usize = blk: {
1471 var pos: usize = elf.ARMAG.len;
1472 pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64);
1473
1474 if (ar_strtab.size() > 0) {
1475 pos = mem.alignForward(usize, pos, 2);
1476 pos += @sizeOf(elf.ar_hdr) + ar_strtab.size();
1477 }
1478
1479 for (files.items) |index| {
1480 const file_ptr = self.file(index).?;
1481 const state = switch (file_ptr) {
1482 .zig_object => |x| &x.output_ar_state,
1483 .object => |x| &x.output_ar_state,
1484 else => unreachable,
1485 };
1486 pos = mem.alignForward(usize, pos, 2);
1487 state.file_off = pos;
1488 pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow);
1489 }
1490
1491 break :blk pos;
1492 };
1493
1494 if (build_options.enable_logging) {
1495 state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)});
1496 state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
1497 }
1498
1499 var buffer = std.ArrayList(u8).init(gpa);
1500 defer buffer.deinit();
1501 try buffer.ensureTotalCapacityPrecise(total_size);
1502
1503 // Write magic
1504 try buffer.writer().writeAll(elf.ARMAG);
1505
1506 // Write symtab
1507 try ar_symtab.write(.p64, self, buffer.writer());
1508
1509 // Write strtab
1510 if (ar_strtab.size() > 0) {
1511 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1512 try ar_strtab.write(buffer.writer());
1513 }
1514
1515 // Write object files
1516 for (files.items) |index| {
1517 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1518 try self.file(index).?.writeAr(buffer.writer());
1519 }
1520
1521 assert(buffer.items.len == total_size);
1522
1523 try self.base.file.?.setEndPos(total_size);
1524 try self.base.file.?.pwriteAll(buffer.items, 0);
1525
1526 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1527}
1528
1529pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
1530 const gpa = self.base.comp.gpa;
1531
1532 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
1533 defer positionals.deinit();
1534 try positionals.ensureUnusedCapacity(comp.objects.len);
1535 positionals.appendSliceAssumeCapacity(comp.objects);
1536
1537 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1538 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1539 // in this set.
1540 for (comp.c_object_table.keys()) |key| {
1541 try positionals.append(.{ .path = key.status.success.object_path });
1542 }
1543
1544 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1545
1546 for (positionals.items) |obj| {
1547 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1548 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1549 else => |e| try self.reportParseError(
1550 obj.path,
1551 "unexpected error: parsing input file failed with error {s}",
1552 .{@errorName(e)},
1553 ),
1554 };
1555 }
1556
1557 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1558
1559 // Init all objects
1560 for (self.objects.items) |index| {
1561 try self.file(index).?.object.init(self);
1562 }
1563
1564 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1565
1566 // Now, we are ready to resolve the symbols across all input files.
1567 // We will first resolve the files in the ZigObject, next in the parsed
1568 // input Object files.
1569 self.resolveSymbols();
1570 self.markEhFrameAtomsDead();
1571 self.claimUnresolvedObject();
1572
1573 try self.initSectionsObject();
1574 try self.sortShdrs();
1575 if (self.zigObjectPtr()) |zig_object| {
1576 try zig_object.addAtomsToRelaSections(self);
1577 }
1578 for (self.objects.items) |index| {
1579 const object = self.file(index).?.object;
1580 try object.addAtomsToOutputSections(self);
1581 try object.addAtomsToRelaSections(self);
1582 }
1583 try self.updateSectionSizesObject();
1584
1585 try self.allocateAllocSectionsObject();
1586 try self.allocateNonAllocSections();
1587 self.allocateAtoms();
1588
1589 if (build_options.enable_logging) {
1590 state_log.debug("{}", .{self.dumpState()});
1591 }
1592
1593 try self.writeAtomsObject();
1594 try self.writeSyntheticSectionsObject();
1595 try self.writeShdrTable();
1596 try self.writeElfHeader();
1597
1598 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1599}
1600
1601/// --verbose-link output1384/// --verbose-link output
1602fn dumpArgv(self: *Elf, comp: *Compilation) !void {1385fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1603 const gpa = self.base.comp.gpa;1386 const gpa = self.base.comp.gpa;
...@@ -1861,7 +1644,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1861,7 +1644,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1861 Compilation.dump_argv(argv.items);1644 Compilation.dump_argv(argv.items);
1862}1645}
18631646
1864const ParseError = error{1647pub const ParseError = error{
1865 MalformedObject,1648 MalformedObject,
1866 MalformedArchive,1649 MalformedArchive,
1867 InvalidCpuArch,1650 InvalidCpuArch,
...@@ -1872,9 +1655,10 @@ const ParseError = error{...@@ -1872,9 +1655,10 @@ const ParseError = error{
1872 FileSystem,1655 FileSystem,
1873 NotSupported,1656 NotSupported,
1874 InvalidCharacter,1657 InvalidCharacter,
1658 UnknownFileType,
1875} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;1659} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
18761660
1877fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {1661pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1878 const tracy = trace(@src());1662 const tracy = trace(@src());
1879 defer tracy.end();1663 defer tracy.end();
1880 if (try Object.isObject(path)) {1664 if (try Object.isObject(path)) {
...@@ -1902,13 +1686,13 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {...@@ -1902,13 +1686,13 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
1902 defer tracy.end();1686 defer tracy.end();
19031687
1904 const gpa = self.base.comp.gpa;1688 const gpa = self.base.comp.gpa;
1905 const in_file = try std.fs.cwd().openFile(path, .{});1689 const handle = try std.fs.cwd().openFile(path, .{});
1906 defer in_file.close();1690 const fh = try self.addFileHandle(handle);
1907 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));1691
1908 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1692 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1909 self.files.set(index, .{ .object = .{1693 self.files.set(index, .{ .object = .{
1910 .path = try gpa.dupe(u8, path),1694 .path = try gpa.dupe(u8, path),
1911 .data = data,1695 .file_handle = fh,
1912 .index = index,1696 .index = index,
1913 } });1697 } });
1914 try self.objects.append(gpa, index);1698 try self.objects.append(gpa, index);
...@@ -1922,12 +1706,12 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {...@@ -1922,12 +1706,12 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1922 defer tracy.end();1706 defer tracy.end();
19231707
1924 const gpa = self.base.comp.gpa;1708 const gpa = self.base.comp.gpa;
1925 const in_file = try std.fs.cwd().openFile(path, .{});1709 const handle = try std.fs.cwd().openFile(path, .{});
1926 defer in_file.close();1710 const fh = try self.addFileHandle(handle);
1927 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));1711
1928 var archive = Archive{ .path = try gpa.dupe(u8, path), .data = data };1712 var archive = Archive{};
1929 defer archive.deinit(gpa);1713 defer archive.deinit(gpa);
1930 try archive.parse(self);1714 try archive.parse(self, path, fh);
19311715
1932 const objects = try archive.objects.toOwnedSlice(gpa);1716 const objects = try archive.objects.toOwnedSlice(gpa);
1933 defer gpa.free(objects);1717 defer gpa.free(objects);
...@@ -1948,13 +1732,12 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {...@@ -1948,13 +1732,12 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
1948 defer tracy.end();1732 defer tracy.end();
19491733
1950 const gpa = self.base.comp.gpa;1734 const gpa = self.base.comp.gpa;
1951 const in_file = try std.fs.cwd().openFile(lib.path, .{});1735 const handle = try std.fs.cwd().openFile(lib.path, .{});
1952 defer in_file.close();1736 defer handle.close();
1953 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));1737
1954 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1738 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1955 self.files.set(index, .{ .shared_object = .{1739 self.files.set(index, .{ .shared_object = .{
1956 .path = try gpa.dupe(u8, lib.path),1740 .path = try gpa.dupe(u8, lib.path),
1957 .data = data,
1958 .index = index,1741 .index = index,
1959 .needed = lib.needed,1742 .needed = lib.needed,
1960 .alive = lib.needed,1743 .alive = lib.needed,
...@@ -1962,7 +1745,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {...@@ -1962,7 +1745,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
1962 try self.shared_objects.append(gpa, index);1745 try self.shared_objects.append(gpa, index);
19631746
1964 const shared_object = self.file(index).?.shared_object;1747 const shared_object = self.file(index).?.shared_object;
1965 try shared_object.parse(self);1748 try shared_object.parse(self, handle);
1966}1749}
19671750
1968fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {1751fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
...@@ -2084,7 +1867,7 @@ fn accessLibPath(...@@ -2084,7 +1867,7 @@ fn accessLibPath(
2084/// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.1867/// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.
2085/// 5. Remove references to dead objects/shared objects1868/// 5. Remove references to dead objects/shared objects
2086/// 6. Re-run symbol resolution on pruned objects and shared objects sets.1869/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
2087fn resolveSymbols(self: *Elf) void {1870pub fn resolveSymbols(self: *Elf) void {
2088 // Resolve symbols in the ZigObject. For now, we assume that it's always live.1871 // Resolve symbols in the ZigObject. For now, we assume that it's always live.
2089 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);1872 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
2090 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).1873 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
...@@ -2135,7 +1918,7 @@ fn resolveSymbols(self: *Elf) void {...@@ -2135,7 +1918,7 @@ fn resolveSymbols(self: *Elf) void {
2135 const cg = self.comdatGroup(cg_index);1918 const cg = self.comdatGroup(cg_index);
2136 const cg_owner = self.comdatGroupOwner(cg.owner);1919 const cg_owner = self.comdatGroupOwner(cg.owner);
2137 if (cg_owner.file != index) {1920 if (cg_owner.file != index) {
2138 for (object.comdatGroupMembers(cg.shndx)) |shndx| {1921 for (cg.comdatGroupMembers(self)) |shndx| {
2139 const atom_index = object.atoms.items[shndx];1922 const atom_index = object.atoms.items[shndx];
2140 if (self.atom(atom_index)) |atom_ptr| {1923 if (self.atom(atom_index)) |atom_ptr| {
2141 atom_ptr.flags.alive = false;1924 atom_ptr.flags.alive = false;
...@@ -2168,7 +1951,7 @@ fn markLive(self: *Elf) void {...@@ -2168,7 +1951,7 @@ fn markLive(self: *Elf) void {
2168 }1951 }
2169}1952}
21701953
2171fn markEhFrameAtomsDead(self: *Elf) void {1954pub fn markEhFrameAtomsDead(self: *Elf) void {
2172 for (self.objects.items) |index| {1955 for (self.objects.items) |index| {
2173 const file_ptr = self.file(index).?;1956 const file_ptr = self.file(index).?;
2174 if (!file_ptr.isAlive()) continue;1957 if (!file_ptr.isAlive()) continue;
...@@ -2234,15 +2017,6 @@ fn claimUnresolved(self: *Elf) void {...@@ -2234,15 +2017,6 @@ fn claimUnresolved(self: *Elf) void {
2234 }2017 }
2235}2018}
22362019
2237fn claimUnresolvedObject(self: *Elf) void {
2238 if (self.zigObjectPtr()) |zig_object| {
2239 zig_object.claimUnresolvedObject(self);
2240 }
2241 for (self.objects.items) |index| {
2242 self.file(index).?.object.claimUnresolvedObject(self);
2243 }
2244}
2245
2246/// In scanRelocs we will go over all live atoms and scan their relocs.2020/// In scanRelocs we will go over all live atoms and scan their relocs.
2247/// This will help us work out what synthetics to emit, GOT indirection, etc.2021/// This will help us work out what synthetics to emit, GOT indirection, etc.
2248/// This is also the point where we will report undefined symbols for any2022/// This is also the point where we will report undefined symbols for any
...@@ -2980,7 +2754,7 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)...@@ -2980,7 +2754,7 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)
2980 }2754 }
2981}2755}
29822756
2983fn writeShdrTable(self: *Elf) !void {2757pub fn writeShdrTable(self: *Elf) !void {
2984 const gpa = self.base.comp.gpa;2758 const gpa = self.base.comp.gpa;
2985 const target = self.base.comp.root_mod.resolved_target.result;2759 const target = self.base.comp.root_mod.resolved_target.result;
2986 const target_endian = target.cpu.arch.endian();2760 const target_endian = target.cpu.arch.endian();
...@@ -3077,7 +2851,7 @@ fn writePhdrTable(self: *Elf) !void {...@@ -3077,7 +2851,7 @@ fn writePhdrTable(self: *Elf) !void {
3077 }2851 }
3078}2852}
30792853
3080fn writeElfHeader(self: *Elf) !void {2854pub fn writeElfHeader(self: *Elf) !void {
3081 const comp = self.base.comp;2855 const comp = self.base.comp;
3082 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable2856 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
30832857
...@@ -3653,61 +3427,7 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -3653,61 +3427,7 @@ fn initSyntheticSections(self: *Elf) !void {
3653 try self.initShStrtab();3427 try self.initShStrtab();
3654}3428}
36553429
3656fn initSectionsObject(self: *Elf) !void {3430pub fn initSymtab(self: *Elf) !void {
3657 const ptr_size = self.ptrWidthBytes();
3658
3659 for (self.objects.items) |index| {
3660 const object = self.file(index).?.object;
3661 try object.initOutputSections(self);
3662 try object.initRelaSections(self);
3663 }
3664
3665 const needs_eh_frame = for (self.objects.items) |index| {
3666 if (self.file(index).?.object.cies.items.len > 0) break true;
3667 } else false;
3668 if (needs_eh_frame) {
3669 self.eh_frame_section_index = try self.addSection(.{
3670 .name = ".eh_frame",
3671 .type = elf.SHT_PROGBITS,
3672 .flags = elf.SHF_ALLOC,
3673 .addralign = ptr_size,
3674 .offset = std.math.maxInt(u64),
3675 });
3676 self.eh_frame_rela_section_index = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?);
3677 }
3678
3679 try self.initComdatGroups();
3680 try self.initSymtab();
3681 try self.initShStrtab();
3682}
3683
3684fn initComdatGroups(self: *Elf) !void {
3685 const gpa = self.base.comp.gpa;
3686
3687 for (self.objects.items) |index| {
3688 const object = self.file(index).?.object;
3689
3690 for (object.comdat_groups.items) |cg_index| {
3691 const cg = self.comdatGroup(cg_index);
3692 const cg_owner = self.comdatGroupOwner(cg.owner);
3693 if (cg_owner.file != index) continue;
3694
3695 const cg_sec = try self.comdat_group_sections.addOne(gpa);
3696 cg_sec.* = .{
3697 .shndx = try self.addSection(.{
3698 .name = ".group",
3699 .type = elf.SHT_GROUP,
3700 .entsize = @sizeOf(u32),
3701 .addralign = @alignOf(u32),
3702 .offset = std.math.maxInt(u64),
3703 }),
3704 .cg_index = cg_index,
3705 };
3706 }
3707 }
3708}
3709
3710fn initSymtab(self: *Elf) !void {
3711 const small_ptr = switch (self.ptr_width) {3431 const small_ptr = switch (self.ptr_width) {
3712 .p32 => true,3432 .p32 => true,
3713 .p64 => false,3433 .p64 => false,
...@@ -3732,7 +3452,7 @@ fn initSymtab(self: *Elf) !void {...@@ -3732,7 +3452,7 @@ fn initSymtab(self: *Elf) !void {
3732 }3452 }
3733}3453}
37343454
3735fn initShStrtab(self: *Elf) !void {3455pub fn initShStrtab(self: *Elf) !void {
3736 if (self.shstrtab_section_index == null) {3456 if (self.shstrtab_section_index == null) {
3737 self.shstrtab_section_index = try self.addSection(.{3457 self.shstrtab_section_index = try self.addSection(.{
3738 .name = ".shstrtab",3458 .name = ".shstrtab",
...@@ -4038,7 +3758,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 {...@@ -4038,7 +3758,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 {
4038 }3758 }
4039}3759}
40403760
4041fn sortShdrs(self: *Elf) !void {3761pub fn sortShdrs(self: *Elf) !void {
4042 const Entry = struct {3762 const Entry = struct {
4043 shndx: u16,3763 shndx: u16,
40443764
...@@ -4350,58 +4070,7 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -4350,58 +4070,7 @@ fn updateSectionSizes(self: *Elf) !void {
4350 self.updateShStrtabSize();4070 self.updateShStrtabSize();
4351}4071}
43524072
4353fn updateSectionSizesObject(self: *Elf) !void {4073pub fn updateShStrtabSize(self: *Elf) void {
4354 for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
4355 const shdr = &self.shdrs.items[shndx];
4356 for (atom_list.items) |atom_index| {
4357 const atom_ptr = self.atom(atom_index) orelse continue;
4358 if (!atom_ptr.flags.alive) continue;
4359 const offset = atom_ptr.alignment.forward(shdr.sh_size);
4360 const padding = offset - shdr.sh_size;
4361 atom_ptr.value = offset;
4362 shdr.sh_size += padding + atom_ptr.size;
4363 shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
4364 }
4365 }
4366
4367 for (self.output_rela_sections.values()) |sec| {
4368 const shdr = &self.shdrs.items[sec.shndx];
4369 for (sec.atom_list.items) |atom_index| {
4370 const atom_ptr = self.atom(atom_index) orelse continue;
4371 if (!atom_ptr.flags.alive) continue;
4372 const relocs = atom_ptr.relocs(self);
4373 shdr.sh_size += shdr.sh_entsize * relocs.len;
4374 }
4375
4376 if (shdr.sh_size == 0) shdr.sh_offset = 0;
4377 }
4378
4379 if (self.eh_frame_section_index) |index| {
4380 self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
4381 }
4382 if (self.eh_frame_rela_section_index) |index| {
4383 const shdr = &self.shdrs.items[index];
4384 shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize;
4385 }
4386
4387 try self.updateSymtabSize();
4388 self.updateComdatGroupsSizes();
4389 self.updateShStrtabSize();
4390}
4391
4392fn updateComdatGroupsSizes(self: *Elf) void {
4393 for (self.comdat_group_sections.items) |cg| {
4394 const shdr = &self.shdrs.items[cg.shndx];
4395 shdr.sh_size = cg.size(self);
4396 shdr.sh_link = self.symtab_section_index.?;
4397
4398 const sym = self.symbol(cg.symbol(self));
4399 shdr.sh_info = sym.outputSymtabIndex(self) orelse
4400 self.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
4401 }
4402}
4403
4404fn updateShStrtabSize(self: *Elf) void {
4405 if (self.shstrtab_section_index) |index| {4074 if (self.shstrtab_section_index) |index| {
4406 self.shdrs.items[index].sh_size = self.shstrtab.items.len;4075 self.shdrs.items[index].sh_size = self.shstrtab.items.len;
4407 }4076 }
...@@ -4486,7 +4155,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {...@@ -4486,7 +4155,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
44864155
4487/// Allocates alloc sections and creates load segments for sections4156/// Allocates alloc sections and creates load segments for sections
4488/// extracted from input object files.4157/// extracted from input object files.
4489fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {4158pub fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
4490 // We use this struct to track maximum alignment of all TLS sections.4159 // We use this struct to track maximum alignment of all TLS sections.
4491 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,4160 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
4492 // in-file offsets have to be aligned against the start of TLS program header.4161 // in-file offsets have to be aligned against the start of TLS program header.
...@@ -4633,27 +4302,8 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {...@@ -4633,27 +4302,8 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
4633 }4302 }
4634}4303}
46354304
4636/// Allocates alloc sections when merging relocatable objects files together.
4637fn allocateAllocSectionsObject(self: *Elf) !void {
4638 for (self.shdrs.items) |*shdr| {
4639 if (shdr.sh_type == elf.SHT_NULL) continue;
4640 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4641 if (shdr.sh_type == elf.SHT_NOBITS) {
4642 shdr.sh_offset = 0;
4643 continue;
4644 }
4645 const needed_size = shdr.sh_size;
4646 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
4647 shdr.sh_size = 0;
4648 const new_offset = self.findFreeSpace(needed_size, shdr.sh_addralign);
4649 shdr.sh_offset = new_offset;
4650 shdr.sh_size = needed_size;
4651 }
4652 }
4653}
4654
4655/// Allocates non-alloc sections (debug info, symtabs, etc.).4305/// Allocates non-alloc sections (debug info, symtabs, etc.).
4656fn allocateNonAllocSections(self: *Elf) !void {4306pub fn allocateNonAllocSections(self: *Elf) !void {
4657 for (self.shdrs.items, 0..) |*shdr, shndx| {4307 for (self.shdrs.items, 0..) |*shdr, shndx| {
4658 if (shdr.sh_type == elf.SHT_NULL) continue;4308 if (shdr.sh_type == elf.SHT_NULL) continue;
4659 if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue;4309 if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue;
...@@ -4752,7 +4402,7 @@ fn allocateSpecialPhdrs(self: *Elf) void {...@@ -4752,7 +4402,7 @@ fn allocateSpecialPhdrs(self: *Elf) void {
4752 }4402 }
4753}4403}
47544404
4755fn allocateAtoms(self: *Elf) void {4405pub fn allocateAtoms(self: *Elf) void {
4756 if (self.zigObjectPtr()) |zig_object| {4406 if (self.zigObjectPtr()) |zig_object| {
4757 zig_object.allocateTlvAtoms(self);4407 zig_object.allocateTlvAtoms(self);
4758 }4408 }
...@@ -4849,76 +4499,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -4849,76 +4499,7 @@ fn writeAtoms(self: *Elf) !void {
4849 try self.reportUndefinedSymbols(&undefs);4499 try self.reportUndefinedSymbols(&undefs);
4850}4500}
48514501
4852fn writeAtomsObject(self: *Elf) !void {4502pub fn updateSymtabSize(self: *Elf) !void {
4853 const gpa = self.base.comp.gpa;
4854
4855 // TODO iterate over `output_sections` directly
4856 for (self.shdrs.items, 0..) |shdr, shndx| {
4857 if (shdr.sh_type == elf.SHT_NULL) continue;
4858 if (shdr.sh_type == elf.SHT_NOBITS) continue;
4859
4860 const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
4861 if (atom_list.items.len == 0) continue;
4862
4863 log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
4864
4865 // TODO really, really handle debug section separately
4866 const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
4867 const zig_object = self.zigObjectPtr().?;
4868 if (shndx == self.debug_info_section_index.?)
4869 break :blk zig_object.debug_info_section_zig_size;
4870 if (shndx == self.debug_abbrev_section_index.?)
4871 break :blk zig_object.debug_abbrev_section_zig_size;
4872 if (shndx == self.debug_str_section_index.?)
4873 break :blk zig_object.debug_str_section_zig_size;
4874 if (shndx == self.debug_aranges_section_index.?)
4875 break :blk zig_object.debug_aranges_section_zig_size;
4876 if (shndx == self.debug_line_section_index.?)
4877 break :blk zig_object.debug_line_section_zig_size;
4878 unreachable;
4879 } else 0;
4880 const sh_offset = shdr.sh_offset + base_offset;
4881 const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
4882
4883 const buffer = try gpa.alloc(u8, sh_size);
4884 defer gpa.free(buffer);
4885 const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
4886 shdr.sh_flags & elf.SHF_EXECINSTR != 0)
4887 0xcc // int3
4888 else
4889 0;
4890 @memset(buffer, padding_byte);
4891
4892 for (atom_list.items) |atom_index| {
4893 const atom_ptr = self.atom(atom_index).?;
4894 assert(atom_ptr.flags.alive);
4895
4896 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
4897 return error.Overflow;
4898 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
4899
4900 log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
4901 atom_index,
4902 sh_offset + offset,
4903 sh_offset + offset + size,
4904 });
4905
4906 // TODO decompress directly into provided buffer
4907 const out_code = buffer[offset..][0..size];
4908 const in_code = switch (atom_ptr.file(self).?) {
4909 .object => |x| try x.codeDecompressAlloc(self, atom_index),
4910 .zig_object => |x| try x.codeAlloc(self, atom_index),
4911 else => unreachable,
4912 };
4913 defer gpa.free(in_code);
4914 @memcpy(out_code, in_code);
4915 }
4916
4917 try self.base.file.?.pwriteAll(buffer, sh_offset);
4918 }
4919}
4920
4921fn updateSymtabSize(self: *Elf) !void {
4922 var nlocals: u32 = 0;4503 var nlocals: u32 = 0;
4923 var nglobals: u32 = 0;4504 var nglobals: u32 = 0;
4924 var strsize: u32 = 0;4505 var strsize: u32 = 0;
...@@ -5136,94 +4717,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -5136,94 +4717,7 @@ fn writeSyntheticSections(self: *Elf) !void {
5136 try self.writeShStrtab();4717 try self.writeShStrtab();
5137}4718}
51384719
5139fn writeSyntheticSectionsObject(self: *Elf) !void {4720pub fn writeShStrtab(self: *Elf) !void {
5140 const gpa = self.base.comp.gpa;
5141
5142 for (self.output_rela_sections.values()) |sec| {
5143 if (sec.atom_list.items.len == 0) continue;
5144
5145 const shdr = self.shdrs.items[sec.shndx];
5146
5147 const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
5148 return error.Overflow;
5149 var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
5150 defer relocs.deinit();
5151
5152 for (sec.atom_list.items) |atom_index| {
5153 const atom_ptr = self.atom(atom_index) orelse continue;
5154 if (!atom_ptr.flags.alive) continue;
5155 try atom_ptr.writeRelocs(self, &relocs);
5156 }
5157 assert(relocs.items.len == num_relocs);
5158
5159 const SortRelocs = struct {
5160 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
5161 _ = ctx;
5162 return lhs.r_offset < rhs.r_offset;
5163 }
5164 };
5165
5166 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
5167
5168 log.debug("writing {s} from 0x{x} to 0x{x}", .{
5169 self.getShString(shdr.sh_name),
5170 shdr.sh_offset,
5171 shdr.sh_offset + shdr.sh_size,
5172 });
5173
5174 try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
5175 }
5176
5177 if (self.eh_frame_section_index) |shndx| {
5178 const shdr = self.shdrs.items[shndx];
5179 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
5180 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5181 defer buffer.deinit();
5182 try eh_frame.writeEhFrameObject(self, buffer.writer());
5183 log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
5184 shdr.sh_offset,
5185 shdr.sh_offset + shdr.sh_size,
5186 });
5187 assert(buffer.items.len == sh_size);
5188 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5189 }
5190 if (self.eh_frame_rela_section_index) |shndx| {
5191 const shdr = self.shdrs.items[shndx];
5192 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
5193 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5194 defer buffer.deinit();
5195 try eh_frame.writeEhFrameRelocs(self, buffer.writer());
5196 assert(buffer.items.len == sh_size);
5197 log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
5198 shdr.sh_offset,
5199 shdr.sh_offset + shdr.sh_size,
5200 });
5201 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5202 }
5203
5204 try self.writeComdatGroups();
5205 try self.writeSymtab();
5206 try self.writeShStrtab();
5207}
5208
5209fn writeComdatGroups(self: *Elf) !void {
5210 const gpa = self.base.comp.gpa;
5211 for (self.comdat_group_sections.items) |cgs| {
5212 const shdr = self.shdrs.items[cgs.shndx];
5213 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
5214 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5215 defer buffer.deinit();
5216 try cgs.write(self, buffer.writer());
5217 assert(buffer.items.len == sh_size);
5218 log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{
5219 shdr.sh_offset,
5220 shdr.sh_offset + shdr.sh_size,
5221 });
5222 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5223 }
5224}
5225
5226fn writeShStrtab(self: *Elf) !void {
5227 if (self.shstrtab_section_index) |index| {4721 if (self.shstrtab_section_index) |index| {
5228 const shdr = self.shdrs.items[index];4722 const shdr = self.shdrs.items[index];
5229 log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });4723 log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });
...@@ -5231,7 +4725,7 @@ fn writeShStrtab(self: *Elf) !void {...@@ -5231,7 +4725,7 @@ fn writeShStrtab(self: *Elf) !void {
5231 }4725 }
5232}4726}
52334727
5234fn writeSymtab(self: *Elf) !void {4728pub fn writeSymtab(self: *Elf) !void {
5235 const target = self.base.comp.root_mod.resolved_target.result;4729 const target = self.base.comp.root_mod.resolved_target.result;
5236 const gpa = self.base.comp.gpa;4730 const gpa = self.base.comp.gpa;
5237 const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];4731 const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];
...@@ -5362,7 +4856,7 @@ pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 {...@@ -5362,7 +4856,7 @@ pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 {
5362}4856}
53634857
5364/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.4858/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
5365fn ptrWidthBytes(self: Elf) u8 {4859pub fn ptrWidthBytes(self: Elf) u8 {
5366 return switch (self.ptr_width) {4860 return switch (self.ptr_width) {
5367 .p32 => 4,4861 .p32 => 4,
5368 .p64 => 8,4862 .p64 => 8,
...@@ -5708,7 +5202,7 @@ fn addPhdr(self: *Elf, opts: struct {...@@ -5708,7 +5202,7 @@ fn addPhdr(self: *Elf, opts: struct {
5708 return index;5202 return index;
5709}5203}
57105204
5711fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {5205pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
5712 const entsize: u64 = switch (self.ptr_width) {5206 const entsize: u64 = switch (self.ptr_width) {
5713 .p32 => @sizeOf(elf.Elf32_Rela),5207 .p32 => @sizeOf(elf.Elf32_Rela),
5714 .p64 => @sizeOf(elf.Elf64_Rela),5208 .p64 => @sizeOf(elf.Elf64_Rela),
...@@ -5862,6 +5356,19 @@ pub fn file(self: *Elf, index: File.Index) ?File {...@@ -5862,6 +5356,19 @@ pub fn file(self: *Elf, index: File.Index) ?File {
5862 };5356 };
5863}5357}
58645358
5359pub fn addFileHandle(self: *Elf, handle: std.fs.File) !File.HandleIndex {
5360 const gpa = self.base.comp.gpa;
5361 const index: File.HandleIndex = @intCast(self.file_handles.items.len);
5362 const fh = try self.file_handles.addOne(gpa);
5363 fh.* = handle;
5364 return index;
5365}
5366
5367pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle {
5368 assert(index < self.file_handles.items.len);
5369 return self.file_handles.items[index];
5370}
5371
5865/// Returns pointer-to-symbol described at sym_index.5372/// Returns pointer-to-symbol described at sym_index.
5866pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol {5373pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol {
5867 return &self.symbols.items[sym_index];5374 return &self.symbols.items[sym_index];
...@@ -6322,7 +5829,7 @@ fn formatPhdr(...@@ -6322,7 +5829,7 @@ fn formatPhdr(
6322 });5829 });
6323}5830}
63245831
6325fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {5832pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
6326 return .{ .data = self };5833 return .{ .data = self };
6327}5834}
63285835
...@@ -6395,6 +5902,15 @@ fn fmtDumpState(...@@ -6395,6 +5902,15 @@ fn fmtDumpState(
6395 }5902 }
6396}5903}
63975904
5905/// Caller owns the memory.
5906pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: u64, size: u64) ![]u8 {
5907 const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow);
5908 errdefer allocator.free(buffer);
5909 const amt = try handle.preadAll(buffer, offset);
5910 if (amt != size) return error.InputOutput;
5911 return buffer;
5912}
5913
6398/// Binary search5914/// Binary search
6399pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {5915pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
6400 if (!@hasDecl(@TypeOf(predicate), "predicate"))5916 if (!@hasDecl(@TypeOf(predicate), "predicate"))
...@@ -6441,12 +5957,22 @@ pub const base_tag: link.File.Tag = .elf;...@@ -6441,12 +5957,22 @@ pub const base_tag: link.File.Tag = .elf;
64415957
6442const ComdatGroupOwner = struct {5958const ComdatGroupOwner = struct {
6443 file: File.Index = 0,5959 file: File.Index = 0,
5960
6444 const Index = u32;5961 const Index = u32;
6445};5962};
64465963
6447pub const ComdatGroup = struct {5964pub const ComdatGroup = struct {
6448 owner: ComdatGroupOwner.Index,5965 owner: ComdatGroupOwner.Index,
6449 shndx: u16,5966 file: File.Index,
5967 shndx: u32,
5968 members_start: u32,
5969 members_len: u32,
5970
5971 pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 {
5972 const object = elf_file.file(cg.file).?.object;
5973 return object.comdat_group_data.items[cg.members_start..][0..cg.members_len];
5974 }
5975
6450 pub const Index = u32;5976 pub const Index = u32;
6451};5977};
64525978
...@@ -6542,6 +6068,7 @@ const glibc = @import("../glibc.zig");...@@ -6542,6 +6068,7 @@ const glibc = @import("../glibc.zig");
6542const link = @import("../link.zig");6068const link = @import("../link.zig");
6543const lldMain = @import("../main.zig").lldMain;6069const lldMain = @import("../main.zig").lldMain;
6544const musl = @import("../musl.zig");6070const musl = @import("../musl.zig");
6071const relocatable = @import("Elf/relocatable.zig");
6545const target_util = @import("../target.zig");6072const target_util = @import("../target.zig");
6546const trace = @import("../tracy.zig").trace;6073const trace = @import("../tracy.zig").trace;
6547const synthetic_sections = @import("Elf/synthetic_sections.zig");6074const synthetic_sections = @import("Elf/synthetic_sections.zig");
src/link/Elf/Archive.zig+33-29
...@@ -1,8 +1,5 @@...@@ -1,8 +1,5 @@
1path: []const u8,
2data: []const u8,
3
4objects: std.ArrayListUnmanaged(Object) = .{},1objects: std.ArrayListUnmanaged(Object) = .{},
5strtab: []const u8 = &[0]u8{},2strtab: std.ArrayListUnmanaged(u8) = .{},
63
7pub fn isArchive(path: []const u8) !bool {4pub fn isArchive(path: []const u8) !bool {
8 const file = try std.fs.cwd().openFile(path, .{});5 const file = try std.fs.cwd().openFile(path, .{});
...@@ -14,68 +11,75 @@ pub fn isArchive(path: []const u8) !bool {...@@ -14,68 +11,75 @@ pub fn isArchive(path: []const u8) !bool {
14}11}
1512
16pub fn deinit(self: *Archive, allocator: Allocator) void {13pub fn deinit(self: *Archive, allocator: Allocator) void {
17 allocator.free(self.path);
18 allocator.free(self.data);
19 self.objects.deinit(allocator);14 self.objects.deinit(allocator);
15 self.strtab.deinit(allocator);
20}16}
2117
22pub fn parse(self: *Archive, elf_file: *Elf) !void {18pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: File.HandleIndex) !void {
23 const comp = elf_file.base.comp;19 const comp = elf_file.base.comp;
24 const gpa = comp.gpa;20 const gpa = comp.gpa;
21 const handle = elf_file.fileHandle(handle_index);
22 const size = (try handle.stat()).size;
2523
26 var stream = std.io.fixedBufferStream(self.data);24 var pos: usize = elf.ARMAG.len;
27 const reader = stream.reader();
28 _ = try reader.readBytesNoEof(elf.ARMAG.len);
29
30 while (true) {25 while (true) {
31 if (stream.pos >= self.data.len) break;26 if (pos >= size) break;
32 if (!mem.isAligned(stream.pos, 2)) stream.pos += 1;27 if (!mem.isAligned(pos, 2)) pos += 1;
3328
34 const hdr = try reader.readStruct(elf.ar_hdr);29 var hdr_buffer: [@sizeOf(elf.ar_hdr)]u8 = undefined;
30 {
31 const amt = try handle.preadAll(&hdr_buffer, pos);
32 if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput;
33 }
34 const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*;
35 pos += @sizeOf(elf.ar_hdr);
3536
36 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {37 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
37 try elf_file.reportParseError(self.path, "invalid archive header delimiter: {s}", .{38 try elf_file.reportParseError(path, "invalid archive header delimiter: {s}", .{
38 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),39 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
39 });40 });
40 return error.MalformedArchive;41 return error.MalformedArchive;
41 }42 }
4243
43 const size = try hdr.size();44 const obj_size = try hdr.size();
44 defer {45 defer pos += obj_size;
45 _ = stream.seekBy(size) catch {};
46 }
4746
48 if (hdr.isSymtab() or hdr.isSymtab64()) continue;47 if (hdr.isSymtab() or hdr.isSymtab64()) continue;
49 if (hdr.isStrtab()) {48 if (hdr.isStrtab()) {
50 self.strtab = self.data[stream.pos..][0..size];49 try self.strtab.resize(gpa, obj_size);
50 const amt = try handle.preadAll(self.strtab.items, pos);
51 if (amt != obj_size) return error.InputOutput;
51 continue;52 continue;
52 }53 }
53 if (hdr.isSymdef() or hdr.isSymdefSorted()) continue;54 if (hdr.isSymdef() or hdr.isSymdefSorted()) continue;
5455
55 const name = if (hdr.name()) |name|56 const name = if (hdr.name()) |name|
56 try gpa.dupe(u8, name)57 name
57 else if (try hdr.nameOffset()) |off|58 else if (try hdr.nameOffset()) |off|
58 try gpa.dupe(u8, self.getString(off))59 self.getString(off)
59 else60 else
60 unreachable;61 unreachable;
6162
62 const object = Object{63 const object = Object{
63 .archive = try gpa.dupe(u8, self.path),64 .archive = .{
64 .path = name,65 .path = try gpa.dupe(u8, path),
65 .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),66 .offset = pos,
67 },
68 .path = try gpa.dupe(u8, name),
69 .file_handle = handle_index,
66 .index = undefined,70 .index = undefined,
67 .alive = false,71 .alive = false,
68 };72 };
6973
70 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });74 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path });
7175
72 try self.objects.append(gpa, object);76 try self.objects.append(gpa, object);
73 }77 }
74}78}
7579
76fn getString(self: Archive, off: u32) []const u8 {80fn getString(self: Archive, off: u32) []const u8 {
77 assert(off < self.strtab.len);81 assert(off < self.strtab.items.len);
78 const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0);82 const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
79 return name[0 .. name.len - 1];83 return name[0 .. name.len - 1];
80}84}
8185
...@@ -86,7 +90,7 @@ pub fn setArHdr(opts: struct {...@@ -86,7 +90,7 @@ pub fn setArHdr(opts: struct {
86 name: []const u8,90 name: []const u8,
87 name_off: u32,91 name_off: u32,
88 },92 },
89 size: u32,93 size: usize,
90}) elf.ar_hdr {94}) elf.ar_hdr {
91 var hdr: elf.ar_hdr = .{95 var hdr: elf.ar_hdr = .{
92 .ar_name = undefined,96 .ar_name = undefined,
src/link/Elf/Atom.zig+8-2
...@@ -22,6 +22,12 @@ output_section_index: u16 = 0,...@@ -22,6 +22,12 @@ output_section_index: u16 = 0,
22/// Index of the input section containing this atom's relocs.22/// Index of the input section containing this atom's relocs.
23relocs_section_index: u32 = 0,23relocs_section_index: u32 = 0,
2424
25/// Start index of the relocations belonging to this atom.
26rel_index: u32 = 0,
27
28/// Number of relocations belonging to this atom.
29rel_num: u32 = 0,
30
25/// Index of this atom in the linker's atoms table.31/// Index of this atom in the linker's atoms table.
26atom_index: Index = 0,32atom_index: Index = 0,
2733
...@@ -52,7 +58,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {...@@ -52,7 +58,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
52 return elf_file.file(self.file_index);58 return elf_file.file(self.file_index);
53}59}
5460
55pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr {61pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
56 return switch (self.file(elf_file).?) {62 return switch (self.file(elf_file).?) {
57 .object => |x| x.shdrs.items[self.input_section_index],63 .object => |x| x.shdrs.items[self.input_section_index],
58 .zig_object => |x| x.inputShdr(self.atom_index, elf_file),64 .zig_object => |x| x.inputShdr(self.atom_index, elf_file),
...@@ -289,7 +295,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {...@@ -289,7 +295,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
289 const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};295 const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};
290 return switch (self.file(elf_file).?) {296 return switch (self.file(elf_file).?) {
291 .zig_object => |x| x.relocs.items[shndx].items,297 .zig_object => |x| x.relocs.items[shndx].items,
292 .object => |x| x.getRelocs(shndx),298 .object => |x| x.relocs.items[self.rel_index..][0..self.rel_num],
293 else => unreachable,299 else => unreachable,
294 };300 };
295}301}
src/link/Elf/Object.zig+143-126
...@@ -1,10 +1,10 @@...@@ -1,10 +1,10 @@
1archive: ?[]const u8 = null,1archive: ?InArchive = null,
2path: []const u8,2path: []const u8,
3data: []const u8,3file_handle: File.HandleIndex,
4index: File.Index,4index: File.Index,
55
6header: ?elf.Elf64_Ehdr = null,6header: ?elf.Elf64_Ehdr = null,
7shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},7shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
88
9symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},9symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
10strtab: std.ArrayListUnmanaged(u8) = .{},10strtab: std.ArrayListUnmanaged(u8) = .{},
...@@ -12,9 +12,12 @@ first_global: ?Symbol.Index = null,...@@ -12,9 +12,12 @@ first_global: ?Symbol.Index = null,
12symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},12symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
13atoms: std.ArrayListUnmanaged(Atom.Index) = .{},13atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
14comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},14comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
15comdat_group_data: std.ArrayListUnmanaged(u32) = .{},
16relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
1517
16fdes: std.ArrayListUnmanaged(Fde) = .{},18fdes: std.ArrayListUnmanaged(Fde) = .{},
17cies: std.ArrayListUnmanaged(Cie) = .{},19cies: std.ArrayListUnmanaged(Cie) = .{},
20eh_frame_data: std.ArrayListUnmanaged(u8) = .{},
1821
19alive: bool = true,22alive: bool = true,
20num_dynrelocs: u32 = 0,23num_dynrelocs: u32 = 0,
...@@ -35,24 +38,44 @@ pub fn isObject(path: []const u8) !bool {...@@ -35,24 +38,44 @@ pub fn isObject(path: []const u8) !bool {
35}38}
3639
37pub fn deinit(self: *Object, allocator: Allocator) void {40pub fn deinit(self: *Object, allocator: Allocator) void {
38 if (self.archive) |path| allocator.free(path);41 if (self.archive) |*ar| allocator.free(ar.path);
39 allocator.free(self.path);42 allocator.free(self.path);
40 allocator.free(self.data);
41 self.shdrs.deinit(allocator);43 self.shdrs.deinit(allocator);
42 self.symtab.deinit(allocator);44 self.symtab.deinit(allocator);
43 self.strtab.deinit(allocator);45 self.strtab.deinit(allocator);
44 self.symbols.deinit(allocator);46 self.symbols.deinit(allocator);
45 self.atoms.deinit(allocator);47 self.atoms.deinit(allocator);
46 self.comdat_groups.deinit(allocator);48 self.comdat_groups.deinit(allocator);
49 self.comdat_group_data.deinit(allocator);
50 self.relocs.deinit(allocator);
47 self.fdes.deinit(allocator);51 self.fdes.deinit(allocator);
48 self.cies.deinit(allocator);52 self.cies.deinit(allocator);
53 self.eh_frame_data.deinit(allocator);
49}54}
5055
51pub fn parse(self: *Object, elf_file: *Elf) !void {56pub fn parse(self: *Object, elf_file: *Elf) !void {
52 var stream = std.io.fixedBufferStream(self.data);57 const gpa = elf_file.base.comp.gpa;
53 const reader = stream.reader();58 const handle = elf_file.fileHandle(self.file_handle);
5459
55 self.header = try reader.readStruct(elf.Elf64_Ehdr);60 try self.parseCommon(gpa, handle, elf_file);
61 try self.initAtoms(gpa, handle, elf_file);
62 try self.initSymtab(gpa, elf_file);
63
64 for (self.shdrs.items, 0..) |shdr, i| {
65 const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
66 if (!atom.flags.alive) continue;
67 if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
68 try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
69 }
70}
71
72fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
73 const offset = if (self.archive) |ar| ar.offset else 0;
74 const file_size = (try handle.stat()).size;
75
76 const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr));
77 defer allocator.free(header_buffer);
78 self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
5679
57 const target = elf_file.base.comp.root_mod.resolved_target.result;80 const target = elf_file.base.comp.root_mod.resolved_target.result;
58 if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {81 if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
...@@ -66,12 +89,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -66,12 +89,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
6689
67 if (self.header.?.e_shnum == 0) return;90 if (self.header.?.e_shnum == 0) return;
6891
69 const comp = elf_file.base.comp;92 const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
70 const gpa = comp.gpa;93 const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
7194 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
72 if (self.data.len < self.header.?.e_shoff or95 if (file_size < offset + shoff or file_size < offset + shoff + shsize) {
73 self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr))
74 {
75 try elf_file.reportParseError2(96 try elf_file.reportParseError2(
76 self.index,97 self.index,
77 "corrupt header: section header table extends past the end of file",98 "corrupt header: section header table extends past the end of file",
...@@ -80,31 +101,29 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -80,31 +101,29 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
80 return error.MalformedObject;101 return error.MalformedObject;
81 }102 }
82103
83 const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;104 const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize);
84 const shdrs = @as(105 defer allocator.free(shdrs_buffer);
85 [*]align(1) const elf.Elf64_Shdr,106 const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
86 @ptrCast(self.data.ptr + shoff),107 try self.shdrs.appendUnalignedSlice(allocator, shdrs);
87 )[0..self.header.?.e_shnum];
88 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
89108
90 for (shdrs) |shdr| {109 for (self.shdrs.items) |shdr| {
91 if (shdr.sh_type != elf.SHT_NOBITS) {110 if (shdr.sh_type != elf.SHT_NOBITS) {
92 if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) {111 if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) {
93 try elf_file.reportParseError2(self.index, "corrupt section: extends past the end of file", .{});112 try elf_file.reportParseError2(self.index, "corrupt section: extends past the end of file", .{});
94 return error.MalformedObject;113 return error.MalformedObject;
95 }114 }
96 }115 }
97 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
98 }116 }
99117
100 const shstrtab = self.shdrContents(self.header.?.e_shstrndx);118 const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx);
101 for (shdrs) |shdr| {119 defer allocator.free(shstrtab);
120 for (self.shdrs.items) |shdr| {
102 if (shdr.sh_name >= shstrtab.len) {121 if (shdr.sh_name >= shstrtab.len) {
103 try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});122 try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});
104 return error.MalformedObject;123 return error.MalformedObject;
105 }124 }
106 }125 }
107 try self.strtab.appendSlice(gpa, shstrtab);126 try self.strtab.appendSlice(allocator, shstrtab);
108127
109 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {128 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
110 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),129 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
...@@ -112,10 +131,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -112,10 +131,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
112 } else null;131 } else null;
113132
114 if (symtab_index) |index| {133 if (symtab_index) |index| {
115 const shdr = shdrs[index];134 const shdr = self.shdrs.items[index];
116 self.first_global = shdr.sh_info;135 self.first_global = shdr.sh_info;
117136
118 const raw_symtab = self.shdrContents(index);137 const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index);
138 defer allocator.free(raw_symtab);
119 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {139 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
120 try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});140 try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});
121 return error.MalformedObject;141 return error.MalformedObject;
...@@ -123,9 +143,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -123,9 +143,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
123 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];143 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
124144
125 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));145 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
126 try self.strtab.appendSlice(gpa, self.shdrContents(@as(u16, @intCast(shdr.sh_link))));146 const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link);
147 defer allocator.free(strtab);
148 try self.strtab.appendSlice(allocator, strtab);
127149
128 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);150 try self.symtab.ensureUnusedCapacity(allocator, symtab.len);
129 for (symtab) |sym| {151 for (symtab) |sym| {
130 const out_sym = self.symtab.addOneAssumeCapacity();152 const out_sym = self.symtab.addOneAssumeCapacity();
131 out_sym.* = sym;153 out_sym.* = sym;
...@@ -137,23 +159,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -137,23 +159,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
137 }159 }
138}160}
139161
140pub fn init(self: *Object, elf_file: *Elf) !void {162fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
141 try self.initAtoms(elf_file);
142 try self.initSymtab(elf_file);
143
144 for (self.shdrs.items, 0..) |shdr, i| {
145 const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
146 if (!atom.flags.alive) continue;
147 if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
148 try self.parseEhFrame(@as(u16, @intCast(i)), elf_file);
149 }
150}
151
152fn initAtoms(self: *Object, elf_file: *Elf) !void {
153 const comp = elf_file.base.comp;
154 const gpa = comp.gpa;
155 const shdrs = self.shdrs.items;163 const shdrs = self.shdrs.items;
156 try self.atoms.resize(gpa, shdrs.len);164 try self.atoms.resize(allocator, shdrs.len);
157 @memset(self.atoms.items, 0);165 @memset(self.atoms.items, 0);
158166
159 for (shdrs, 0..) |shdr, i| {167 for (shdrs, 0..) |shdr, i| {
...@@ -177,8 +185,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -177,8 +185,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
177 break :blk self.getString(group_info_sym.st_name);185 break :blk self.getString(group_info_sym.st_name);
178 };186 };
179187
180 const shndx = @as(u16, @intCast(i));188 const shndx = @as(u32, @intCast(i));
181 const group_raw_data = self.shdrContents(shndx);189 const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
190 defer allocator.free(group_raw_data);
182 const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));191 const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));
183 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];192 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
184193
...@@ -188,14 +197,20 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -188,14 +197,20 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
188 continue;197 continue;
189 }198 }
190199
200 const group_start = @as(u32, @intCast(self.comdat_group_data.items.len));
201 try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]);
202
191 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature);203 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature);
192 const comdat_group_index = try elf_file.addComdatGroup();204 const comdat_group_index = try elf_file.addComdatGroup();
193 const comdat_group = elf_file.comdatGroup(comdat_group_index);205 const comdat_group = elf_file.comdatGroup(comdat_group_index);
194 comdat_group.* = .{206 comdat_group.* = .{
195 .owner = gop.index,207 .owner = gop.index,
208 .file = self.index,
196 .shndx = shndx,209 .shndx = shndx,
210 .members_start = group_start,
211 .members_len = @intCast(group_nmembers - 1),
197 };212 };
198 try self.comdat_groups.append(gpa, comdat_group_index);213 try self.comdat_groups.append(allocator, comdat_group_index);
199 },214 },
200215
201 elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),216 elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),
...@@ -210,7 +225,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -210,7 +225,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
210 else => {225 else => {
211 const shndx = @as(u16, @intCast(i));226 const shndx = @as(u16, @intCast(i));
212 if (self.skipShdr(shndx, elf_file)) continue;227 if (self.skipShdr(shndx, elf_file)) continue;
213 try self.addAtom(shdr, shndx, elf_file);228 try self.addAtom(allocator, handle, shdr, shndx, elf_file);
214 },229 },
215 }230 }
216 }231 }
...@@ -220,14 +235,19 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -220,14 +235,19 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
220 elf.SHT_REL, elf.SHT_RELA => {235 elf.SHT_REL, elf.SHT_RELA => {
221 const atom_index = self.atoms.items[shdr.sh_info];236 const atom_index = self.atoms.items[shdr.sh_info];
222 if (elf_file.atom(atom_index)) |atom| {237 if (elf_file.atom(atom_index)) |atom| {
223 atom.relocs_section_index = @as(u16, @intCast(i));238 const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i));
239 defer allocator.free(relocs);
240 atom.relocs_section_index = @intCast(i);
241 atom.rel_index = @intCast(self.relocs.items.len);
242 atom.rel_num = @intCast(relocs.len);
243 try self.relocs.appendUnalignedSlice(allocator, relocs);
224 }244 }
225 },245 },
226 else => {},246 else => {},
227 };247 };
228}248}
229249
230fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOfMemory}!void {250fn addAtom(self: *Object, allocator: Allocator, handle: std.fs.File, shdr: elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void {
231 const atom_index = try elf_file.addAtom();251 const atom_index = try elf_file.addAtom();
232 const atom = elf_file.atom(atom_index).?;252 const atom = elf_file.atom(atom_index).?;
233 atom.atom_index = atom_index;253 atom.atom_index = atom_index;
...@@ -237,7 +257,8 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf...@@ -237,7 +257,8 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf
237 self.atoms.items[shndx] = atom_index;257 self.atoms.items[shndx] = atom_index;
238258
239 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {259 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
240 const data = self.shdrContents(shndx);260 const data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
261 defer allocator.free(data);
241 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;262 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
242 atom.size = chdr.ch_size;263 atom.size = chdr.ch_size;
243 atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign);264 atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign);
...@@ -247,7 +268,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf...@@ -247,7 +268,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf
247 }268 }
248}269}
249270
250fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 {271fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 {
251 const name = blk: {272 const name = blk: {
252 const name = self.getString(shdr.sh_name);273 const name = self.getString(shdr.sh_name);
253 if (elf_file.base.isRelocatable()) break :blk name;274 if (elf_file.base.isRelocatable()) break :blk name;
...@@ -310,12 +331,10 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {...@@ -310,12 +331,10 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
310 return ignore;331 return ignore;
311}332}
312333
313fn initSymtab(self: *Object, elf_file: *Elf) !void {334fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
314 const comp = elf_file.base.comp;
315 const gpa = comp.gpa;
316 const first_global = self.first_global orelse self.symtab.items.len;335 const first_global = self.first_global orelse self.symtab.items.len;
317336
318 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);337 try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len);
319338
320 for (self.symtab.items[0..first_global], 0..) |sym, i| {339 for (self.symtab.items[0..first_global], 0..) |sym, i| {
321 const index = try elf_file.addSymbol();340 const index = try elf_file.addSymbol();
...@@ -335,19 +354,24 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {...@@ -335,19 +354,24 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {
335 }354 }
336}355}
337356
338fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {357fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: u32, elf_file: *Elf) !void {
339 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {358 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
340 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u16, @intCast(i)),359 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
341 else => {},360 else => {},
342 } else {361 } else {
362 // TODO: convert into an error
343 log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});363 log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
344 return;364 return;
345 };365 };
346366
347 const comp = elf_file.base.comp;367 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
348 const gpa = comp.gpa;368 defer allocator.free(raw);
349 const raw = self.shdrContents(shndx);369 const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));
350 const relocs = self.getRelocs(relocs_shndx);370 try self.eh_frame_data.appendSlice(allocator, raw);
371 const relocs = try self.preadRelocsAlloc(allocator, handle, relocs_shndx);
372 defer allocator.free(relocs);
373 const rel_start = @as(u32, @intCast(self.relocs.items.len));
374 try self.relocs.appendUnalignedSlice(allocator, relocs);
351 const fdes_start = self.fdes.items.len;375 const fdes_start = self.fdes.items.len;
352 const cies_start = self.cies.items.len;376 const cies_start = self.cies.items.len;
353377
...@@ -355,22 +379,20 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {...@@ -355,22 +379,20 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {
355 while (try it.next()) |rec| {379 while (try it.next()) |rec| {
356 const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4);380 const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4);
357 switch (rec.tag) {381 switch (rec.tag) {
358 .cie => try self.cies.append(gpa, .{382 .cie => try self.cies.append(allocator, .{
359 .offset = rec.offset,383 .offset = data_start + rec.offset,
360 .size = rec.size,384 .size = rec.size,
361 .rel_index = @as(u32, @intCast(rel_range.start)),385 .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
362 .rel_num = @as(u32, @intCast(rel_range.len)),386 .rel_num = @as(u32, @intCast(rel_range.len)),
363 .rel_section_index = relocs_shndx,
364 .input_section_index = shndx,387 .input_section_index = shndx,
365 .file_index = self.index,388 .file_index = self.index,
366 }),389 }),
367 .fde => try self.fdes.append(gpa, .{390 .fde => try self.fdes.append(allocator, .{
368 .offset = rec.offset,391 .offset = data_start + rec.offset,
369 .size = rec.size,392 .size = rec.size,
370 .cie_index = undefined,393 .cie_index = undefined,
371 .rel_index = @as(u32, @intCast(rel_range.start)),394 .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
372 .rel_num = @as(u32, @intCast(rel_range.len)),395 .rel_num = @as(u32, @intCast(rel_range.len)),
373 .rel_section_index = relocs_shndx,
374 .input_section_index = shndx,396 .input_section_index = shndx,
375 .file_index = self.index,397 .file_index = self.index,
376 }),398 }),
...@@ -759,6 +781,12 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void {...@@ -759,6 +781,12 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void {
759 }781 }
760}782}
761783
784pub fn parseAr(self: *Object, elf_file: *Elf) !void {
785 const gpa = elf_file.base.comp.gpa;
786 const handle = elf_file.fileHandle(self.file_handle);
787 try self.parseCommon(gpa, handle, elf_file);
788}
789
762pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {790pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
763 const comp = elf_file.base.comp;791 const comp = elf_file.base.comp;
764 const gpa = comp.gpa;792 const gpa = comp.gpa;
...@@ -773,21 +801,30 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf...@@ -773,21 +801,30 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf
773 }801 }
774}802}
775803
776pub fn updateArSize(self: *Object) void {804pub fn updateArSize(self: *Object, elf_file: *Elf) !void {
777 self.output_ar_state.size = self.data.len;805 const handle = elf_file.fileHandle(self.file_handle);
806 const size = (try handle.stat()).size;
807 self.output_ar_state.size = size;
778}808}
779809
780pub fn writeAr(self: Object, writer: anytype) !void {810pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
811 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
781 const name = self.path;812 const name = self.path;
782 const hdr = Archive.setArHdr(.{813 const hdr = Archive.setArHdr(.{
783 .name = if (name.len <= Archive.max_member_name_len)814 .name = if (name.len <= Archive.max_member_name_len)
784 .{ .name = name }815 .{ .name = name }
785 else816 else
786 .{ .name_off = self.output_ar_state.name_off },817 .{ .name_off = self.output_ar_state.name_off },
787 .size = @intCast(self.data.len),818 .size = size,
788 });819 });
789 try writer.writeAll(mem.asBytes(&hdr));820 try writer.writeAll(mem.asBytes(&hdr));
790 try writer.writeAll(self.data);821 const handle = elf_file.fileHandle(self.file_handle);
822 const gpa = elf_file.base.comp.gpa;
823 const data = try gpa.alloc(u8, size);
824 defer gpa.free(data);
825 const amt = try handle.preadAll(data, 0);
826 if (amt != size) return error.InputOutput;
827 try writer.writeAll(data);
791}828}
792829
793pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {830pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
...@@ -859,12 +896,6 @@ pub fn globals(self: Object) []const Symbol.Index {...@@ -859,12 +896,6 @@ pub fn globals(self: Object) []const Symbol.Index {
859 return self.symbols.items[start..];896 return self.symbols.items[start..];
860}897}
861898
862pub fn shdrContents(self: Object, index: u32) []const u8 {
863 assert(index < self.shdrs.items.len);
864 const shdr = self.shdrs.items[index];
865 return self.data[shdr.sh_offset..][0..shdr.sh_size];
866}
867
868/// Returns atom's code and optionally uncompresses data if required (for compressed sections).899/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
869/// Caller owns the memory.900/// Caller owns the memory.
870pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {901pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
...@@ -872,8 +903,11 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)...@@ -872,8 +903,11 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
872 const gpa = comp.gpa;903 const gpa = comp.gpa;
873 const atom_ptr = elf_file.atom(atom_index).?;904 const atom_ptr = elf_file.atom(atom_index).?;
874 assert(atom_ptr.file_index == self.index);905 assert(atom_ptr.file_index == self.index);
875 const data = self.shdrContents(atom_ptr.input_section_index);
876 const shdr = atom_ptr.inputShdr(elf_file);906 const shdr = atom_ptr.inputShdr(elf_file);
907 const handle = elf_file.fileHandle(self.file_handle);
908 const data = try self.preadShdrContentsAlloc(gpa, handle, atom_ptr.input_section_index);
909 defer if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) gpa.free(data);
910
877 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {911 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
878 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;912 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
879 switch (chdr.ch_type) {913 switch (chdr.ch_type) {
...@@ -892,31 +926,37 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)...@@ -892,31 +926,37 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
892 },926 },
893 else => @panic("TODO unhandled compression scheme"),927 else => @panic("TODO unhandled compression scheme"),
894 }928 }
895 } else return gpa.dupe(u8, data);929 }
896}
897930
898pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 {931 return data;
899 const raw = self.shdrContents(index);
900 const nmembers = @divExact(raw.len, @sizeOf(u32));
901 const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers];
902 return members;
903}932}
904933
905pub fn asFile(self: *Object) File {934pub fn asFile(self: *Object) File {
906 return .{ .object = self };935 return .{ .object = self };
907}936}
908937
909pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela {
910 const raw = self.shdrContents(shndx);
911 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
912 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
913}
914
915pub fn getString(self: Object, off: u32) [:0]const u8 {938pub fn getString(self: Object, off: u32) [:0]const u8 {
916 assert(off < self.strtab.items.len);939 assert(off < self.strtab.items.len);
917 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);940 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
918}941}
919942
943/// Caller owns the memory.
944fn preadShdrContentsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, index: u32) ![]u8 {
945 assert(index < self.shdrs.items.len);
946 const offset = if (self.archive) |ar| ar.offset else 0;
947 const shdr = self.shdrs.items[index];
948 const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow;
949 const sh_size = math.cast(u64, shdr.sh_size) orelse return error.Overflow;
950 return Elf.preadAllAlloc(allocator, handle, offset + sh_offset, sh_size);
951}
952
953/// Caller owns the memory.
954fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {
955 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
956 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
957 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
958}
959
920pub fn format(960pub fn format(
921 self: *Object,961 self: *Object,
922 comptime unused_fmt_string: []const u8,962 comptime unused_fmt_string: []const u8,
...@@ -1053,7 +1093,7 @@ fn formatComdatGroups(...@@ -1053,7 +1093,7 @@ fn formatComdatGroups(
1053 const cg_owner = elf_file.comdatGroupOwner(cg.owner);1093 const cg_owner = elf_file.comdatGroupOwner(cg.owner);
1054 if (cg_owner.file != object.index) continue;1094 if (cg_owner.file != object.index) continue;
1055 try writer.print(" COMDAT({d})\n", .{cg_index});1095 try writer.print(" COMDAT({d})\n", .{cg_index});
1056 const cg_members = object.comdatGroupMembers(cg.shndx);1096 const cg_members = cg.comdatGroupMembers(elf_file);
1057 for (cg_members) |shndx| {1097 for (cg_members) |shndx| {
1058 const atom_index = object.atoms.items[shndx];1098 const atom_index = object.atoms.items[shndx];
1059 const atom = elf_file.atom(atom_index) orelse continue;1099 const atom = elf_file.atom(atom_index) orelse continue;
...@@ -1074,40 +1114,17 @@ fn formatPath(...@@ -1074,40 +1114,17 @@ fn formatPath(
1074) !void {1114) !void {
1075 _ = unused_fmt_string;1115 _ = unused_fmt_string;
1076 _ = options;1116 _ = options;
1077 if (object.archive) |path| {1117 if (object.archive) |ar| {
1078 try writer.writeAll(path);1118 try writer.writeAll(ar.path);
1079 try writer.writeByte('(');1119 try writer.writeByte('(');
1080 try writer.writeAll(object.path);1120 try writer.writeAll(object.path);
1081 try writer.writeByte(')');1121 try writer.writeByte(')');
1082 } else try writer.writeAll(object.path);1122 } else try writer.writeAll(object.path);
1083}1123}
10841124
1085pub const ElfShdr = struct {1125const InArchive = struct {
1086 sh_name: u32,1126 path: []const u8,
1087 sh_type: u32,1127 offset: u64,
1088 sh_flags: u64,
1089 sh_addr: u64,
1090 sh_offset: usize,
1091 sh_size: usize,
1092 sh_link: u32,
1093 sh_info: u32,
1094 sh_addralign: u64,
1095 sh_entsize: u64,
1096
1097 pub fn fromElf64Shdr(shdr: elf.Elf64_Shdr) error{Overflow}!ElfShdr {
1098 return .{
1099 .sh_name = shdr.sh_name,
1100 .sh_type = shdr.sh_type,
1101 .sh_flags = shdr.sh_flags,
1102 .sh_addr = shdr.sh_addr,
1103 .sh_offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow,
1104 .sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow,
1105 .sh_link = shdr.sh_link,
1106 .sh_info = shdr.sh_info,
1107 .sh_addralign = shdr.sh_addralign,
1108 .sh_entsize = shdr.sh_entsize,
1109 };
1110 }
1111};1128};
11121129
1113const Object = @This();1130const Object = @This();
src/link/Elf/SharedObject.zig+88-77
...@@ -1,22 +1,18 @@...@@ -1,22 +1,18 @@
1path: []const u8,1path: []const u8,
2data: []const u8,
3index: File.Index,2index: File.Index,
43
5header: ?elf.Elf64_Ehdr = null,4header: ?elf.Elf64_Ehdr = null,
6shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},5shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
76
8symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},7symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
9strtab: std.ArrayListUnmanaged(u8) = .{},8strtab: std.ArrayListUnmanaged(u8) = .{},
10/// Version symtab contains version strings of the symbols if present.9/// Version symtab contains version strings of the symbols if present.
11versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},10versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
12verstrings: std.ArrayListUnmanaged(u32) = .{},11verstrings: std.ArrayListUnmanaged(u32) = .{},
12
13symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},13symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
14aliases: ?std.ArrayListUnmanaged(u32) = null,14aliases: ?std.ArrayListUnmanaged(u32) = null,
1515dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{},
16dynsym_sect_index: ?u16 = null,
17dynamic_sect_index: ?u16 = null,
18versym_sect_index: ?u16 = null,
19verdef_sect_index: ?u16 = null,
2016
21needed: bool,17needed: bool,
22alive: bool,18alive: bool,
...@@ -36,23 +32,24 @@ pub fn isSharedObject(path: []const u8) !bool {...@@ -36,23 +32,24 @@ pub fn isSharedObject(path: []const u8) !bool {
3632
37pub fn deinit(self: *SharedObject, allocator: Allocator) void {33pub fn deinit(self: *SharedObject, allocator: Allocator) void {
38 allocator.free(self.path);34 allocator.free(self.path);
39 allocator.free(self.data);35 self.shdrs.deinit(allocator);
40 self.symtab.deinit(allocator);36 self.symtab.deinit(allocator);
41 self.strtab.deinit(allocator);37 self.strtab.deinit(allocator);
42 self.versyms.deinit(allocator);38 self.versyms.deinit(allocator);
43 self.verstrings.deinit(allocator);39 self.verstrings.deinit(allocator);
44 self.symbols.deinit(allocator);40 self.symbols.deinit(allocator);
45 if (self.aliases) |*aliases| aliases.deinit(allocator);41 if (self.aliases) |*aliases| aliases.deinit(allocator);
46 self.shdrs.deinit(allocator);42 self.dynamic_table.deinit(allocator);
47}43}
4844
49pub fn parse(self: *SharedObject, elf_file: *Elf) !void {45pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void {
50 const comp = elf_file.base.comp;46 const comp = elf_file.base.comp;
51 const gpa = comp.gpa;47 const gpa = comp.gpa;
52 var stream = std.io.fixedBufferStream(self.data);48 const file_size = (try handle.stat()).size;
53 const reader = stream.reader();
5449
55 self.header = try reader.readStruct(elf.Elf64_Ehdr);50 const header_buffer = try Elf.preadAllAlloc(gpa, handle, 0, @sizeOf(elf.Elf64_Ehdr));
51 defer gpa.free(header_buffer);
52 self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
5653
57 const target = elf_file.base.comp.root_mod.resolved_target.result;54 const target = elf_file.base.comp.root_mod.resolved_target.result;
58 if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {55 if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
...@@ -64,9 +61,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {...@@ -64,9 +61,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
64 return error.InvalidCpuArch;61 return error.InvalidCpuArch;
65 }62 }
6663
67 if (self.data.len < self.header.?.e_shoff or64 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
68 self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr))65 const shnum = std.math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
69 {66 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
67 if (file_size < shoff or file_size < shoff + shsize) {
70 try elf_file.reportParseError2(68 try elf_file.reportParseError2(
71 self.index,69 self.index,
72 "corrupted header: section header table extends past the end of file",70 "corrupted header: section header table extends past the end of file",
...@@ -75,45 +73,84 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {...@@ -75,45 +73,84 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
75 return error.MalformedObject;73 return error.MalformedObject;
76 }74 }
7775
78 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;76 const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, shoff, shsize);
7977 defer gpa.free(shdrs_buffer);
80 const shdrs = @as(78 const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
81 [*]align(1) const elf.Elf64_Shdr,79 try self.shdrs.appendUnalignedSlice(gpa, shdrs);
82 @ptrCast(self.data.ptr + shoff),
83 )[0..self.header.?.e_shnum];
84 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
8580
86 for (shdrs, 0..) |shdr, i| {81 var dynsym_sect_index: ?u32 = null;
82 var dynamic_sect_index: ?u32 = null;
83 var versym_sect_index: ?u32 = null;
84 var verdef_sect_index: ?u32 = null;
85 for (self.shdrs.items, 0..) |shdr, i| {
87 if (shdr.sh_type != elf.SHT_NOBITS) {86 if (shdr.sh_type != elf.SHT_NOBITS) {
88 if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) {87 if (file_size < shdr.sh_offset or file_size < shdr.sh_offset + shdr.sh_size) {
89 try elf_file.reportParseError2(self.index, "corrupted section header", .{});88 try elf_file.reportParseError2(self.index, "corrupted section header", .{});
90 return error.MalformedObject;89 return error.MalformedObject;
91 }90 }
92 }91 }
93 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
94 switch (shdr.sh_type) {92 switch (shdr.sh_type) {
95 elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),93 elf.SHT_DYNSYM => dynsym_sect_index = @intCast(i),
96 elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)),94 elf.SHT_DYNAMIC => dynamic_sect_index = @intCast(i),
97 elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)),95 elf.SHT_GNU_VERSYM => versym_sect_index = @intCast(i),
98 elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)),96 elf.SHT_GNU_VERDEF => verdef_sect_index = @intCast(i),
99 else => {},97 else => {},
100 }98 }
101 }99 }
102100
103 try self.parseVersions(elf_file);101 if (dynamic_sect_index) |index| {
102 const shdr = self.shdrs.items[index];
103 const raw = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
104 defer gpa.free(raw);
105 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn));
106 const dyntab = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num];
107 try self.dynamic_table.appendUnalignedSlice(gpa, dyntab);
108 }
109
110 const symtab = if (dynsym_sect_index) |index| blk: {
111 const shdr = self.shdrs.items[index];
112 const buffer = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
113 const nsyms = @divExact(buffer.len, @sizeOf(elf.Elf64_Sym));
114 break :blk @as([*]align(1) const elf.Elf64_Sym, @ptrCast(buffer.ptr))[0..nsyms];
115 } else &[0]elf.Elf64_Sym{};
116 defer gpa.free(symtab);
117
118 const strtab = if (dynsym_sect_index) |index| blk: {
119 const symtab_shdr = self.shdrs.items[index];
120 const shdr = self.shdrs.items[symtab_shdr.sh_link];
121 const buffer = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
122 break :blk buffer;
123 } else &[0]u8{};
124 defer gpa.free(strtab);
125
126 try self.parseVersions(elf_file, handle, .{
127 .symtab = symtab,
128 .verdef_sect_index = verdef_sect_index,
129 .versym_sect_index = versym_sect_index,
130 });
131
132 try self.initSymtab(elf_file, .{
133 .symtab = symtab,
134 .strtab = strtab,
135 });
104}136}
105137
106fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {138fn parseVersions(self: *SharedObject, elf_file: *Elf, handle: std.fs.File, opts: struct {
139 symtab: []align(1) const elf.Elf64_Sym,
140 verdef_sect_index: ?u32,
141 versym_sect_index: ?u32,
142}) !void {
107 const comp = elf_file.base.comp;143 const comp = elf_file.base.comp;
108 const gpa = comp.gpa;144 const gpa = comp.gpa;
109 const symtab = self.getSymtabRaw();
110145
111 try self.verstrings.resize(gpa, 2);146 try self.verstrings.resize(gpa, 2);
112 self.verstrings.items[elf.VER_NDX_LOCAL] = 0;147 self.verstrings.items[elf.VER_NDX_LOCAL] = 0;
113 self.verstrings.items[elf.VER_NDX_GLOBAL] = 0;148 self.verstrings.items[elf.VER_NDX_GLOBAL] = 0;
114149
115 if (self.verdef_sect_index) |shndx| {150 if (opts.verdef_sect_index) |shndx| {
116 const verdefs = self.shdrContents(shndx);151 const shdr = self.shdrs.items[shndx];
152 const verdefs = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
153 defer gpa.free(verdefs);
117 const nverdefs = self.verdefNum();154 const nverdefs = self.verdefNum();
118 try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs);155 try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs);
119156
...@@ -131,10 +168,12 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {...@@ -131,10 +168,12 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
131 }168 }
132 }169 }
133170
134 try self.versyms.ensureTotalCapacityPrecise(gpa, symtab.len);171 try self.versyms.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
135172
136 if (self.versym_sect_index) |shndx| {173 if (opts.versym_sect_index) |shndx| {
137 const versyms_raw = self.shdrContents(shndx);174 const shdr = self.shdrs.items[shndx];
175 const versyms_raw = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
176 defer gpa.free(versyms_raw);
138 const nversyms = @divExact(versyms_raw.len, @sizeOf(elf.Elf64_Versym));177 const nversyms = @divExact(versyms_raw.len, @sizeOf(elf.Elf64_Versym));
139 const versyms = @as([*]align(1) const elf.Elf64_Versym, @ptrCast(versyms_raw.ptr))[0..nversyms];178 const versyms = @as([*]align(1) const elf.Elf64_Versym, @ptrCast(versyms_raw.ptr))[0..nversyms];
140 for (versyms) |ver| {179 for (versyms) |ver| {
...@@ -144,22 +183,23 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {...@@ -144,22 +183,23 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
144 ver;183 ver;
145 self.versyms.appendAssumeCapacity(normalized_ver);184 self.versyms.appendAssumeCapacity(normalized_ver);
146 }185 }
147 } else for (0..symtab.len) |_| {186 } else for (0..opts.symtab.len) |_| {
148 self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL);187 self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL);
149 }188 }
150}189}
151190
152pub fn init(self: *SharedObject, elf_file: *Elf) !void {191fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct {
192 symtab: []align(1) const elf.Elf64_Sym,
193 strtab: []const u8,
194}) !void {
153 const comp = elf_file.base.comp;195 const comp = elf_file.base.comp;
154 const gpa = comp.gpa;196 const gpa = comp.gpa;
155 const symtab = self.getSymtabRaw();
156 const strtab = self.getStrtabRaw();
157197
158 try self.strtab.appendSlice(gpa, strtab);198 try self.strtab.appendSlice(gpa, opts.strtab);
159 try self.symtab.ensureTotalCapacityPrecise(gpa, symtab.len);199 try self.symtab.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
160 try self.symbols.ensureTotalCapacityPrecise(gpa, symtab.len);200 try self.symbols.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
161201
162 for (symtab, 0..) |sym, i| {202 for (opts.symtab, 0..) |sym, i| {
163 const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;203 const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;
164 const name = self.getString(sym.st_name);204 const name = self.getString(sym.st_name);
165 // We need to garble up the name so that we don't pick this symbol205 // We need to garble up the name so that we don't pick this symbol
...@@ -250,11 +290,6 @@ pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void {...@@ -250,11 +290,6 @@ pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void {
250 }290 }
251}291}
252292
253pub fn shdrContents(self: SharedObject, index: u16) []const u8 {
254 const shdr = self.shdrs.items[index];
255 return self.data[shdr.sh_offset..][0..shdr.sh_size];
256}
257
258pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 {293pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 {
259 const off = self.verstrings.items[index & elf.VERSYM_VERSION];294 const off = self.verstrings.items[index & elf.VERSYM_VERSION];
260 return self.getString(off);295 return self.getString(off);
...@@ -264,16 +299,8 @@ pub fn asFile(self: *SharedObject) File {...@@ -264,16 +299,8 @@ pub fn asFile(self: *SharedObject) File {
264 return .{ .shared_object = self };299 return .{ .shared_object = self };
265}300}
266301
267fn dynamicTable(self: *SharedObject) []align(1) const elf.Elf64_Dyn {
268 const shndx = self.dynamic_sect_index orelse return &[0]elf.Elf64_Dyn{};
269 const raw = self.shdrContents(shndx);
270 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn));
271 return @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num];
272}
273
274fn verdefNum(self: *SharedObject) u32 {302fn verdefNum(self: *SharedObject) u32 {
275 const entries = self.dynamicTable();303 for (self.dynamic_table.items) |entry| switch (entry.d_tag) {
276 for (entries) |entry| switch (entry.d_tag) {
277 elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)),304 elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)),
278 else => {},305 else => {},
279 };306 };
...@@ -281,8 +308,7 @@ fn verdefNum(self: *SharedObject) u32 {...@@ -281,8 +308,7 @@ fn verdefNum(self: *SharedObject) u32 {
281}308}
282309
283pub fn soname(self: *SharedObject) []const u8 {310pub fn soname(self: *SharedObject) []const u8 {
284 const entries = self.dynamicTable();311 for (self.dynamic_table.items) |entry| switch (entry.d_tag) {
285 for (entries) |entry| switch (entry.d_tag) {
286 elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))),312 elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))),
287 else => {},313 else => {},
288 };314 };
...@@ -342,20 +368,6 @@ pub fn getString(self: SharedObject, off: u32) [:0]const u8 {...@@ -342,20 +368,6 @@ pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
342 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);368 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
343}369}
344370
345pub fn getSymtabRaw(self: SharedObject) []align(1) const elf.Elf64_Sym {
346 const index = self.dynsym_sect_index orelse return &[0]elf.Elf64_Sym{};
347 const raw_symtab = self.shdrContents(index);
348 const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));
349 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
350 return symtab;
351}
352
353pub fn getStrtabRaw(self: SharedObject) []const u8 {
354 const index = self.dynsym_sect_index orelse return &[0]u8{};
355 const shdr = self.shdrs.items[index];
356 return self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
357}
358
359pub fn format(371pub fn format(
360 self: SharedObject,372 self: SharedObject,
361 comptime unused_fmt_string: []const u8,373 comptime unused_fmt_string: []const u8,
...@@ -407,6 +419,5 @@ const mem = std.mem;...@@ -407,6 +419,5 @@ const mem = std.mem;
407419
408const Allocator = mem.Allocator;420const Allocator = mem.Allocator;
409const Elf = @import("../Elf.zig");421const Elf = @import("../Elf.zig");
410const ElfShdr = @import("Object.zig").ElfShdr;
411const File = @import("file.zig").File;422const File = @import("file.zig").File;
412const Symbol = @import("Symbol.zig");423const Symbol = @import("Symbol.zig");
src/link/Elf/ZigObject.zig+10-13
...@@ -305,19 +305,16 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {...@@ -305,19 +305,16 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
305}305}
306306
307/// TODO actually create fake input shdrs and return that instead.307/// TODO actually create fake input shdrs and return that instead.
308pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr {308pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr {
309 _ = self;309 _ = self;
310 const shdr = shdr: {310 const atom = elf_file.atom(atom_index) orelse return Elf.null_shdr;
311 const atom = elf_file.atom(atom_index) orelse break :shdr Elf.null_shdr;311 const shndx = atom.outputShndx() orelse return Elf.null_shdr;
312 const shndx = atom.outputShndx() orelse break :shdr Elf.null_shdr;312 var shdr = elf_file.shdrs.items[shndx];
313 var shdr = elf_file.shdrs.items[shndx];313 shdr.sh_addr = 0;
314 shdr.sh_addr = 0;314 shdr.sh_offset = 0;
315 shdr.sh_offset = 0;315 shdr.sh_size = atom.size;
316 shdr.sh_size = atom.size;316 shdr.sh_addralign = atom.alignment.toByteUnits(1);
317 shdr.sh_addralign = atom.alignment.toByteUnits(1);317 return shdr;
318 break :shdr shdr;
319 };
320 return Object.ElfShdr.fromElf64Shdr(shdr) catch unreachable;
321}318}
322319
323pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {320pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
...@@ -525,7 +522,7 @@ pub fn writeAr(self: ZigObject, writer: anytype) !void {...@@ -525,7 +522,7 @@ pub fn writeAr(self: ZigObject, writer: anytype) !void {
525 .{ .name = name }522 .{ .name = name }
526 else523 else
527 .{ .name_off = self.output_ar_state.name_off },524 .{ .name_off = self.output_ar_state.name_off },
528 .size = @intCast(self.data.items.len),525 .size = self.data.items.len,
529 });526 });
530 try writer.writeAll(mem.asBytes(&hdr));527 try writer.writeAll(mem.asBytes(&hdr));
531 try writer.writeAll(self.data.items);528 try writer.writeAll(self.data.items);
src/link/Elf/eh_frame.zig+9-22
...@@ -5,7 +5,6 @@ pub const Fde = struct {...@@ -5,7 +5,6 @@ pub const Fde = struct {
5 cie_index: u32,5 cie_index: u32,
6 rel_index: u32 = 0,6 rel_index: u32 = 0,
7 rel_num: u32 = 0,7 rel_num: u32 = 0,
8 rel_section_index: u32 = 0,
9 input_section_index: u32 = 0,8 input_section_index: u32 = 0,
10 file_index: u32 = 0,9 file_index: u32 = 0,
11 alive: bool = true,10 alive: bool = true,
...@@ -20,10 +19,9 @@ pub const Fde = struct {...@@ -20,10 +19,9 @@ pub const Fde = struct {
20 return base + fde.out_offset;19 return base + fde.out_offset;
21 }20 }
2221
23 pub fn data(fde: Fde, elf_file: *Elf) []const u8 {22 pub fn data(fde: Fde, elf_file: *Elf) []u8 {
24 const object = elf_file.file(fde.file_index).?.object;23 const object = elf_file.file(fde.file_index).?.object;
25 const contents = object.shdrContents(fde.input_section_index);24 return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()];
26 return contents[fde.offset..][0..fde.calcSize()];
27 }25 }
2826
29 pub fn cie(fde: Fde, elf_file: *Elf) Cie {27 pub fn cie(fde: Fde, elf_file: *Elf) Cie {
...@@ -50,7 +48,7 @@ pub const Fde = struct {...@@ -50,7 +48,7 @@ pub const Fde = struct {
5048
51 pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {49 pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
52 const object = elf_file.file(fde.file_index).?.object;50 const object = elf_file.file(fde.file_index).?.object;
53 return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num];51 return object.relocs.items[fde.rel_index..][0..fde.rel_num];
54 }52 }
5553
56 pub fn format(54 pub fn format(
...@@ -106,7 +104,6 @@ pub const Cie = struct {...@@ -106,7 +104,6 @@ pub const Cie = struct {
106 size: usize,104 size: usize,
107 rel_index: u32 = 0,105 rel_index: u32 = 0,
108 rel_num: u32 = 0,106 rel_num: u32 = 0,
109 rel_section_index: u32 = 0,
110 input_section_index: u32 = 0,107 input_section_index: u32 = 0,
111 file_index: u32 = 0,108 file_index: u32 = 0,
112 /// Includes 4byte size cell.109 /// Includes 4byte size cell.
...@@ -121,10 +118,9 @@ pub const Cie = struct {...@@ -121,10 +118,9 @@ pub const Cie = struct {
121 return base + cie.out_offset;118 return base + cie.out_offset;
122 }119 }
123120
124 pub fn data(cie: Cie, elf_file: *Elf) []const u8 {121 pub fn data(cie: Cie, elf_file: *Elf) []u8 {
125 const object = elf_file.file(cie.file_index).?.object;122 const object = elf_file.file(cie.file_index).?.object;
126 const contents = object.shdrContents(cie.input_section_index);123 return object.eh_frame_data.items[cie.offset..][0..cie.calcSize()];
127 return contents[cie.offset..][0..cie.calcSize()];
128 }124 }
129125
130 pub fn calcSize(cie: Cie) usize {126 pub fn calcSize(cie: Cie) usize {
...@@ -133,7 +129,7 @@ pub const Cie = struct {...@@ -133,7 +129,7 @@ pub const Cie = struct {
133129
134 pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela {130 pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
135 const object = elf_file.file(cie.file_index).?.object;131 const object = elf_file.file(cie.file_index).?.object;
136 return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num];132 return object.relocs.items[cie.rel_index..][0..cie.rel_num];
137 }133 }
138134
139 pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool {135 pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool {
...@@ -330,9 +326,6 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:...@@ -330,9 +326,6 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:
330}326}
331327
332pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {328pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
333 const comp = elf_file.base.comp;
334 const gpa = comp.gpa;
335
336 relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});329 relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});
337330
338 for (elf_file.objects.items) |index| {331 for (elf_file.objects.items) |index| {
...@@ -341,8 +334,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -341,8 +334,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
341 for (object.cies.items) |cie| {334 for (object.cies.items) |cie| {
342 if (!cie.alive) continue;335 if (!cie.alive) continue;
343336
344 const contents = try gpa.dupe(u8, cie.data(elf_file));337 const contents = cie.data(elf_file);
345 defer gpa.free(contents);
346338
347 for (cie.relocs(elf_file)) |rel| {339 for (cie.relocs(elf_file)) |rel| {
348 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);340 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
...@@ -359,8 +351,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -359,8 +351,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
359 for (object.fdes.items) |fde| {351 for (object.fdes.items) |fde| {
360 if (!fde.alive) continue;352 if (!fde.alive) continue;
361353
362 const contents = try gpa.dupe(u8, fde.data(elf_file));354 const contents = fde.data(elf_file);
363 defer gpa.free(contents);
364355
365 std.mem.writeInt(356 std.mem.writeInt(
366 i32,357 i32,
...@@ -382,9 +373,6 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -382,9 +373,6 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
382}373}
383374
384pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {375pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {
385 const comp = elf_file.base.comp;
386 const gpa = comp.gpa;
387
388 for (elf_file.objects.items) |index| {376 for (elf_file.objects.items) |index| {
389 const object = elf_file.file(index).?.object;377 const object = elf_file.file(index).?.object;
390378
...@@ -400,8 +388,7 @@ pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {...@@ -400,8 +388,7 @@ pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {
400 for (object.fdes.items) |fde| {388 for (object.fdes.items) |fde| {
401 if (!fde.alive) continue;389 if (!fde.alive) continue;
402390
403 const contents = try gpa.dupe(u8, fde.data(elf_file));391 const contents = fde.data(elf_file);
404 defer gpa.free(contents);
405392
406 std.mem.writeInt(393 std.mem.writeInt(
407 i32,394 i32,
src/link/Elf/file.zig+7-4
...@@ -162,18 +162,18 @@ pub const File = union(enum) {...@@ -162,18 +162,18 @@ pub const File = union(enum) {
162 state.name_off = try ar_strtab.insert(allocator, path);162 state.name_off = try ar_strtab.insert(allocator, path);
163 }163 }
164164
165 pub fn updateArSize(file: File) void {165 pub fn updateArSize(file: File, elf_file: *Elf) !void {
166 return switch (file) {166 return switch (file) {
167 .zig_object => |x| x.updateArSize(),167 .zig_object => |x| x.updateArSize(),
168 .object => |x| x.updateArSize(),168 .object => |x| x.updateArSize(elf_file),
169 inline else => unreachable,169 inline else => unreachable,
170 };170 };
171 }171 }
172172
173 pub fn writeAr(file: File, writer: anytype) !void {173 pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {
174 return switch (file) {174 return switch (file) {
175 .zig_object => |x| x.writeAr(writer),175 .zig_object => |x| x.writeAr(writer),
176 .object => |x| x.writeAr(writer),176 .object => |x| x.writeAr(elf_file, writer),
177 inline else => unreachable,177 inline else => unreachable,
178 };178 };
179 }179 }
...@@ -187,6 +187,9 @@ pub const File = union(enum) {...@@ -187,6 +187,9 @@ pub const File = union(enum) {
187 object: Object,187 object: Object,
188 shared_object: SharedObject,188 shared_object: SharedObject,
189 };189 };
190
191 pub const Handle = std.fs.File;
192 pub const HandleIndex = Index;
190};193};
191194
192const std = @import("std");195const std = @import("std");
src/link/Elf/relocatable.zig created+565
...@@ -0,0 +1,565 @@
1pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
2 const gpa = comp.gpa;
3
4 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
5 defer positionals.deinit();
6
7 try positionals.ensureUnusedCapacity(comp.objects.len);
8 positionals.appendSliceAssumeCapacity(comp.objects);
9
10 for (comp.c_object_table.keys()) |key| {
11 try positionals.append(.{ .path = key.status.success.object_path });
12 }
13
14 if (module_obj_path) |path| try positionals.append(.{ .path = path });
15
16 if (comp.include_compiler_rt) {
17 try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });
18 }
19
20 for (positionals.items) |obj| {
21 parsePositional(elf_file, obj.path) catch |err| switch (err) {
22 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
23 error.UnknownFileType => try elf_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
24 else => |e| try elf_file.reportParseError(
25 obj.path,
26 "unexpected error: parsing input file failed with error {s}",
27 .{@errorName(e)},
28 ),
29 };
30 }
31
32 if (comp.link_errors.items.len > 0) return error.FlushFailure;
33
34 // First, we flush relocatable object file generated with our backends.
35 if (elf_file.zigObjectPtr()) |zig_object| {
36 zig_object.resolveSymbols(elf_file);
37 zig_object.claimUnresolvedObject(elf_file);
38
39 try elf_file.initSymtab();
40 try elf_file.initShStrtab();
41 try elf_file.sortShdrs();
42 try zig_object.addAtomsToRelaSections(elf_file);
43 try updateSectionSizes(elf_file);
44
45 try allocateAllocSections(elf_file);
46 try elf_file.allocateNonAllocSections();
47
48 if (build_options.enable_logging) {
49 state_log.debug("{}", .{elf_file.dumpState()});
50 }
51
52 try writeSyntheticSections(elf_file);
53 try elf_file.writeShdrTable();
54 try elf_file.writeElfHeader();
55
56 // TODO we can avoid reading in the file contents we just wrote if we give the linker
57 // ability to write directly to a buffer.
58 try zig_object.readFileContents(elf_file);
59 }
60
61 var files = std.ArrayList(File.Index).init(gpa);
62 defer files.deinit();
63 try files.ensureTotalCapacityPrecise(elf_file.objects.items.len + 1);
64 if (elf_file.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
65 for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
66
67 // Update ar symtab from parsed objects
68 var ar_symtab: Archive.ArSymtab = .{};
69 defer ar_symtab.deinit(gpa);
70
71 for (files.items) |index| {
72 try elf_file.file(index).?.updateArSymtab(&ar_symtab, elf_file);
73 }
74
75 ar_symtab.sort();
76
77 // Save object paths in filenames strtab.
78 var ar_strtab: Archive.ArStrtab = .{};
79 defer ar_strtab.deinit(gpa);
80
81 for (files.items) |index| {
82 const file_ptr = elf_file.file(index).?;
83 try file_ptr.updateArStrtab(gpa, &ar_strtab);
84 try file_ptr.updateArSize(elf_file);
85 }
86
87 // Update file offsets of contributing objects.
88 const total_size: usize = blk: {
89 var pos: usize = elf.ARMAG.len;
90 pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64);
91
92 if (ar_strtab.size() > 0) {
93 pos = mem.alignForward(usize, pos, 2);
94 pos += @sizeOf(elf.ar_hdr) + ar_strtab.size();
95 }
96
97 for (files.items) |index| {
98 const file_ptr = elf_file.file(index).?;
99 const state = switch (file_ptr) {
100 .zig_object => |x| &x.output_ar_state,
101 .object => |x| &x.output_ar_state,
102 else => unreachable,
103 };
104 pos = mem.alignForward(usize, pos, 2);
105 state.file_off = pos;
106 pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow);
107 }
108
109 break :blk pos;
110 };
111
112 if (build_options.enable_logging) {
113 state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(elf_file)});
114 state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
115 }
116
117 var buffer = std.ArrayList(u8).init(gpa);
118 defer buffer.deinit();
119 try buffer.ensureTotalCapacityPrecise(total_size);
120
121 // Write magic
122 try buffer.writer().writeAll(elf.ARMAG);
123
124 // Write symtab
125 try ar_symtab.write(.p64, elf_file, buffer.writer());
126
127 // Write strtab
128 if (ar_strtab.size() > 0) {
129 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
130 try ar_strtab.write(buffer.writer());
131 }
132
133 // Write object files
134 for (files.items) |index| {
135 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
136 try elf_file.file(index).?.writeAr(elf_file, buffer.writer());
137 }
138
139 assert(buffer.items.len == total_size);
140
141 try elf_file.base.file.?.setEndPos(total_size);
142 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
143
144 if (comp.link_errors.items.len > 0) return error.FlushFailure;
145}
146
147pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
148 const gpa = elf_file.base.comp.gpa;
149
150 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
151 defer positionals.deinit();
152 try positionals.ensureUnusedCapacity(comp.objects.len);
153 positionals.appendSliceAssumeCapacity(comp.objects);
154
155 // This is a set of object files emitted by clang in a single `build-exe` invocation.
156 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
157 // in this set.
158 for (comp.c_object_table.keys()) |key| {
159 try positionals.append(.{ .path = key.status.success.object_path });
160 }
161
162 if (module_obj_path) |path| try positionals.append(.{ .path = path });
163
164 for (positionals.items) |obj| {
165 elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
166 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
167 else => |e| try elf_file.reportParseError(
168 obj.path,
169 "unexpected error: parsing input file failed with error {s}",
170 .{@errorName(e)},
171 ),
172 };
173 }
174
175 if (comp.link_errors.items.len > 0) return error.FlushFailure;
176
177 // Now, we are ready to resolve the symbols across all input files.
178 // We will first resolve the files in the ZigObject, next in the parsed
179 // input Object files.
180 elf_file.resolveSymbols();
181 elf_file.markEhFrameAtomsDead();
182 claimUnresolved(elf_file);
183
184 try initSections(elf_file);
185 try elf_file.sortShdrs();
186 if (elf_file.zigObjectPtr()) |zig_object| {
187 try zig_object.addAtomsToRelaSections(elf_file);
188 }
189 for (elf_file.objects.items) |index| {
190 const object = elf_file.file(index).?.object;
191 try object.addAtomsToOutputSections(elf_file);
192 try object.addAtomsToRelaSections(elf_file);
193 }
194 try updateSectionSizes(elf_file);
195
196 try allocateAllocSections(elf_file);
197 try elf_file.allocateNonAllocSections();
198 elf_file.allocateAtoms();
199
200 if (build_options.enable_logging) {
201 state_log.debug("{}", .{elf_file.dumpState()});
202 }
203
204 try writeAtoms(elf_file);
205 try writeSyntheticSections(elf_file);
206 try elf_file.writeShdrTable();
207 try elf_file.writeElfHeader();
208
209 if (comp.link_errors.items.len > 0) return error.FlushFailure;
210}
211
212fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
213 if (try Object.isObject(path)) {
214 try parseObject(elf_file, path);
215 } else if (try Archive.isArchive(path)) {
216 try parseArchive(elf_file, path);
217 } else return error.UnknownFileType;
218 // TODO: should we check for LD script?
219 // Actually, should we even unpack an archive?
220}
221
222fn parseObject(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
223 const gpa = elf_file.base.comp.gpa;
224 const handle = try std.fs.cwd().openFile(path, .{});
225 const fh = try elf_file.addFileHandle(handle);
226
227 const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
228 elf_file.files.set(index, .{ .object = .{
229 .path = try gpa.dupe(u8, path),
230 .file_handle = fh,
231 .index = index,
232 } });
233 try elf_file.objects.append(gpa, index);
234
235 const object = elf_file.file(index).?.object;
236 try object.parseAr(elf_file);
237}
238
239fn parseArchive(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
240 const gpa = elf_file.base.comp.gpa;
241 const handle = try std.fs.cwd().openFile(path, .{});
242 const fh = try elf_file.addFileHandle(handle);
243
244 var archive = Archive{};
245 defer archive.deinit(gpa);
246 try archive.parse(elf_file, path, fh);
247
248 const objects = try archive.objects.toOwnedSlice(gpa);
249 defer gpa.free(objects);
250
251 for (objects) |extracted| {
252 const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
253 elf_file.files.set(index, .{ .object = extracted });
254 const object = &elf_file.files.items(.data)[index].object;
255 object.index = index;
256 try object.parseAr(elf_file);
257 try elf_file.objects.append(gpa, index);
258 }
259}
260
261fn claimUnresolved(elf_file: *Elf) void {
262 if (elf_file.zigObjectPtr()) |zig_object| {
263 zig_object.claimUnresolvedObject(elf_file);
264 }
265 for (elf_file.objects.items) |index| {
266 elf_file.file(index).?.object.claimUnresolvedObject(elf_file);
267 }
268}
269
270fn initSections(elf_file: *Elf) !void {
271 const ptr_size = elf_file.ptrWidthBytes();
272
273 for (elf_file.objects.items) |index| {
274 const object = elf_file.file(index).?.object;
275 try object.initOutputSections(elf_file);
276 try object.initRelaSections(elf_file);
277 }
278
279 const needs_eh_frame = for (elf_file.objects.items) |index| {
280 if (elf_file.file(index).?.object.cies.items.len > 0) break true;
281 } else false;
282 if (needs_eh_frame) {
283 elf_file.eh_frame_section_index = try elf_file.addSection(.{
284 .name = ".eh_frame",
285 .type = elf.SHT_PROGBITS,
286 .flags = elf.SHF_ALLOC,
287 .addralign = ptr_size,
288 .offset = std.math.maxInt(u64),
289 });
290 elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(".rela.eh_frame", elf_file.eh_frame_section_index.?);
291 }
292
293 try initComdatGroups(elf_file);
294 try elf_file.initSymtab();
295 try elf_file.initShStrtab();
296}
297
298fn initComdatGroups(elf_file: *Elf) !void {
299 const gpa = elf_file.base.comp.gpa;
300
301 for (elf_file.objects.items) |index| {
302 const object = elf_file.file(index).?.object;
303
304 for (object.comdat_groups.items) |cg_index| {
305 const cg = elf_file.comdatGroup(cg_index);
306 const cg_owner = elf_file.comdatGroupOwner(cg.owner);
307 if (cg_owner.file != index) continue;
308
309 const cg_sec = try elf_file.comdat_group_sections.addOne(gpa);
310 cg_sec.* = .{
311 .shndx = try elf_file.addSection(.{
312 .name = ".group",
313 .type = elf.SHT_GROUP,
314 .entsize = @sizeOf(u32),
315 .addralign = @alignOf(u32),
316 .offset = std.math.maxInt(u64),
317 }),
318 .cg_index = cg_index,
319 };
320 }
321 }
322}
323
324fn updateSectionSizes(elf_file: *Elf) !void {
325 for (elf_file.output_sections.keys(), elf_file.output_sections.values()) |shndx, atom_list| {
326 const shdr = &elf_file.shdrs.items[shndx];
327 for (atom_list.items) |atom_index| {
328 const atom_ptr = elf_file.atom(atom_index) orelse continue;
329 if (!atom_ptr.flags.alive) continue;
330 const offset = atom_ptr.alignment.forward(shdr.sh_size);
331 const padding = offset - shdr.sh_size;
332 atom_ptr.value = offset;
333 shdr.sh_size += padding + atom_ptr.size;
334 shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
335 }
336 }
337
338 for (elf_file.output_rela_sections.values()) |sec| {
339 const shdr = &elf_file.shdrs.items[sec.shndx];
340 for (sec.atom_list.items) |atom_index| {
341 const atom_ptr = elf_file.atom(atom_index) orelse continue;
342 if (!atom_ptr.flags.alive) continue;
343 const relocs = atom_ptr.relocs(elf_file);
344 shdr.sh_size += shdr.sh_entsize * relocs.len;
345 }
346
347 if (shdr.sh_size == 0) shdr.sh_offset = 0;
348 }
349
350 if (elf_file.eh_frame_section_index) |index| {
351 elf_file.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(elf_file);
352 }
353 if (elf_file.eh_frame_rela_section_index) |index| {
354 const shdr = &elf_file.shdrs.items[index];
355 shdr.sh_size = eh_frame.calcEhFrameRelocs(elf_file) * shdr.sh_entsize;
356 }
357
358 try elf_file.updateSymtabSize();
359 updateComdatGroupsSizes(elf_file);
360 elf_file.updateShStrtabSize();
361}
362
363fn updateComdatGroupsSizes(elf_file: *Elf) void {
364 for (elf_file.comdat_group_sections.items) |cg| {
365 const shdr = &elf_file.shdrs.items[cg.shndx];
366 shdr.sh_size = cg.size(elf_file);
367 shdr.sh_link = elf_file.symtab_section_index.?;
368
369 const sym = elf_file.symbol(cg.symbol(elf_file));
370 shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse
371 elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
372 }
373}
374
375/// Allocates alloc sections when merging relocatable objects files together.
376fn allocateAllocSections(elf_file: *Elf) !void {
377 for (elf_file.shdrs.items) |*shdr| {
378 if (shdr.sh_type == elf.SHT_NULL) continue;
379 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
380 if (shdr.sh_type == elf.SHT_NOBITS) {
381 shdr.sh_offset = 0;
382 continue;
383 }
384 const needed_size = shdr.sh_size;
385 if (needed_size > elf_file.allocatedSize(shdr.sh_offset)) {
386 shdr.sh_size = 0;
387 const new_offset = elf_file.findFreeSpace(needed_size, shdr.sh_addralign);
388 shdr.sh_offset = new_offset;
389 shdr.sh_size = needed_size;
390 }
391 }
392}
393
394fn writeAtoms(elf_file: *Elf) !void {
395 const gpa = elf_file.base.comp.gpa;
396
397 // TODO iterate over `output_sections` directly
398 for (elf_file.shdrs.items, 0..) |shdr, shndx| {
399 if (shdr.sh_type == elf.SHT_NULL) continue;
400 if (shdr.sh_type == elf.SHT_NOBITS) continue;
401
402 const atom_list = elf_file.output_sections.get(@intCast(shndx)) orelse continue;
403 if (atom_list.items.len == 0) continue;
404
405 log.debug("writing atoms in '{s}' section", .{elf_file.getShString(shdr.sh_name)});
406
407 // TODO really, really handle debug section separately
408 const base_offset = if (elf_file.isDebugSection(@intCast(shndx))) blk: {
409 const zig_object = elf_file.zigObjectPtr().?;
410 if (shndx == elf_file.debug_info_section_index.?)
411 break :blk zig_object.debug_info_section_zig_size;
412 if (shndx == elf_file.debug_abbrev_section_index.?)
413 break :blk zig_object.debug_abbrev_section_zig_size;
414 if (shndx == elf_file.debug_str_section_index.?)
415 break :blk zig_object.debug_str_section_zig_size;
416 if (shndx == elf_file.debug_aranges_section_index.?)
417 break :blk zig_object.debug_aranges_section_zig_size;
418 if (shndx == elf_file.debug_line_section_index.?)
419 break :blk zig_object.debug_line_section_zig_size;
420 unreachable;
421 } else 0;
422 const sh_offset = shdr.sh_offset + base_offset;
423 const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
424
425 const buffer = try gpa.alloc(u8, sh_size);
426 defer gpa.free(buffer);
427 const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
428 shdr.sh_flags & elf.SHF_EXECINSTR != 0)
429 0xcc // int3
430 else
431 0;
432 @memset(buffer, padding_byte);
433
434 for (atom_list.items) |atom_index| {
435 const atom_ptr = elf_file.atom(atom_index).?;
436 assert(atom_ptr.flags.alive);
437
438 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
439 return error.Overflow;
440 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
441
442 log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
443 atom_index,
444 sh_offset + offset,
445 sh_offset + offset + size,
446 });
447
448 // TODO decompress directly into provided buffer
449 const out_code = buffer[offset..][0..size];
450 const in_code = switch (atom_ptr.file(elf_file).?) {
451 .object => |x| try x.codeDecompressAlloc(elf_file, atom_index),
452 .zig_object => |x| try x.codeAlloc(elf_file, atom_index),
453 else => unreachable,
454 };
455 defer gpa.free(in_code);
456 @memcpy(out_code, in_code);
457 }
458
459 try elf_file.base.file.?.pwriteAll(buffer, sh_offset);
460 }
461}
462
463fn writeSyntheticSections(elf_file: *Elf) !void {
464 const gpa = elf_file.base.comp.gpa;
465
466 for (elf_file.output_rela_sections.values()) |sec| {
467 if (sec.atom_list.items.len == 0) continue;
468
469 const shdr = elf_file.shdrs.items[sec.shndx];
470
471 const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
472 return error.Overflow;
473 var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
474 defer relocs.deinit();
475
476 for (sec.atom_list.items) |atom_index| {
477 const atom_ptr = elf_file.atom(atom_index) orelse continue;
478 if (!atom_ptr.flags.alive) continue;
479 try atom_ptr.writeRelocs(elf_file, &relocs);
480 }
481 assert(relocs.items.len == num_relocs);
482
483 const SortRelocs = struct {
484 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
485 _ = ctx;
486 return lhs.r_offset < rhs.r_offset;
487 }
488 };
489
490 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
491
492 log.debug("writing {s} from 0x{x} to 0x{x}", .{
493 elf_file.getShString(shdr.sh_name),
494 shdr.sh_offset,
495 shdr.sh_offset + shdr.sh_size,
496 });
497
498 try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
499 }
500
501 if (elf_file.eh_frame_section_index) |shndx| {
502 const shdr = elf_file.shdrs.items[shndx];
503 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
504 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
505 defer buffer.deinit();
506 try eh_frame.writeEhFrameObject(elf_file, buffer.writer());
507 log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
508 shdr.sh_offset,
509 shdr.sh_offset + shdr.sh_size,
510 });
511 assert(buffer.items.len == sh_size);
512 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
513 }
514 if (elf_file.eh_frame_rela_section_index) |shndx| {
515 const shdr = elf_file.shdrs.items[shndx];
516 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
517 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
518 defer buffer.deinit();
519 try eh_frame.writeEhFrameRelocs(elf_file, buffer.writer());
520 assert(buffer.items.len == sh_size);
521 log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
522 shdr.sh_offset,
523 shdr.sh_offset + shdr.sh_size,
524 });
525 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
526 }
527
528 try writeComdatGroups(elf_file);
529 try elf_file.writeSymtab();
530 try elf_file.writeShStrtab();
531}
532
533fn writeComdatGroups(elf_file: *Elf) !void {
534 const gpa = elf_file.base.comp.gpa;
535 for (elf_file.comdat_group_sections.items) |cgs| {
536 const shdr = elf_file.shdrs.items[cgs.shndx];
537 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
538 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
539 defer buffer.deinit();
540 try cgs.write(elf_file, buffer.writer());
541 assert(buffer.items.len == sh_size);
542 log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{
543 shdr.sh_offset,
544 shdr.sh_offset + shdr.sh_size,
545 });
546 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
547 }
548}
549
550const assert = std.debug.assert;
551const build_options = @import("build_options");
552const eh_frame = @import("eh_frame.zig");
553const elf = std.elf;
554const link = @import("../../link.zig");
555const log = std.log.scoped(.link);
556const math = std.math;
557const mem = std.mem;
558const state_log = std.log.scoped(.link_state);
559const std = @import("std");
560
561const Archive = @import("Archive.zig");
562const Compilation = @import("../../Compilation.zig");
563const Elf = @import("../Elf.zig");
564const File = @import("file.zig").File;
565const Object = @import("Object.zig");
src/link/Elf/synthetic_sections.zig+2-3
...@@ -1582,15 +1582,14 @@ pub const ComdatGroupSection = struct {...@@ -1582,15 +1582,14 @@ pub const ComdatGroupSection = struct {
15821582
1583 pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {1583 pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {
1584 const cg = elf_file.comdatGroup(cgs.cg_index);1584 const cg = elf_file.comdatGroup(cgs.cg_index);
1585 const object = cgs.file(elf_file).?.object;1585 const members = cg.comdatGroupMembers(elf_file);
1586 const members = object.comdatGroupMembers(cg.shndx);
1587 return (members.len + 1) * @sizeOf(u32);1586 return (members.len + 1) * @sizeOf(u32);
1588 }1587 }
15891588
1590 pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void {1589 pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void {
1591 const cg = elf_file.comdatGroup(cgs.cg_index);1590 const cg = elf_file.comdatGroup(cgs.cg_index);
1592 const object = cgs.file(elf_file).?.object;1591 const object = cgs.file(elf_file).?.object;
1593 const members = object.comdatGroupMembers(cg.shndx);1592 const members = cg.comdatGroupMembers(elf_file);
1594 try writer.writeInt(u32, elf.GRP_COMDAT, .little);1593 try writer.writeInt(u32, elf.GRP_COMDAT, .little);
1595 for (members) |shndx| {1594 for (members) |shndx| {
1596 const shdr = object.shdrs.items[shndx];1595 const shdr = object.shdrs.items[shndx];
src/link/MachO/Archive.zig+12-16
...@@ -24,20 +24,18 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -24,20 +24,18 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
24 const handle = macho_file.getFileHandle(handle_index);24 const handle = macho_file.getFileHandle(handle_index);
25 const offset = if (fat_arch) |ar| ar.offset else 0;25 const offset = if (fat_arch) |ar| ar.offset else 0;
26 const size = if (fat_arch) |ar| ar.size else (try handle.stat()).size;26 const size = if (fat_arch) |ar| ar.size else (try handle.stat()).size;
27 try handle.seekTo(offset);
2827
29 const reader = handle.reader();28 var pos: usize = offset + SARMAG;
30 _ = try reader.readBytesNoEof(SARMAG);
31
32 var pos: usize = SARMAG;
33 while (true) {29 while (true) {
34 if (pos >= size) break;30 if (pos >= size) break;
35 if (!mem.isAligned(pos, 2)) {31 if (!mem.isAligned(pos, 2)) pos += 1;
36 try handle.seekBy(1);
37 pos += 1;
38 }
3932
40 const hdr = try reader.readStruct(ar_hdr);33 var hdr_buffer: [@sizeOf(ar_hdr)]u8 = undefined;
34 {
35 const amt = try handle.preadAll(&hdr_buffer, pos);
36 if (amt != @sizeOf(ar_hdr)) return error.InputOutput;
37 }
38 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;
41 pos += @sizeOf(ar_hdr);39 pos += @sizeOf(ar_hdr);
4240
43 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {41 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
...@@ -53,17 +51,15 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -53,17 +51,15 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
53 if (try hdr.nameLength()) |len| {51 if (try hdr.nameLength()) |len| {
54 hdr_size -= len;52 hdr_size -= len;
55 const buf = try arena.allocator().alloc(u8, len);53 const buf = try arena.allocator().alloc(u8, len);
56 try reader.readNoEof(buf);54 const amt = try handle.preadAll(buf, pos);
55 if (amt != len) return error.InputOutput;
57 pos += len;56 pos += len;
58 const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len;57 const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len;
59 break :name buf[0..actual_len];58 break :name buf[0..actual_len];
60 }59 }
61 unreachable;60 unreachable;
62 };61 };
63 defer {62 defer pos += hdr_size;
64 _ = handle.seekBy(hdr_size) catch {};
65 pos += hdr_size;
66 }
6763
68 if (mem.eql(u8, name, SYMDEF) or64 if (mem.eql(u8, name, SYMDEF) or
69 mem.eql(u8, name, SYMDEF64) or65 mem.eql(u8, name, SYMDEF64) or
...@@ -73,7 +69,7 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -73,7 +69,7 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
73 const object = Object{69 const object = Object{
74 .archive = .{70 .archive = .{
75 .path = try gpa.dupe(u8, path),71 .path = try gpa.dupe(u8, path),
76 .offset = offset + pos,72 .offset = pos,
77 },73 },
78 .path = try gpa.dupe(u8, name),74 .path = try gpa.dupe(u8, name),
79 .file_handle = handle_index,75 .file_handle = handle_index,