| author | |
| committer | |
| log | b8c8565e93d3625e5eb46a3f64a461f86b8d8eb4 |
| tree | 1e40889825b691397d0aed0e7349044b26fa41dc |
| parent | 25c53f08a6add493043a407ce15bc727dc33356d |
3 files changed, 157 insertions(+), 22 deletions(-)
src/link/Elf/file.zig+16| ... | @@ -99,6 +99,21 @@ pub const File = union(enum) { | ... | @@ -99,6 +99,21 @@ pub const File = union(enum) { |
| 99 | }; | 99 | }; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | pub fn cies(file: File) []const Cie { | ||
| 103 | return switch (file) { | ||
| 104 | .zig_object => &[0]Cie{}, | ||
| 105 | .object => |x| x.cies.items, | ||
| 106 | inline else => unreachable, | ||
| 107 | }; | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index { | ||
| 111 | return switch (file) { | ||
| 112 | .zig_object => |x| x.symbol(ind), | ||
| 113 | inline else => |x| x.symbols.items[ind], | ||
| 114 | }; | ||
| 115 | } | ||
| 116 | |||
| 102 | pub fn locals(file: File) []const Symbol.Index { | 117 | pub fn locals(file: File) []const Symbol.Index { |
| 103 | return switch (file) { | 118 | return switch (file) { |
| 104 | .linker_defined, .shared_object => &[0]Symbol.Index{}, | 119 | .linker_defined, .shared_object => &[0]Symbol.Index{}, |
| ... | @@ -196,6 +211,7 @@ const elf = std.elf; | ... | @@ -196,6 +211,7 @@ const elf = std.elf; |
| 196 | 211 | ||
| 197 | const Allocator = std.mem.Allocator; | 212 | const Allocator = std.mem.Allocator; |
| 198 | const Atom = @import("Atom.zig"); | 213 | const Atom = @import("Atom.zig"); |
| 214 | const Cie = @import("eh_frame.zig").Cie; | ||
| 199 | const Elf = @import("../Elf.zig"); | 215 | const Elf = @import("../Elf.zig"); |
| 200 | const LinkerDefined = @import("LinkerDefined.zig"); | 216 | const LinkerDefined = @import("LinkerDefined.zig"); |
| 201 | const Object = @import("Object.zig"); | 217 | const Object = @import("Object.zig"); |
src/link/Elf/gc.zig+26-17| ... | @@ -1,19 +1,27 @@ | ... | @@ -1,19 +1,27 @@ |
| 1 | pub fn gcAtoms(elf_file: *Elf) !void { | 1 | pub fn gcAtoms(elf_file: *Elf) !void { |
| 2 | var roots = std.ArrayList(*Atom).init(elf_file.base.allocator); | 2 | const gpa = elf_file.base.allocator; |
| 3 | const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null); | ||
| 4 | var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files); | ||
| 5 | defer files.deinit(); | ||
| 6 | if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index); | ||
| 7 | for (elf_file.objects.items) |index| files.appendAssumeCapacity(index); | ||
| 8 | |||
| 9 | var roots = std.ArrayList(*Atom).init(gpa); | ||
| 3 | defer roots.deinit(); | 10 | defer roots.deinit(); |
| 4 | try collectRoots(&roots, elf_file); | 11 | try collectRoots(&roots, files.items, elf_file); |
| 12 | |||
| 5 | mark(roots, elf_file); | 13 | mark(roots, elf_file); |
| 6 | prune(elf_file); | 14 | prune(files.items, elf_file); |
| 7 | } | 15 | } |
| 8 | 16 | ||
| 9 | fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { | 17 | fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void { |
| 10 | if (elf_file.entry_index) |index| { | 18 | if (elf_file.entry_index) |index| { |
| 11 | const global = elf_file.symbol(index); | 19 | const global = elf_file.symbol(index); |
| 12 | try markSymbol(global, roots, elf_file); | 20 | try markSymbol(global, roots, elf_file); |
| 13 | } | 21 | } |
| 14 | 22 | ||
| 15 | for (elf_file.objects.items) |index| { | 23 | for (files) |index| { |
| 16 | for (elf_file.file(index).?.object.globals()) |global_index| { | 24 | for (elf_file.file(index).?.globals()) |global_index| { |
| 17 | const global = elf_file.symbol(global_index); | 25 | const global = elf_file.symbol(global_index); |
| 18 | if (global.file(elf_file)) |file| { | 26 | if (global.file(elf_file)) |file| { |
| 19 | if (file.index() == index and global.flags.@"export") | 27 | if (file.index() == index and global.flags.@"export") |
| ... | @@ -22,10 +30,10 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { | ... | @@ -22,10 +30,10 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { |
| 22 | } | 30 | } |
| 23 | } | 31 | } |
| 24 | 32 | ||
| 25 | for (elf_file.objects.items) |index| { | 33 | for (files) |index| { |
| 26 | const object = elf_file.file(index).?.object; | 34 | const file = elf_file.file(index).?; |
| 27 | 35 | ||
| 28 | for (object.atoms.items) |atom_index| { | 36 | for (file.atoms()) |atom_index| { |
| 29 | const atom = elf_file.atom(atom_index) orelse continue; | 37 | const atom = elf_file.atom(atom_index) orelse continue; |
| 30 | if (!atom.flags.alive) continue; | 38 | if (!atom.flags.alive) continue; |
| 31 | 39 | ||
| ... | @@ -49,9 +57,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { | ... | @@ -49,9 +57,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { |
| 49 | } | 57 | } |
| 50 | 58 | ||
| 51 | // Mark every atom referenced by CIE as alive. | 59 | // Mark every atom referenced by CIE as alive. |
| 52 | for (object.cies.items) |cie| { | 60 | for (file.cies()) |cie| { |
| 53 | for (cie.relocs(elf_file)) |rel| { | 61 | for (cie.relocs(elf_file)) |rel| { |
| 54 | const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); | 62 | const sym = elf_file.symbol(file.symbol(rel.r_sym())); |
| 55 | try markSymbol(sym, roots, elf_file); | 63 | try markSymbol(sym, roots, elf_file); |
| 56 | } | 64 | } |
| 57 | } | 65 | } |
| ... | @@ -73,11 +81,11 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { | ... | @@ -73,11 +81,11 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { |
| 73 | if (@import("build_options").enable_logging) track_live_level.incr(); | 81 | if (@import("build_options").enable_logging) track_live_level.incr(); |
| 74 | 82 | ||
| 75 | assert(atom.flags.visited); | 83 | assert(atom.flags.visited); |
| 76 | const object = atom.file(elf_file).?.object; | 84 | const file = atom.file(elf_file).?; |
| 77 | 85 | ||
| 78 | for (atom.fdes(elf_file)) |fde| { | 86 | for (atom.fdes(elf_file)) |fde| { |
| 79 | for (fde.relocs(elf_file)[1..]) |rel| { | 87 | for (fde.relocs(elf_file)[1..]) |rel| { |
| 80 | const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); | 88 | const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); |
| 81 | const target_atom = target_sym.atom(elf_file) orelse continue; | 89 | const target_atom = target_sym.atom(elf_file) orelse continue; |
| 82 | target_atom.flags.alive = true; | 90 | target_atom.flags.alive = true; |
| 83 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); | 91 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); |
| ... | @@ -86,7 +94,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { | ... | @@ -86,7 +94,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { |
| 86 | } | 94 | } |
| 87 | 95 | ||
| 88 | for (atom.relocs(elf_file)) |rel| { | 96 | for (atom.relocs(elf_file)) |rel| { |
| 89 | const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); | 97 | const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); |
| 90 | const target_atom = target_sym.atom(elf_file) orelse continue; | 98 | const target_atom = target_sym.atom(elf_file) orelse continue; |
| 91 | target_atom.flags.alive = true; | 99 | target_atom.flags.alive = true; |
| 92 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); | 100 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); |
| ... | @@ -101,9 +109,9 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { | ... | @@ -101,9 +109,9 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { |
| 101 | } | 109 | } |
| 102 | } | 110 | } |
| 103 | 111 | ||
| 104 | fn prune(elf_file: *Elf) void { | 112 | fn prune(files: []const File.Index, elf_file: *Elf) void { |
| 105 | for (elf_file.objects.items) |index| { | 113 | for (files) |index| { |
| 106 | for (elf_file.file(index).?.object.atoms.items) |atom_index| { | 114 | for (elf_file.file(index).?.atoms()) |atom_index| { |
| 107 | const atom = elf_file.atom(atom_index) orelse continue; | 115 | const atom = elf_file.atom(atom_index) orelse continue; |
| 108 | if (atom.flags.alive and !atom.flags.visited) { | 116 | if (atom.flags.alive and !atom.flags.visited) { |
| 109 | atom.flags.alive = false; | 117 | atom.flags.alive = false; |
| ... | @@ -158,4 +166,5 @@ const mem = std.mem; | ... | @@ -158,4 +166,5 @@ const mem = std.mem; |
| 158 | const Allocator = mem.Allocator; | 166 | const Allocator = mem.Allocator; |
| 159 | const Atom = @import("Atom.zig"); | 167 | const Atom = @import("Atom.zig"); |
| 160 | const Elf = @import("../Elf.zig"); | 168 | const Elf = @import("../Elf.zig"); |
| 169 | const File = @import("file.zig").File; | ||
| 161 | const Symbol = @import("Symbol.zig"); | 170 | const Symbol = @import("Symbol.zig"); |
test/link/elf.zig+115-5| ... | @@ -6,9 +6,13 @@ pub fn build(b: *Build) void { | ... | @@ -6,9 +6,13 @@ pub fn build(b: *Build) void { |
| 6 | const elf_step = b.step("test-elf", "Run ELF tests"); | 6 | const elf_step = b.step("test-elf", "Run ELF tests"); |
| 7 | b.default_step = elf_step; | 7 | b.default_step = elf_step; |
| 8 | 8 | ||
| 9 | const musl_target = CrossTarget{ | 9 | const default_target = CrossTarget{ |
| 10 | .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs | 10 | .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs |
| 11 | .os_tag = .linux, | 11 | .os_tag = .linux, |
| 12 | }; | ||
| 13 | const musl_target = CrossTarget{ | ||
| 14 | .cpu_arch = .x86_64, | ||
| 15 | .os_tag = .linux, | ||
| 12 | .abi = .musl, | 16 | .abi = .musl, |
| 13 | }; | 17 | }; |
| 14 | const glibc_target = CrossTarget{ | 18 | const glibc_target = CrossTarget{ |
| ... | @@ -18,7 +22,8 @@ pub fn build(b: *Build) void { | ... | @@ -18,7 +22,8 @@ pub fn build(b: *Build) void { |
| 18 | }; | 22 | }; |
| 19 | 23 | ||
| 20 | // Exercise linker with self-hosted backend (no LLVM) | 24 | // Exercise linker with self-hosted backend (no LLVM) |
| 21 | elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false })); | 25 | elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); |
| 26 | elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target })); | ||
| 22 | elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target })); | 27 | elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target })); |
| 23 | elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target })); | 28 | elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target })); |
| 24 | 29 | ||
| ... | @@ -876,6 +881,110 @@ fn testGcSections(b: *Build, opts: Options) *Step { | ... | @@ -876,6 +881,110 @@ fn testGcSections(b: *Build, opts: Options) *Step { |
| 876 | return test_step; | 881 | return test_step; |
| 877 | } | 882 | } |
| 878 | 883 | ||
| 884 | fn testGcSectionsZig(b: *Build, opts: Options) *Step { | ||
| 885 | const test_step = addTestStep(b, "gc-sections-zig", opts); | ||
| 886 | |||
| 887 | const obj = addObject(b, "obj", .{ | ||
| 888 | .target = opts.target, | ||
| 889 | .use_llvm = true, | ||
| 890 | .use_lld = true, | ||
| 891 | }); | ||
| 892 | addCSourceBytes(obj, | ||
| 893 | \\int live_var1 = 1; | ||
| 894 | \\int live_var2 = 2; | ||
| 895 | \\int dead_var1 = 3; | ||
| 896 | \\int dead_var2 = 4; | ||
| 897 | \\void live_fn1() {} | ||
| 898 | \\void live_fn2() { live_fn1(); } | ||
| 899 | \\void dead_fn1() {} | ||
| 900 | \\void dead_fn2() { dead_fn1(); } | ||
| 901 | , &.{}); | ||
| 902 | obj.link_function_sections = true; | ||
| 903 | obj.link_data_sections = true; | ||
| 904 | |||
| 905 | { | ||
| 906 | const exe = addExecutable(b, "test1", opts); | ||
| 907 | addZigSourceBytes(exe, | ||
| 908 | \\const std = @import("std"); | ||
| 909 | \\extern var live_var1: i32; | ||
| 910 | \\extern var live_var2: i32; | ||
| 911 | \\extern fn live_fn2() void; | ||
| 912 | \\pub fn main() void { | ||
| 913 | \\ const stdout = std.io.getStdOut(); | ||
| 914 | \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; | ||
| 915 | \\ live_fn2(); | ||
| 916 | \\} | ||
| 917 | ); | ||
| 918 | exe.addObject(obj); | ||
| 919 | exe.link_gc_sections = false; | ||
| 920 | |||
| 921 | const run = addRunArtifact(exe); | ||
| 922 | run.expectStdOutEqual("1 2\n"); | ||
| 923 | test_step.dependOn(&run.step); | ||
| 924 | |||
| 925 | const check = exe.checkObject(); | ||
| 926 | check.checkInSymtab(); | ||
| 927 | check.checkContains("live_var1"); | ||
| 928 | check.checkInSymtab(); | ||
| 929 | check.checkContains("live_var2"); | ||
| 930 | check.checkInSymtab(); | ||
| 931 | check.checkContains("dead_var1"); | ||
| 932 | check.checkInSymtab(); | ||
| 933 | check.checkContains("dead_var2"); | ||
| 934 | check.checkInSymtab(); | ||
| 935 | check.checkContains("live_fn1"); | ||
| 936 | check.checkInSymtab(); | ||
| 937 | check.checkContains("live_fn2"); | ||
| 938 | check.checkInSymtab(); | ||
| 939 | check.checkContains("dead_fn1"); | ||
| 940 | check.checkInSymtab(); | ||
| 941 | check.checkContains("dead_fn2"); | ||
| 942 | test_step.dependOn(&check.step); | ||
| 943 | } | ||
| 944 | |||
| 945 | { | ||
| 946 | const exe = addExecutable(b, "test2", opts); | ||
| 947 | addZigSourceBytes(exe, | ||
| 948 | \\const std = @import("std"); | ||
| 949 | \\extern var live_var1: i32; | ||
| 950 | \\extern var live_var2: i32; | ||
| 951 | \\extern fn live_fn2() void; | ||
| 952 | \\pub fn main() void { | ||
| 953 | \\ const stdout = std.io.getStdOut(); | ||
| 954 | \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; | ||
| 955 | \\ live_fn2(); | ||
| 956 | \\} | ||
| 957 | ); | ||
| 958 | exe.addObject(obj); | ||
| 959 | exe.link_gc_sections = true; | ||
| 960 | |||
| 961 | const run = addRunArtifact(exe); | ||
| 962 | run.expectStdOutEqual("1 2\n"); | ||
| 963 | test_step.dependOn(&run.step); | ||
| 964 | |||
| 965 | const check = exe.checkObject(); | ||
| 966 | check.checkInSymtab(); | ||
| 967 | check.checkContains("live_var1"); | ||
| 968 | check.checkInSymtab(); | ||
| 969 | check.checkContains("live_var2"); | ||
| 970 | check.checkInSymtab(); | ||
| 971 | check.checkNotPresent("dead_var1"); | ||
| 972 | check.checkInSymtab(); | ||
| 973 | check.checkNotPresent("dead_var2"); | ||
| 974 | check.checkInSymtab(); | ||
| 975 | check.checkContains("live_fn1"); | ||
| 976 | check.checkInSymtab(); | ||
| 977 | check.checkContains("live_fn2"); | ||
| 978 | check.checkInSymtab(); | ||
| 979 | check.checkNotPresent("dead_fn1"); | ||
| 980 | check.checkInSymtab(); | ||
| 981 | check.checkNotPresent("dead_fn2"); | ||
| 982 | test_step.dependOn(&check.step); | ||
| 983 | } | ||
| 984 | |||
| 985 | return test_step; | ||
| 986 | } | ||
| 987 | |||
| 879 | fn testHiddenWeakUndef(b: *Build, opts: Options) *Step { | 988 | fn testHiddenWeakUndef(b: *Build, opts: Options) *Step { |
| 880 | const test_step = addTestStep(b, "hidden-weak-undef", opts); | 989 | const test_step = addTestStep(b, "hidden-weak-undef", opts); |
| 881 | 990 | ||
| ... | @@ -3114,6 +3223,7 @@ const Options = struct { | ... | @@ -3114,6 +3223,7 @@ const Options = struct { |
| 3114 | target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux }, | 3223 | target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux }, |
| 3115 | optimize: std.builtin.OptimizeMode = .Debug, | 3224 | optimize: std.builtin.OptimizeMode = .Debug, |
| 3116 | use_llvm: bool = true, | 3225 | use_llvm: bool = true, |
| 3226 | use_lld: bool = false, | ||
| 3117 | }; | 3227 | }; |
| 3118 | 3228 | ||
| 3119 | fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { | 3229 | fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { |
| ... | @@ -3134,7 +3244,7 @@ fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile { | ... | @@ -3134,7 +3244,7 @@ fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile { |
| 3134 | .target = opts.target, | 3244 | .target = opts.target, |
| 3135 | .optimize = opts.optimize, | 3245 | .optimize = opts.optimize, |
| 3136 | .use_llvm = opts.use_llvm, | 3246 | .use_llvm = opts.use_llvm, |
| 3137 | .use_lld = false, | 3247 | .use_lld = opts.use_lld, |
| 3138 | }); | 3248 | }); |
| 3139 | } | 3249 | } |
| 3140 | 3250 | ||
| ... | @@ -3144,7 +3254,7 @@ fn addObject(b: *Build, name: []const u8, opts: Options) *Compile { | ... | @@ -3144,7 +3254,7 @@ fn addObject(b: *Build, name: []const u8, opts: Options) *Compile { |
| 3144 | .target = opts.target, | 3254 | .target = opts.target, |
| 3145 | .optimize = opts.optimize, | 3255 | .optimize = opts.optimize, |
| 3146 | .use_llvm = opts.use_llvm, | 3256 | .use_llvm = opts.use_llvm, |
| 3147 | .use_lld = false, | 3257 | .use_lld = opts.use_lld, |
| 3148 | }); | 3258 | }); |
| 3149 | } | 3259 | } |
| 3150 | 3260 | ||
| ... | @@ -3164,7 +3274,7 @@ fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile { | ... | @@ -3164,7 +3274,7 @@ fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile { |
| 3164 | .target = opts.target, | 3274 | .target = opts.target, |
| 3165 | .optimize = opts.optimize, | 3275 | .optimize = opts.optimize, |
| 3166 | .use_llvm = opts.use_llvm, | 3276 | .use_llvm = opts.use_llvm, |
| 3167 | .use_lld = false, | 3277 | .use_lld = opts.use_lld, |
| 3168 | }); | 3278 | }); |
| 3169 | } | 3279 | } |
| 3170 | 3280 |