diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index c2c91ad447..65030fde32 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -160,6 +160,9 @@ dead_strip_dylibs: bool = false, /// (Darwin) Force load all members of static archives that implement an Objective-C class or category force_load_objc: bool = false, +/// Whether local symbols should be discarded from the symbol table. +discard_local_symbols: bool = false, + /// Position Independent Executable pie: ?bool = null, @@ -1555,6 +1558,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { if (compile.force_load_objc) { try zig_args.append("-ObjC"); } + if (compile.discard_local_symbols) { + try zig_args.append("--discard-all"); + } try addFlag(&zig_args, "compiler-rt", compile.bundle_compiler_rt); try addFlag(&zig_args, "dll-export-fns", compile.dll_export_fns); diff --git a/src/Compilation.zig b/src/Compilation.zig index c2b72c6b0f..bec6696c92 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1160,6 +1160,8 @@ pub const CreateOptions = struct { dead_strip_dylibs: bool = false, /// (Darwin) Force load all members of static archives that implement an Objective-C class or category force_load_objc: bool = false, + /// Whether local symbols should be discarded from the symbol table. + discard_local_symbols: bool = false, libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, /// (Windows) PDB source path prefix to instruct the linker how to resolve relative /// paths when consolidating CodeView streams into a single PDB file. @@ -1585,6 +1587,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .headerpad_max_install_names = options.headerpad_max_install_names, .dead_strip_dylibs = options.dead_strip_dylibs, .force_load_objc = options.force_load_objc, + .discard_local_symbols = options.discard_local_symbols, .pdb_source_path = options.pdb_source_path, .pdb_out_path = options.pdb_out_path, .entry_addr = null, // CLI does not expose this option (yet?) @@ -2665,6 +2668,7 @@ fn addNonIncrementalStuffToCacheManifest( man.hash.add(opts.headerpad_max_install_names); man.hash.add(opts.dead_strip_dylibs); man.hash.add(opts.force_load_objc); + man.hash.add(opts.discard_local_symbols); // COFF specific stuff man.hash.addOptional(opts.subsystem); diff --git a/src/link.zig b/src/link.zig index d805b331e3..99d7908be1 100644 --- a/src/link.zig +++ b/src/link.zig @@ -491,6 +491,8 @@ pub const File = struct { /// Force load all members of static archives that implement an /// Objective-C class or category force_load_objc: bool, + /// Whether local symbols should be discarded from the symbol table. + discard_local_symbols: bool, /// Windows-specific linker flags: /// PDB source path prefix to instruct the linker how to resolve relative diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a5e3d2e070..97a84809d7 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -139,6 +139,8 @@ no_implicit_dylibs: bool = false, /// Whether the linker should parse and always force load objects containing ObjC in archives. // TODO: in Zig we currently take -ObjC as always on force_load_objc: bool = true, +/// Whether local symbols should be discarded from the symbol table. +discard_local_symbols: bool = false, /// Hot-code swapping state. hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, @@ -221,6 +223,7 @@ pub fn createEmpty( .lib_directories = options.lib_directories, .framework_dirs = options.framework_dirs, .force_load_objc = options.force_load_objc, + .discard_local_symbols = options.discard_local_symbols, }; if (use_llvm and comp.config.have_zcu) { self.llvm_object = try LlvmObject.create(arena, comp); @@ -720,6 +723,10 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { try argv.append("-ObjC"); } + if (self.discard_local_symbols) { + try argv.append("-x"); + } + if (self.entry_name) |entry_name| { try argv.appendSlice(&.{ "-e", entry_name }); } diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 2eb9837833..0218f0c1bb 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -583,6 +583,7 @@ pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void { const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (sym.getName(macho_file).len == 0) continue; + if (macho_file.discard_local_symbols and sym.isLocal()) continue; sym.flags.output_symtab = true; if (sym.isLocal()) { sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 000f374035..ec9d07aabc 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1722,6 +1722,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { if (file.getIndex() != self.index) continue; if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue; if (sym.isSymbolStab(macho_file)) continue; + if (macho_file.discard_local_symbols and sym.isLocal()) continue; const name = sym.getName(macho_file); if (name.len == 0) continue; // TODO in -r mode, we actually want to merge symbol names and emit only one diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 5f2e1291c0..76fa43bc65 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -500,6 +500,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue; + if (macho_file.discard_local_symbols and sym.isLocal()) continue; const name = sym.getName(macho_file); assert(name.len > 0); sym.flags.output_symtab = true; diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index 900316a769..6042fe628d 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -532,17 +532,23 @@ pub const Indsymtab = struct { for (macho_file.stubs.symbols.items) |ref| { const sym = ref.getSymbol(macho_file).?; - try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little); + if (sym.getOutputSymtabIndex(macho_file)) |idx| { + try writer.writeInt(u32, idx, .little); + } } for (macho_file.got.symbols.items) |ref| { const sym = ref.getSymbol(macho_file).?; - try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little); + if (sym.getOutputSymtabIndex(macho_file)) |idx| { + try writer.writeInt(u32, idx, .little); + } } for (macho_file.stubs.symbols.items) |ref| { const sym = ref.getSymbol(macho_file).?; - try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little); + if (sym.getOutputSymtabIndex(macho_file)) |idx| { + try writer.writeInt(u32, idx, .little); + } } } }; diff --git a/src/main.zig b/src/main.zig index eac8674b19..a5b0f23014 100644 --- a/src/main.zig +++ b/src/main.zig @@ -918,6 +918,7 @@ fn buildOutputType( var headerpad_max_install_names: bool = false; var dead_strip_dylibs: bool = false; var force_load_objc: bool = false; + var discard_local_symbols: bool = false; var contains_res_file: bool = false; var reference_trace: ?u32 = null; var pdb_out_path: ?[]const u8 = null; @@ -1151,6 +1152,8 @@ fn buildOutputType( entry = .{ .named = arg["-fentry=".len..] }; } else if (mem.eql(u8, arg, "--force_undefined")) { try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {}); + } else if (mem.eql(u8, arg, "--discard-all")) { + discard_local_symbols = true; } else if (mem.eql(u8, arg, "--stack")) { stack_size = parseStackSize(args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "--image-base")) { @@ -2510,6 +2513,8 @@ fn buildOutputType( entry = .{ .named = linker_args_it.nextOrFatal() }; } else if (mem.eql(u8, arg, "-u")) { try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {}); + } else if (mem.eql(u8, arg, "-x") or mem.eql(u8, arg, "--discard-all")) { + discard_local_symbols = true; } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) { stack_size = parseStackSize(linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--image-base")) { @@ -3579,6 +3584,7 @@ fn buildOutputType( .headerpad_max_install_names = headerpad_max_install_names, .dead_strip_dylibs = dead_strip_dylibs, .force_load_objc = force_load_objc, + .discard_local_symbols = discard_local_symbols, .reference_trace = reference_trace, .pdb_out_path = pdb_out_path, .error_limit = error_limit, diff --git a/test/link/macho.zig b/test/link/macho.zig index 49925e1c14..289d0201f7 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -62,6 +62,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); macho_step.dependOn(testTlsZig(b, .{ .target = default_target })); macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target })); + macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target })); macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target })); macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target })); macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target })); @@ -2502,6 +2503,51 @@ fn testTwoLevelNamespace(b: *Build, opts: Options) *Step { return test_step; } +fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "discard-local-symbols", opts); + + const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "static int foo = 42;" }); + + const lib = addStaticLibrary(b, opts, .{ .name = "a" }); + lib.addObject(obj); + + const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); + + { + const exe = addExecutable(b, opts, .{ .name = "main3" }); + exe.addObject(main_o); + exe.addObject(obj); + exe.discard_local_symbols = true; + + const run = addRunArtifact(exe); + run.expectExitCode(0); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkNotPresent("_foo"); + test_step.dependOn(&check.step); + } + + { + const exe = addExecutable(b, opts, .{ .name = "main4" }); + exe.addObject(main_o); + exe.linkLibrary(lib); + exe.discard_local_symbols = true; + + const run = addRunArtifact(exe); + run.expectExitCode(0); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkNotPresent("_foo"); + test_step.dependOn(&check.step); + } + + return test_step; +} + fn testUndefinedFlag(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "undefined-flag", opts);