| author | |
| committer | |
| log | 9a80ac0429b34541b91671246833c47143d1e4da |
| tree | 692c0ea0e5e0be753f4a35b6899c19294b29c869 |
| parent | 2ee221328f1aa1e34a37f38ac8426c1e8e8ec204 |
4 files changed, 199 insertions(+), 25 deletions(-)
CMakeLists.txt+1| ... | @@ -595,6 +595,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -595,6 +595,7 @@ set(ZIG_STAGE2_SOURCES |
| 595 | "${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig" | 595 | "${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig" |
| 596 | "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig" | 596 | "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig" |
| 597 | "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig" | 597 | "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig" |
| 598 | "${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig" | ||
| 598 | "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig" | 599 | "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig" |
| 599 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" | 600 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" |
| 600 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" | 601 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" |
src/link/Elf.zig+36-25| ... | @@ -53,7 +53,7 @@ phdr_load_tls_data_index: ?u16 = null, | ... | @@ -53,7 +53,7 @@ phdr_load_tls_data_index: ?u16 = null, |
| 53 | /// The index into the program headers of a PT_LOAD program header with TLS zerofill data. | 53 | /// The index into the program headers of a PT_LOAD program header with TLS zerofill data. |
| 54 | phdr_load_tls_zerofill_index: ?u16 = null, | 54 | phdr_load_tls_zerofill_index: ?u16 = null, |
| 55 | 55 | ||
| 56 | entry_addr: ?u64 = null, | 56 | entry_index: ?Symbol.Index = null, |
| 57 | page_size: u32, | 57 | page_size: u32, |
| 58 | default_sym_version: elf.Elf64_Versym, | 58 | default_sym_version: elf.Elf64_Versym, |
| 59 | 59 | ||
| ... | @@ -665,7 +665,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -665,7 +665,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 665 | .alignment = self.page_size, | 665 | .alignment = self.page_size, |
| 666 | .flags = elf.PF_X | elf.PF_R | elf.PF_W, | 666 | .flags = elf.PF_X | elf.PF_R | elf.PF_W, |
| 667 | }); | 667 | }); |
| 668 | self.entry_addr = null; | ||
| 669 | } | 668 | } |
| 670 | 669 | ||
| 671 | if (self.phdr_got_index == null) { | 670 | if (self.phdr_got_index == null) { |
| ... | @@ -1283,9 +1282,29 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1283,9 +1282,29 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1283 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak | 1282 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak |
| 1284 | // symbol for potential resolution at load-time. | 1283 | // symbol for potential resolution at load-time. |
| 1285 | self.resolveSymbols(); | 1284 | self.resolveSymbols(); |
| 1286 | try self.addLinkerDefinedSymbols(); | ||
| 1287 | self.markEhFrameAtomsDead(); | 1285 | self.markEhFrameAtomsDead(); |
| 1288 | self.markImportsExports(); | 1286 | self.markImportsExports(); |
| 1287 | |||
| 1288 | // Look for entry address in objects if not set by the incremental compiler. | ||
| 1289 | if (self.entry_index == null) { | ||
| 1290 | const entry: ?[]const u8 = entry: { | ||
| 1291 | if (self.base.options.entry) |entry| break :entry entry; | ||
| 1292 | if (!self.isDynLib()) break :entry "_start"; | ||
| 1293 | break :entry null; | ||
| 1294 | }; | ||
| 1295 | self.entry_index = if (entry) |name| self.globalByName(name) else null; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | const gc_sections = self.base.options.gc_sections orelse false; | ||
| 1299 | if (gc_sections) { | ||
| 1300 | try gc.gcAtoms(self); | ||
| 1301 | |||
| 1302 | // if (self.base.options.print_gc_sections) { | ||
| 1303 | // try gc.dumpPrunedAtoms(self); | ||
| 1304 | // } | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | try self.addLinkerDefinedSymbols(); | ||
| 1289 | self.claimUnresolved(); | 1308 | self.claimUnresolved(); |
| 1290 | 1309 | ||
| 1291 | // Scan and create missing synthetic entries such as GOT indirection. | 1310 | // Scan and create missing synthetic entries such as GOT indirection. |
| ... | @@ -1310,19 +1329,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1310,19 +1329,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1310 | state_log.debug("{}", .{self.dumpState()}); | 1329 | state_log.debug("{}", .{self.dumpState()}); |
| 1311 | } | 1330 | } |
| 1312 | 1331 | ||
| 1313 | // Look for entry address in objects if not set by the incremental compiler. | ||
| 1314 | if (self.entry_addr == null) { | ||
| 1315 | const entry: ?[]const u8 = entry: { | ||
| 1316 | if (self.base.options.entry) |entry| break :entry entry; | ||
| 1317 | if (!self.isDynLib()) break :entry "_start"; | ||
| 1318 | break :entry null; | ||
| 1319 | }; | ||
| 1320 | self.entry_addr = if (entry) |name| entry_addr: { | ||
| 1321 | const global_index = self.globalByName(name) orelse break :entry_addr null; | ||
| 1322 | break :entry_addr self.symbol(global_index).value; | ||
| 1323 | } else null; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | // Beyond this point, everything has been allocated a virtual address and we can resolve | 1332 | // Beyond this point, everything has been allocated a virtual address and we can resolve |
| 1327 | // the relocations, and commit objects to file. | 1333 | // the relocations, and commit objects to file. |
| 1328 | if (self.zig_module_index) |index| { | 1334 | if (self.zig_module_index) |index| { |
| ... | @@ -1538,7 +1544,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1538,7 +1544,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1538 | try self.writeAtoms(); | 1544 | try self.writeAtoms(); |
| 1539 | try self.writeSyntheticSections(); | 1545 | try self.writeSyntheticSections(); |
| 1540 | 1546 | ||
| 1541 | if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { | 1547 | if (self.entry_index == null and self.base.options.effectiveOutputMode() == .Exe) { |
| 1542 | log.debug("flushing. no_entry_point_found = true", .{}); | 1548 | log.debug("flushing. no_entry_point_found = true", .{}); |
| 1543 | self.error_flags.no_entry_point_found = true; | 1549 | self.error_flags.no_entry_point_found = true; |
| 1544 | } else { | 1550 | } else { |
| ... | @@ -2580,7 +2586,7 @@ fn writeHeader(self: *Elf) !void { | ... | @@ -2580,7 +2586,7 @@ fn writeHeader(self: *Elf) !void { |
| 2580 | mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); | 2586 | mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); |
| 2581 | index += 4; | 2587 | index += 4; |
| 2582 | 2588 | ||
| 2583 | const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; | 2589 | const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0; |
| 2584 | 2590 | ||
| 2585 | // TODO | 2591 | // TODO |
| 2586 | const phdr_table_offset = if (self.phdr_table_index) |ind| | 2592 | const phdr_table_offset = if (self.phdr_table_index) |ind| |
| ... | @@ -3199,13 +3205,7 @@ pub fn updateDeclExports( | ... | @@ -3199,13 +3205,7 @@ pub fn updateDeclExports( |
| 3199 | } | 3205 | } |
| 3200 | const stb_bits: u8 = switch (exp.opts.linkage) { | 3206 | const stb_bits: u8 = switch (exp.opts.linkage) { |
| 3201 | .Internal => elf.STB_LOCAL, | 3207 | .Internal => elf.STB_LOCAL, |
| 3202 | .Strong => blk: { | 3208 | .Strong => elf.STB_GLOBAL, |
| 3203 | const entry_name = self.base.options.entry orelse "_start"; | ||
| 3204 | if (mem.eql(u8, exp_name, entry_name)) { | ||
| 3205 | self.entry_addr = decl_sym.value; | ||
| 3206 | } | ||
| 3207 | break :blk elf.STB_GLOBAL; | ||
| 3208 | }, | ||
| 3209 | .Weak => elf.STB_WEAK, | 3209 | .Weak => elf.STB_WEAK, |
| 3210 | .LinkOnce => { | 3210 | .LinkOnce => { |
| 3211 | try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); | 3211 | try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); |
| ... | @@ -4693,6 +4693,16 @@ fn calcNumIRelativeRelocs(self: *Elf) usize { | ... | @@ -4693,6 +4693,16 @@ fn calcNumIRelativeRelocs(self: *Elf) usize { |
| 4693 | return count; | 4693 | return count; |
| 4694 | } | 4694 | } |
| 4695 | 4695 | ||
| 4696 | pub fn isCIdentifier(name: []const u8) bool { | ||
| 4697 | if (name.len == 0) return false; | ||
| 4698 | const first_c = name[0]; | ||
| 4699 | if (!std.ascii.isAlphabetic(first_c) and first_c != '_') return false; | ||
| 4700 | for (name[1..]) |c| { | ||
| 4701 | if (!std.ascii.isAlphanumeric(c) and c != '_') return false; | ||
| 4702 | } | ||
| 4703 | return true; | ||
| 4704 | } | ||
| 4705 | |||
| 4696 | pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom { | 4706 | pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom { |
| 4697 | if (atom_index == 0) return null; | 4707 | if (atom_index == 0) return null; |
| 4698 | assert(atom_index < self.atoms.items.len); | 4708 | assert(atom_index < self.atoms.items.len); |
| ... | @@ -5202,6 +5212,7 @@ const mem = std.mem; | ... | @@ -5202,6 +5212,7 @@ const mem = std.mem; |
| 5202 | 5212 | ||
| 5203 | const codegen = @import("../codegen.zig"); | 5213 | const codegen = @import("../codegen.zig"); |
| 5204 | const eh_frame = @import("Elf/eh_frame.zig"); | 5214 | const eh_frame = @import("Elf/eh_frame.zig"); |
| 5215 | const gc = @import("Elf/gc.zig"); | ||
| 5205 | const glibc = @import("../glibc.zig"); | 5216 | const glibc = @import("../glibc.zig"); |
| 5206 | const link = @import("../link.zig"); | 5217 | const link = @import("../link.zig"); |
| 5207 | const lldMain = @import("../main.zig").lldMain; | 5218 | const lldMain = @import("../main.zig").lldMain; |
src/link/Elf/gc.zig created+161| ... | @@ -0,0 +1,161 @@ | ||
| 1 | pub fn gcAtoms(elf_file: *Elf) !void { | ||
| 2 | var roots = std.ArrayList(*Atom).init(elf_file.base.allocator); | ||
| 3 | defer roots.deinit(); | ||
| 4 | try collectRoots(&roots, elf_file); | ||
| 5 | mark(roots, elf_file); | ||
| 6 | prune(elf_file); | ||
| 7 | } | ||
| 8 | |||
| 9 | fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { | ||
| 10 | if (elf_file.entry_index) |index| { | ||
| 11 | const global = elf_file.symbol(index); | ||
| 12 | try markSymbol(global, roots, elf_file); | ||
| 13 | } | ||
| 14 | |||
| 15 | for (elf_file.objects.items) |index| { | ||
| 16 | for (elf_file.file(index).?.object.globals()) |global_index| { | ||
| 17 | const global = elf_file.symbol(global_index); | ||
| 18 | if (global.file(elf_file)) |file| { | ||
| 19 | if (file.index() == index and global.flags.@"export") | ||
| 20 | try markSymbol(global, roots, elf_file); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | for (elf_file.objects.items) |index| { | ||
| 26 | const object = elf_file.file(index).?.object; | ||
| 27 | |||
| 28 | for (object.atoms.items) |atom_index| { | ||
| 29 | const atom = elf_file.atom(atom_index) orelse continue; | ||
| 30 | if (!atom.flags.alive) continue; | ||
| 31 | |||
| 32 | const shdr = atom.inputShdr(elf_file); | ||
| 33 | const name = atom.name(elf_file); | ||
| 34 | const is_gc_root = blk: { | ||
| 35 | if (shdr.sh_flags & elf.SHF_GNU_RETAIN != 0) break :blk true; | ||
| 36 | if (shdr.sh_type == elf.SHT_NOTE) break :blk true; | ||
| 37 | if (shdr.sh_type == elf.SHT_PREINIT_ARRAY) break :blk true; | ||
| 38 | if (shdr.sh_type == elf.SHT_INIT_ARRAY) break :blk true; | ||
| 39 | if (shdr.sh_type == elf.SHT_FINI_ARRAY) break :blk true; | ||
| 40 | if (mem.startsWith(u8, name, ".ctors")) break :blk true; | ||
| 41 | if (mem.startsWith(u8, name, ".dtors")) break :blk true; | ||
| 42 | if (mem.startsWith(u8, name, ".init")) break :blk true; | ||
| 43 | if (mem.startsWith(u8, name, ".fini")) break :blk true; | ||
| 44 | if (Elf.isCIdentifier(name)) break :blk true; | ||
| 45 | break :blk false; | ||
| 46 | }; | ||
| 47 | if (is_gc_root and markAtom(atom)) try roots.append(atom); | ||
| 48 | if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.flags.visited = true; | ||
| 49 | } | ||
| 50 | |||
| 51 | // Mark every atom referenced by CIE as alive. | ||
| 52 | for (object.cies.items) |cie| { | ||
| 53 | for (cie.relocs(elf_file)) |rel| { | ||
| 54 | const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); | ||
| 55 | try markSymbol(sym, roots, elf_file); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { | ||
| 62 | const atom = sym.atom(elf_file) orelse return; | ||
| 63 | if (markAtom(atom)) try roots.append(atom); | ||
| 64 | } | ||
| 65 | |||
| 66 | fn markAtom(atom: *Atom) bool { | ||
| 67 | const already_visited = atom.flags.visited; | ||
| 68 | atom.flags.visited = true; | ||
| 69 | return atom.flags.alive and !already_visited; | ||
| 70 | } | ||
| 71 | |||
| 72 | fn markLive(atom: *Atom, elf_file: *Elf) void { | ||
| 73 | if (@import("build_options").enable_logging) track_live_level.incr(); | ||
| 74 | |||
| 75 | assert(atom.flags.visited); | ||
| 76 | const object = atom.file(elf_file).?.object; | ||
| 77 | |||
| 78 | for (atom.fdes(elf_file)) |fde| { | ||
| 79 | for (fde.relocs(elf_file)[1..]) |rel| { | ||
| 80 | const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); | ||
| 81 | const target_atom = target_sym.atom(elf_file) orelse continue; | ||
| 82 | target_atom.flags.alive = true; | ||
| 83 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); | ||
| 84 | if (markAtom(target_atom)) markLive(target_atom, elf_file); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | for (atom.relocs(elf_file)) |rel| { | ||
| 89 | const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); | ||
| 90 | const target_atom = target_sym.atom(elf_file) orelse continue; | ||
| 91 | target_atom.flags.alive = true; | ||
| 92 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); | ||
| 93 | if (markAtom(target_atom)) markLive(target_atom, elf_file); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { | ||
| 98 | for (roots.items) |root| { | ||
| 99 | gc_track_live_log.debug("root atom({d})", .{root.atom_index}); | ||
| 100 | markLive(root, elf_file); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | fn prune(elf_file: *Elf) void { | ||
| 105 | for (elf_file.objects.items) |index| { | ||
| 106 | for (elf_file.file(index).?.object.atoms.items) |atom_index| { | ||
| 107 | const atom = elf_file.atom(atom_index) orelse continue; | ||
| 108 | if (atom.flags.alive and !atom.flags.visited) { | ||
| 109 | atom.flags.alive = false; | ||
| 110 | atom.markFdesDead(elf_file); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn dumpPrunedAtoms(elf_file: *Elf) !void { | ||
| 117 | const stderr = std.io.getStdErr().writer(); | ||
| 118 | for (elf_file.objects.items) |index| { | ||
| 119 | for (elf_file.file(index).?.object.atoms.items) |atom_index| { | ||
| 120 | const atom = elf_file.atom(atom_index) orelse continue; | ||
| 121 | if (!atom.flags.alive) | ||
| 122 | // TODO should we simply print to stderr? | ||
| 123 | try stderr.print("link: removing unused section '{s}' in file '{}'\n", .{ | ||
| 124 | atom.name(elf_file), | ||
| 125 | atom.file(elf_file).?.fmtPath(), | ||
| 126 | }); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | const Level = struct { | ||
| 132 | value: usize = 0, | ||
| 133 | |||
| 134 | fn incr(self: *@This()) void { | ||
| 135 | self.value += 1; | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn format( | ||
| 139 | self: *const @This(), | ||
| 140 | comptime unused_fmt_string: []const u8, | ||
| 141 | options: std.fmt.FormatOptions, | ||
| 142 | writer: anytype, | ||
| 143 | ) !void { | ||
| 144 | _ = unused_fmt_string; | ||
| 145 | _ = options; | ||
| 146 | try writer.writeByteNTimes(' ', self.value); | ||
| 147 | } | ||
| 148 | }; | ||
| 149 | |||
| 150 | var track_live_level: Level = .{}; | ||
| 151 | |||
| 152 | const std = @import("std"); | ||
| 153 | const assert = std.debug.assert; | ||
| 154 | const elf = std.elf; | ||
| 155 | const gc_track_live_log = std.log.scoped(.gc_track_live); | ||
| 156 | const mem = std.mem; | ||
| 157 | |||
| 158 | const Allocator = mem.Allocator; | ||
| 159 | const Atom = @import("Atom.zig"); | ||
| 160 | const Elf = @import("../Elf.zig"); | ||
| 161 | const Symbol = @import("Symbol.zig"); | ||
test/link/elf.zig+1| ... | @@ -60,6 +60,7 @@ fn testGcSections(b: *Build, opts: Options) *Step { | ... | @@ -60,6 +60,7 @@ fn testGcSections(b: *Build, opts: Options) *Step { |
| 60 | \\} | 60 | \\} |
| 61 | ); | 61 | ); |
| 62 | obj.link_function_sections = true; | 62 | obj.link_function_sections = true; |
| 63 | obj.link_data_sections = true; | ||
| 63 | obj.is_linking_libc = true; | 64 | obj.is_linking_libc = true; |
| 64 | obj.is_linking_libcpp = true; | 65 | obj.is_linking_libcpp = true; |
| 65 | 66 |