Skip to content

Commit f87b443

Browse files
committed
link.MachO: Add support for the -x flag (discard local symbols).
This can also be extended to ELF later as it means roughly the same thing there. This addresses the main issue in #21721 but as I don't have a macOS machine to do further testing on, I can't confirm whether zig cc is able to pass the entire cgo test suite after this commit. It can, however, cross-compile a basic program that uses cgo to x86_64-macos-none which previously failed due to lack of -x support. Unlike previously, the resulting symbol table does not contain local symbols (such as C static functions). I believe this satisfies the related donor bounty: https://ziglang.org/news/second-donor-bounty
1 parent a7467b9 commit f87b443

File tree

10 files changed

+83
-3
lines changed

10 files changed

+83
-3
lines changed

lib/std/Build/Step/Compile.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ dead_strip_dylibs: bool = false,
160160
/// (Darwin) Force load all members of static archives that implement an Objective-C class or category
161161
force_load_objc: bool = false,
162162

163+
/// Whether local symbols should be discarded from the symbol table.
164+
discard_local_symbols: bool = false,
165+
163166
/// Position Independent Executable
164167
pie: ?bool = null,
165168

@@ -1555,6 +1558,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
15551558
if (compile.force_load_objc) {
15561559
try zig_args.append("-ObjC");
15571560
}
1561+
if (compile.discard_local_symbols) {
1562+
try zig_args.append("--discard-all");
1563+
}
15581564

15591565
try addFlag(&zig_args, "compiler-rt", compile.bundle_compiler_rt);
15601566
try addFlag(&zig_args, "dll-export-fns", compile.dll_export_fns);

src/Compilation.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,8 @@ pub const CreateOptions = struct {
11601160
dead_strip_dylibs: bool = false,
11611161
/// (Darwin) Force load all members of static archives that implement an Objective-C class or category
11621162
force_load_objc: bool = false,
1163+
/// Whether local symbols should be discarded from the symbol table.
1164+
discard_local_symbols: bool = false,
11631165
libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default,
11641166
/// (Windows) PDB source path prefix to instruct the linker how to resolve relative
11651167
/// paths when consolidating CodeView streams into a single PDB file.
@@ -1585,6 +1587,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15851587
.headerpad_max_install_names = options.headerpad_max_install_names,
15861588
.dead_strip_dylibs = options.dead_strip_dylibs,
15871589
.force_load_objc = options.force_load_objc,
1590+
.discard_local_symbols = options.discard_local_symbols,
15881591
.pdb_source_path = options.pdb_source_path,
15891592
.pdb_out_path = options.pdb_out_path,
15901593
.entry_addr = null, // CLI does not expose this option (yet?)
@@ -2665,6 +2668,7 @@ fn addNonIncrementalStuffToCacheManifest(
26652668
man.hash.add(opts.headerpad_max_install_names);
26662669
man.hash.add(opts.dead_strip_dylibs);
26672670
man.hash.add(opts.force_load_objc);
2671+
man.hash.add(opts.discard_local_symbols);
26682672

26692673
// COFF specific stuff
26702674
man.hash.addOptional(opts.subsystem);

src/link.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ pub const File = struct {
491491
/// Force load all members of static archives that implement an
492492
/// Objective-C class or category
493493
force_load_objc: bool,
494+
/// Whether local symbols should be discarded from the symbol table.
495+
discard_local_symbols: bool,
494496

495497
/// Windows-specific linker flags:
496498
/// PDB source path prefix to instruct the linker how to resolve relative

src/link/MachO.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ no_implicit_dylibs: bool = false,
139139
/// Whether the linker should parse and always force load objects containing ObjC in archives.
140140
// TODO: in Zig we currently take -ObjC as always on
141141
force_load_objc: bool = true,
142+
/// Whether local symbols should be discarded from the symbol table.
143+
discard_local_symbols: bool = false,
142144

143145
/// Hot-code swapping state.
144146
hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
@@ -221,6 +223,7 @@ pub fn createEmpty(
221223
.lib_directories = options.lib_directories,
222224
.framework_dirs = options.framework_dirs,
223225
.force_load_objc = options.force_load_objc,
226+
.discard_local_symbols = options.discard_local_symbols,
224227
};
225228
if (use_llvm and comp.config.have_zcu) {
226229
self.llvm_object = try LlvmObject.create(arena, comp);
@@ -720,6 +723,10 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
720723
try argv.append("-ObjC");
721724
}
722725

726+
if (self.discard_local_symbols) {
727+
try argv.append("-x");
728+
}
729+
723730
if (self.entry_name) |entry_name| {
724731
try argv.appendSlice(&.{ "-e", entry_name });
725732
}

src/link/MachO/InternalObject.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void {
583583
const file = ref.getFile(macho_file) orelse continue;
584584
if (file.getIndex() != self.index) continue;
585585
if (sym.getName(macho_file).len == 0) continue;
586+
if (macho_file.discard_local_symbols and sym.isLocal()) continue;
586587
sym.flags.output_symtab = true;
587588
if (sym.isLocal()) {
588589
sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);

src/link/MachO/Object.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
17221722
if (file.getIndex() != self.index) continue;
17231723
if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
17241724
if (sym.isSymbolStab(macho_file)) continue;
1725+
if (macho_file.discard_local_symbols and sym.isLocal()) continue;
17251726
const name = sym.getName(macho_file);
17261727
if (name.len == 0) continue;
17271728
// TODO in -r mode, we actually want to merge symbol names and emit only one

src/link/MachO/ZigObject.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
500500
const file = ref.getFile(macho_file) orelse continue;
501501
if (file.getIndex() != self.index) continue;
502502
if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
503+
if (macho_file.discard_local_symbols and sym.isLocal()) continue;
503504
const name = sym.getName(macho_file);
504505
assert(name.len > 0);
505506
sym.flags.output_symtab = true;

src/link/MachO/synthetic.zig

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,17 +532,23 @@ pub const Indsymtab = struct {
532532

533533
for (macho_file.stubs.symbols.items) |ref| {
534534
const sym = ref.getSymbol(macho_file).?;
535-
try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
535+
if (sym.getOutputSymtabIndex(macho_file)) |idx| {
536+
try writer.writeInt(u32, idx, .little);
537+
}
536538
}
537539

538540
for (macho_file.got.symbols.items) |ref| {
539541
const sym = ref.getSymbol(macho_file).?;
540-
try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
542+
if (sym.getOutputSymtabIndex(macho_file)) |idx| {
543+
try writer.writeInt(u32, idx, .little);
544+
}
541545
}
542546

543547
for (macho_file.stubs.symbols.items) |ref| {
544548
const sym = ref.getSymbol(macho_file).?;
545-
try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
549+
if (sym.getOutputSymtabIndex(macho_file)) |idx| {
550+
try writer.writeInt(u32, idx, .little);
551+
}
546552
}
547553
}
548554
};

src/main.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,7 @@ fn buildOutputType(
918918
var headerpad_max_install_names: bool = false;
919919
var dead_strip_dylibs: bool = false;
920920
var force_load_objc: bool = false;
921+
var discard_local_symbols: bool = false;
921922
var contains_res_file: bool = false;
922923
var reference_trace: ?u32 = null;
923924
var pdb_out_path: ?[]const u8 = null;
@@ -1151,6 +1152,8 @@ fn buildOutputType(
11511152
entry = .{ .named = arg["-fentry=".len..] };
11521153
} else if (mem.eql(u8, arg, "--force_undefined")) {
11531154
try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});
1155+
} else if (mem.eql(u8, arg, "--discard-all")) {
1156+
discard_local_symbols = true;
11541157
} else if (mem.eql(u8, arg, "--stack")) {
11551158
stack_size = parseStackSize(args_iter.nextOrFatal());
11561159
} else if (mem.eql(u8, arg, "--image-base")) {
@@ -2510,6 +2513,8 @@ fn buildOutputType(
25102513
entry = .{ .named = linker_args_it.nextOrFatal() };
25112514
} else if (mem.eql(u8, arg, "-u")) {
25122515
try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {});
2516+
} else if (mem.eql(u8, arg, "-x") or mem.eql(u8, arg, "--discard-all")) {
2517+
discard_local_symbols = true;
25132518
} else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
25142519
stack_size = parseStackSize(linker_args_it.nextOrFatal());
25152520
} else if (mem.eql(u8, arg, "--image-base")) {
@@ -3579,6 +3584,7 @@ fn buildOutputType(
35793584
.headerpad_max_install_names = headerpad_max_install_names,
35803585
.dead_strip_dylibs = dead_strip_dylibs,
35813586
.force_load_objc = force_load_objc,
3587+
.discard_local_symbols = discard_local_symbols,
35823588
.reference_trace = reference_trace,
35833589
.pdb_out_path = pdb_out_path,
35843590
.error_limit = error_limit,

test/link/macho.zig

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
6262
macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
6363
macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
6464
macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
65+
macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target }));
6566
macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
6667
macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target }));
6768
macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
@@ -2502,6 +2503,51 @@ fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
25022503
return test_step;
25032504
}
25042505

