| ... | @@ -1052,13 +1052,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1052,13 +1052,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1052 | const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. | 1052 | const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. |
| 1053 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); | 1053 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); |
| 1054 | | 1054 | |
| 1055 | const compiler_rt_path: ?[]const u8 = blk: { | | |
| 1056 | if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; | | |
| 1057 | if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; | | |
| 1058 | break :blk null; | | |
| 1059 | }; | | |
| 1060 | _ = compiler_rt_path; | | |
| 1061 | | | |
| 1062 | // Here we will parse input positional and library files (if referenced). | 1055 | // Here we will parse input positional and library files (if referenced). |
| 1063 | // This will roughly match in any linker backend we support. | 1056 | // This will roughly match in any linker backend we support. |
| 1064 | var positionals = std.ArrayList(Compilation.LinkObject).init(arena); | 1057 | var positionals = std.ArrayList(Compilation.LinkObject).init(arena); |
| ... | @@ -1084,6 +1077,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1084,6 +1077,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1084 | try positionals.append(.{ .path = key.status.success.object_path }); | 1077 | try positionals.append(.{ .path = key.status.success.object_path }); |
| 1085 | } | 1078 | } |
| 1086 | | 1079 | |
| | 1080 | const compiler_rt_path: ?[]const u8 = blk: { |
| | 1081 | if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; |
| | 1082 | if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; |
| | 1083 | break :blk null; |
| | 1084 | }; |
| | 1085 | if (compiler_rt_path) |path| { |
| | 1086 | try positionals.append(.{ .path = path }); |
| | 1087 | } |
| | 1088 | |
| 1087 | for (positionals.items) |obj| { | 1089 | for (positionals.items) |obj| { |
| 1088 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); | 1090 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); |
| 1089 | defer in_file.close(); | 1091 | defer in_file.close(); |
| ... | @@ -1140,7 +1142,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1140,7 +1142,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1140 | // input Object files. | 1142 | // input Object files. |
| 1141 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak | 1143 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak |
| 1142 | // symbol for potential resolution at load-time. | 1144 | // symbol for potential resolution at load-time. |
| 1143 | self.resolveSymbols(); | 1145 | try self.resolveSymbols(); |
| 1144 | self.markImportsExports(); | 1146 | self.markImportsExports(); |
| 1145 | self.claimUnresolved(); | 1147 | self.claimUnresolved(); |
| 1146 | | 1148 | |
| ... | @@ -1403,6 +1405,7 @@ const ParseError = error{ | ... | @@ -1403,6 +1405,7 @@ const ParseError = error{ |
| 1403 | EndOfStream, | 1405 | EndOfStream, |
| 1404 | FileSystem, | 1406 | FileSystem, |
| 1405 | NotSupported, | 1407 | NotSupported, |
| | 1408 | InvalidCharacter, |
| 1406 | } || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; | 1409 | } || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; |
| 1407 | | 1410 | |
| 1408 | fn parsePositional( | 1411 | fn parsePositional( |
| ... | @@ -1414,10 +1417,32 @@ fn parsePositional( | ... | @@ -1414,10 +1417,32 @@ fn parsePositional( |
| 1414 | ) ParseError!void { | 1417 | ) ParseError!void { |
| 1415 | const tracy = trace(@src()); | 1418 | const tracy = trace(@src()); |
| 1416 | defer tracy.end(); | 1419 | defer tracy.end(); |
| 1417 | _ = must_link; | | |
| 1418 | | 1420 | |
| 1419 | if (Object.isObject(in_file)) { | 1421 | if (Object.isObject(in_file)) { |
| 1420 | try self.parseObject(in_file, path, ctx); | 1422 | try self.parseObject(in_file, path, ctx); |
| | 1423 | } else { |
| | 1424 | try self.parseLibrary(in_file, path, .{ |
| | 1425 | .path = null, |
| | 1426 | .needed = false, |
| | 1427 | .weak = false, |
| | 1428 | }, must_link, ctx); |
| | 1429 | } |
| | 1430 | } |
| | 1431 | |
| | 1432 | fn parseLibrary( |
| | 1433 | self: *Elf, |
| | 1434 | in_file: std.fs.File, |
| | 1435 | path: []const u8, |
| | 1436 | lib: link.SystemLib, |
| | 1437 | must_link: bool, |
| | 1438 | ctx: *ParseErrorCtx, |
| | 1439 | ) ParseError!void { |
| | 1440 | const tracy = trace(@src()); |
| | 1441 | defer tracy.end(); |
| | 1442 | _ = lib; |
| | 1443 | |
| | 1444 | if (Archive.isArchive(in_file)) { |
| | 1445 | try self.parseArchive(in_file, path, must_link, ctx); |
| 1421 | } else return error.UnknownFileType; | 1446 | } else return error.UnknownFileType; |
| 1422 | } | 1447 | } |
| 1423 | | 1448 | |
| ... | @@ -1442,15 +1467,109 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr | ... | @@ -1442,15 +1467,109 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr |
| 1442 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; | 1467 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; |
| 1443 | } | 1468 | } |
| 1444 | | 1469 | |
| 1445 | fn resolveSymbols(self: *Elf) void { | 1470 | fn parseArchive( |
| 1446 | if (self.zig_module_index) |index| { | 1471 | self: *Elf, |
| 1447 | const zig_module = self.file(index).?.zig_module; | 1472 | in_file: std.fs.File, |
| 1448 | zig_module.resolveSymbols(self); | 1473 | path: []const u8, |
| | 1474 | must_link: bool, |
| | 1475 | ctx: *ParseErrorCtx, |
| | 1476 | ) ParseError!void { |
| | 1477 | const tracy = trace(@src()); |
| | 1478 | defer tracy.end(); |
| | 1479 | |
| | 1480 | const gpa = self.base.allocator; |
| | 1481 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| | 1482 | var archive = Archive{ .path = path, .data = data }; |
| | 1483 | defer archive.deinit(gpa); |
| | 1484 | try archive.parse(self); |
| | 1485 | |
| | 1486 | for (archive.objects.items) |extracted| { |
| | 1487 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| | 1488 | self.files.set(index, .{ .object = extracted }); |
| | 1489 | const object = &self.files.items(.data)[index].object; |
| | 1490 | object.index = index; |
| | 1491 | object.alive = must_link; |
| | 1492 | try object.parse(self); |
| | 1493 | try self.objects.append(gpa, index); |
| | 1494 | |
| | 1495 | ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?; |
| | 1496 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; |
| | 1497 | } |
| | 1498 | } |
| | 1499 | |
| | 1500 | /// When resolving symbols, we approach the problem similarly to `mold`. |
| | 1501 | /// 1. Resolve symbols across all objects (including those preemptively extracted archives). |
| | 1502 | /// 2. Resolve symbols across all shared objects. |
| | 1503 | /// 3. Mark live objects (see `Elf.markLive`) |
| | 1504 | /// 4. Reset state of all resolved globals since we will redo this bit on the pruned set. |
| | 1505 | /// 5. Remove references to dead objects/shared objects |
| | 1506 | /// 6. Re-run symbol resolution on pruned objects and shared objects sets. |
| | 1507 | fn resolveSymbols(self: *Elf) error{Overflow}!void { |
| | 1508 | // Resolve symbols in the ZigModule. For now, we assume that it's always live. |
| | 1509 | if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); |
| | 1510 | // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). |
| | 1511 | for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); |
| | 1512 | |
| | 1513 | // Mark live objects. |
| | 1514 | self.markLive(); |
| | 1515 | |
| | 1516 | // Reset state of all globals after marking live objects. |
| | 1517 | if (self.zig_module_index) |index| self.file(index).?.resetGlobals(self); |
| | 1518 | for (self.objects.items) |index| self.file(index).?.resetGlobals(self); |
| | 1519 | |
| | 1520 | // Prune dead objects and shared objects. |
| | 1521 | var i: usize = 0; |
| | 1522 | while (i < self.objects.items.len) { |
| | 1523 | const index = self.objects.items[i]; |
| | 1524 | if (!self.file(index).?.isAlive()) { |
| | 1525 | _ = self.objects.orderedRemove(i); |
| | 1526 | } else i += 1; |
| 1449 | } | 1527 | } |
| 1450 | | 1528 | |
| | 1529 | // Dedup comdat groups. |
| 1451 | for (self.objects.items) |index| { | 1530 | for (self.objects.items) |index| { |
| 1452 | const object = self.file(index).?.object; | 1531 | const object = self.file(index).?.object; |
| 1453 | object.resolveSymbols(self); | 1532 | for (object.comdat_groups.items) |cg_index| { |
| | 1533 | const cg = self.comdatGroup(cg_index); |
| | 1534 | const cg_owner = self.comdatGroupOwner(cg.owner); |
| | 1535 | const owner_file_index = if (self.file(cg_owner.file)) |file_ptr| |
| | 1536 | file_ptr.object.index |
| | 1537 | else |
| | 1538 | std.math.maxInt(File.Index); |
| | 1539 | cg_owner.file = @min(owner_file_index, index); |
| | 1540 | } |
| | 1541 | } |
| | 1542 | |
| | 1543 | for (self.objects.items) |index| { |
| | 1544 | const object = self.file(index).?.object; |
| | 1545 | for (object.comdat_groups.items) |cg_index| { |
| | 1546 | const cg = self.comdatGroup(cg_index); |
| | 1547 | const cg_owner = self.comdatGroupOwner(cg.owner); |
| | 1548 | if (cg_owner.file != index) { |
| | 1549 | for (try object.comdatGroupMembers(cg.shndx)) |shndx| { |
| | 1550 | const atom_index = object.atoms.items[shndx]; |
| | 1551 | if (self.atom(atom_index)) |atom_ptr| { |
| | 1552 | atom_ptr.alive = false; |
| | 1553 | // atom_ptr.markFdesDead(self); |
| | 1554 | } |
| | 1555 | } |
| | 1556 | } |
| | 1557 | } |
| | 1558 | } |
| | 1559 | |
| | 1560 | // Re-resolve the symbols. |
| | 1561 | if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); |
| | 1562 | for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); |
| | 1563 | } |
| | 1564 | |
| | 1565 | /// Traverses all objects and shared objects marking any object referenced by |
| | 1566 | /// a live object/shared object as alive itself. |
| | 1567 | /// This routine will prune unneeded objects extracted from archives and |
| | 1568 | /// unneeded shared objects. |
| | 1569 | fn markLive(self: *Elf) void { |
| | 1570 | for (self.objects.items) |index| { |
| | 1571 | const file_ptr = self.file(index).?; |
| | 1572 | if (file_ptr.isAlive()) file_ptr.markLive(self); |
| 1454 | } | 1573 | } |
| 1455 | } | 1574 | } |
| 1456 | | 1575 | |
| ... | @@ -4059,6 +4178,7 @@ const synthetic_sections = @import("Elf/synthetic_sections.zig"); | ... | @@ -4059,6 +4178,7 @@ const synthetic_sections = @import("Elf/synthetic_sections.zig"); |
| 4059 | | 4178 | |
| 4060 | const Air = @import("../Air.zig"); | 4179 | const Air = @import("../Air.zig"); |
| 4061 | const Allocator = std.mem.Allocator; | 4180 | const Allocator = std.mem.Allocator; |
| | 4181 | const Archive = @import("Elf/Archive.zig"); |
| 4062 | pub const Atom = @import("Elf/Atom.zig"); | 4182 | pub const Atom = @import("Elf/Atom.zig"); |
| 4063 | const Cache = std.Build.Cache; | 4183 | const Cache = std.Build.Cache; |
| 4064 | const Compilation = @import("../Compilation.zig"); | 4184 | const Compilation = @import("../Compilation.zig"); |