mirror of
https://github.com/ziglang/zig.git
synced 2025-12-24 07:03:11 +00:00
elf: implement --gc-sections for non-LLVM Zig source
This commit is contained in:
parent
25c53f08a6
commit
b8c8565e93
@ -99,6 +99,21 @@ pub const File = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn cies(file: File) []const Cie {
|
||||
return switch (file) {
|
||||
.zig_object => &[0]Cie{},
|
||||
.object => |x| x.cies.items,
|
||||
inline else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index {
|
||||
return switch (file) {
|
||||
.zig_object => |x| x.symbol(ind),
|
||||
inline else => |x| x.symbols.items[ind],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn locals(file: File) []const Symbol.Index {
|
||||
return switch (file) {
|
||||
.linker_defined, .shared_object => &[0]Symbol.Index{},
|
||||
@ -196,6 +211,7 @@ const elf = std.elf;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Cie = @import("eh_frame.zig").Cie;
|
||||
const Elf = @import("../Elf.zig");
|
||||
const LinkerDefined = @import("LinkerDefined.zig");
|
||||
const Object = @import("Object.zig");
|
||||
|
||||
@ -1,19 +1,27 @@
|
||||
pub fn gcAtoms(elf_file: *Elf) !void {
|
||||
var roots = std.ArrayList(*Atom).init(elf_file.base.allocator);
|
||||
const gpa = elf_file.base.allocator;
|
||||
const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null);
|
||||
var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files);
|
||||
defer files.deinit();
|
||||
if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index);
|
||||
for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
|
||||
|
||||
var roots = std.ArrayList(*Atom).init(gpa);
|
||||
defer roots.deinit();
|
||||
try collectRoots(&roots, elf_file);
|
||||
try collectRoots(&roots, files.items, elf_file);
|
||||
|
||||
mark(roots, elf_file);
|
||||
prune(elf_file);
|
||||
prune(files.items, elf_file);
|
||||
}
|
||||
|
||||
fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
|
||||
fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void {
|
||||
if (elf_file.entry_index) |index| {
|
||||
const global = elf_file.symbol(index);
|
||||
try markSymbol(global, roots, elf_file);
|
||||
}
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
for (elf_file.file(index).?.object.globals()) |global_index| {
|
||||
for (files) |index| {
|
||||
for (elf_file.file(index).?.globals()) |global_index| {
|
||||
const global = elf_file.symbol(global_index);
|
||||
if (global.file(elf_file)) |file| {
|
||||
if (file.index() == index and global.flags.@"export")
|
||||
@ -22,10 +30,10 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
for (files) |index| {
|
||||
const file = elf_file.file(index).?;
|
||||
|
||||
for (object.atoms.items) |atom_index| {
|
||||
for (file.atoms()) |atom_index| {
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.flags.alive) continue;
|
||||
|
||||
@ -49,9 +57,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
|
||||
}
|
||||
|
||||
// Mark every atom referenced by CIE as alive.
|
||||
for (object.cies.items) |cie| {
|
||||
for (file.cies()) |cie| {
|
||||
for (cie.relocs(elf_file)) |rel| {
|
||||
const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
|
||||
const sym = elf_file.symbol(file.symbol(rel.r_sym()));
|
||||
try markSymbol(sym, roots, elf_file);
|
||||
}
|
||||
}
|
||||
@ -73,11 +81,11 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
|
||||
if (@import("build_options").enable_logging) track_live_level.incr();
|
||||
|
||||
assert(atom.flags.visited);
|
||||
const object = atom.file(elf_file).?.object;
|
||||
const file = atom.file(elf_file).?;
|
||||
|
||||
for (atom.fdes(elf_file)) |fde| {
|
||||
for (fde.relocs(elf_file)[1..]) |rel| {
|
||||
const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
|
||||
const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
|
||||
const target_atom = target_sym.atom(elf_file) orelse continue;
|
||||
target_atom.flags.alive = true;
|
||||
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 {
|
||||
}
|
||||
|
||||
for (atom.relocs(elf_file)) |rel| {
|
||||
const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
|
||||
const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
|
||||
const target_atom = target_sym.atom(elf_file) orelse continue;
|
||||
target_atom.flags.alive = true;
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
fn prune(elf_file: *Elf) void {
|
||||
for (elf_file.objects.items) |index| {
|
||||
for (elf_file.file(index).?.object.atoms.items) |atom_index| {
|
||||
fn prune(files: []const File.Index, elf_file: *Elf) void {
|
||||
for (files) |index| {
|
||||
for (elf_file.file(index).?.atoms()) |atom_index| {
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (atom.flags.alive and !atom.flags.visited) {
|
||||
atom.flags.alive = false;
|
||||
@ -158,4 +166,5 @@ const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const File = @import("file.zig").File;
|
||||
const Symbol = @import("Symbol.zig");
|
||||
|
||||
@ -6,9 +6,13 @@ pub fn build(b: *Build) void {
|
||||
const elf_step = b.step("test-elf", "Run ELF tests");
|
||||
b.default_step = elf_step;
|
||||
|
||||
const musl_target = CrossTarget{
|
||||
const default_target = CrossTarget{
|
||||
.cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
|
||||
.os_tag = .linux,
|
||||
};
|
||||
const musl_target = CrossTarget{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .linux,
|
||||
.abi = .musl,
|
||||
};
|
||||
const glibc_target = CrossTarget{
|
||||
@ -18,7 +22,8 @@ pub fn build(b: *Build) void {
|
||||
};
|
||||
|
||||
// Exercise linker with self-hosted backend (no LLVM)
|
||||
elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));
|
||||
elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
|
||||
elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
|
||||
elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target }));
|
||||
elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target }));
|
||||
|
||||
@ -876,6 +881,110 @@ fn testGcSections(b: *Build, opts: Options) *Step {
|
||||
return test_step;
|
||||
}
|
||||
|
||||
fn testGcSectionsZig(b: *Build, opts: Options) *Step {
|
||||
const test_step = addTestStep(b, "gc-sections-zig", opts);
|
||||
|
||||
const obj = addObject(b, "obj", .{
|
||||
.target = opts.target,
|
||||
.use_llvm = true,
|
||||
.use_lld = true,
|
||||
});
|
||||
addCSourceBytes(obj,
|
||||
\\int live_var1 = 1;
|
||||
\\int live_var2 = 2;
|
||||
\\int dead_var1 = 3;
|
||||
\\int dead_var2 = 4;
|
||||
\\void live_fn1() {}
|
||||
\\void live_fn2() { live_fn1(); }
|
||||
\\void dead_fn1() {}
|
||||
\\void dead_fn2() { dead_fn1(); }
|
||||
, &.{});
|
||||
obj.link_function_sections = true;
|
||||
obj.link_data_sections = true;
|
||||
|
||||
{
|
||||
const exe = addExecutable(b, "test1", opts);
|
||||
addZigSourceBytes(exe,
|
||||
\\const std = @import("std");
|
||||
\\extern var live_var1: i32;
|
||||
\\extern var live_var2: i32;
|
||||
\\extern fn live_fn2() void;
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = std.io.getStdOut();
|
||||
\\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
|
||||
\\ live_fn2();
|
||||
\\}
|
||||
);
|
||||
exe.addObject(obj);
|
||||
exe.link_gc_sections = false;
|
||||
|
||||
const run = addRunArtifact(exe);
|
||||
run.expectStdOutEqual("1 2\n");
|
||||
test_step.dependOn(&run.step);
|
||||
|
||||
const check = exe.checkObject();
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_var1");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_var2");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("dead_var1");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("dead_var2");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_fn1");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_fn2");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("dead_fn1");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("dead_fn2");
|
||||
test_step.dependOn(&check.step);
|
||||
}
|
||||
|
||||
{
|
||||
const exe = addExecutable(b, "test2", opts);
|
||||
addZigSourceBytes(exe,
|
||||
\\const std = @import("std");
|
||||
\\extern var live_var1: i32;
|
||||
\\extern var live_var2: i32;
|
||||
\\extern fn live_fn2() void;
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = std.io.getStdOut();
|
||||
\\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
|
||||
\\ live_fn2();
|
||||
\\}
|
||||
);
|
||||
exe.addObject(obj);
|
||||
exe.link_gc_sections = true;
|
||||
|
||||
const run = addRunArtifact(exe);
|
||||
run.expectStdOutEqual("1 2\n");
|
||||
test_step.dependOn(&run.step);
|
||||
|
||||
const check = exe.checkObject();
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_var1");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_var2");
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("dead_var1");
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("dead_var2");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_fn1");
|
||||
check.checkInSymtab();
|
||||
check.checkContains("live_fn2");
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("dead_fn1");
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("dead_fn2");
|
||||
test_step.dependOn(&check.step);
|
||||
}
|
||||
|
||||
return test_step;
|
||||
}
|
||||
|
||||
fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
|
||||
const test_step = addTestStep(b, "hidden-weak-undef", opts);
|
||||
|
||||
@ -3114,6 +3223,7 @@ const Options = struct {
|
||||
target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
|
||||
optimize: std.builtin.OptimizeMode = .Debug,
|
||||
use_llvm: bool = true,
|
||||
use_lld: bool = false,
|
||||
};
|
||||
|
||||
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 {
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = false,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3144,7 +3254,7 @@ fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = false,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3164,7 +3274,7 @@ fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = false,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user