2506+
fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step {
2507+
const test_step = addTestStep(b, "discard-local-symbols", opts);
2508+
2509+
const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "static int foo = 42;" });
2510+
2511+
const lib = addStaticLibrary(b, opts, .{ .name = "a" });
2512+
lib.addObject(obj);
2513+
2514+
const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2515+
2516+
{
2517+
const exe = addExecutable(b, opts, .{ .name = "main3" });
2518+
exe.addObject(main_o);
2519+
exe.addObject(obj);
2520+
exe.discard_local_symbols = true;
2521+
2522+
const run = addRunArtifact(exe);
2523+
run.expectExitCode(0);
2524+
test_step.dependOn(&run.step);
2525+
2526+
const check = exe.checkObject();
2527+
check.checkInSymtab();
2528+
check.checkNotPresent("_foo");
2529+
test_step.dependOn(&check.step);
2530+
}
2531+
2532+
{
2533+
const exe = addExecutable(b, opts, .{ .name = "main4" });
2534+
exe.addObject(main_o);
2535+
exe.linkLibrary(lib);
2536+
exe.discard_local_symbols = true;
2537+
2538+
const run = addRunArtifact(exe);
2539+
run.expectExitCode(0);
2540+
test_step.dependOn(&run.step);
2541+
2542+
const check = exe.checkObject();
2543+
check.checkInSymtab();
2544+
check.checkNotPresent("_foo");
2545+
test_step.dependOn(&check.step);
2546+
}
2547+
2548+
return test_step;
2549+
}
2550+
25052551
fn testUndefinedFlag(b: *Build, opts: Options) *Step {
25062552
const test_step = addTestStep(b, "undefined-flag", opts);
25072553

0 commit comments

Comments
 (0)