From 1fdea551b22726783356c5bed90bce63706b550f Mon Sep 17 00:00:00 2001 From: Zach Cheung Date: Thu, 23 Mar 2023 17:16:39 -0700 Subject: [PATCH 1/2] add linker -wrap flag --- src/Compilation.zig | 3 +++ src/link.zig | 1 + src/link/Elf.zig | 12 ++++++++++++ src/main.zig | 14 ++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 8bddc5f6f0..e1e665c192 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -494,6 +494,7 @@ pub const InitOptions = struct { clang_argv: []const []const u8 = &[0][]const u8{}, lib_dirs: []const []const u8 = &[0][]const u8{}, rpath_list: []const []const u8 = &[0][]const u8{}, + wrap_list: []const []const u8 = &[0][]const u8{}, c_source_files: []const CSourceFile = &[0]CSourceFile{}, link_objects: []LinkObject = &[0]LinkObject{}, framework_dirs: []const []const u8 = &[0][]const u8{}, @@ -1438,6 +1439,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .wasi_emulated_libs = options.wasi_emulated_libs, .lib_dirs = options.lib_dirs, .rpath_list = options.rpath_list, + .wrap_list = options.wrap_list, .strip = strip, .is_native_os = options.is_native_os, .is_native_abi = options.is_native_abi, @@ -2259,6 +2261,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.rdynamic); man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); man.hash.addListOfBytes(comp.bin_file.options.rpath_list); + man.hash.addListOfBytes(comp.bin_file.options.wrap_list); man.hash.add(comp.bin_file.options.each_lib_rpath); man.hash.add(comp.bin_file.options.build_id); man.hash.add(comp.bin_file.options.skip_linker_dependencies); diff --git a/src/link.zig b/src/link.zig index 45873fec26..fb0f79f5f2 100644 --- a/src/link.zig +++ b/src/link.zig @@ -183,6 +183,7 @@ pub const Options = struct { wasi_emulated_libs: []const wasi_libc.CRTFile, lib_dirs: []const []const u8, rpath_list: []const []const u8, + wrap_list: []const []const u8, /// List of symbols forced as undefined in the symbol table /// thus forcing their resolution by the linker. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 7a3a0b0fdd..56bf3d8002 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1386,6 +1386,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.add(stack_size); man.hash.add(self.base.options.build_id); } + man.hash.addListOfBytes(self.base.options.wrap_list); man.hash.add(self.base.options.skip_linker_dependencies); man.hash.add(self.base.options.z_nodelete); man.hash.add(self.base.options.z_notext); @@ -1665,6 +1666,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(rpath); } } + + // wrap + var wrap_table = std.StringHashMap(void).init(self.base.allocator); + defer wrap_table.deinit(); + for (self.base.options.wrap_list) |wrap| { + if ((try wrap_table.fetchPut(wrap, {})) == null) { + try argv.append("-wrap"); + try argv.append(wrap); + } + } + if (self.base.options.each_lib_rpath) { var test_path = std.ArrayList(u8).init(self.base.allocator); defer test_path.deinit(); diff --git a/src/main.zig b/src/main.zig index 76c43476ee..167bdfbafb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -487,6 +487,7 @@ const usage_build_generic = \\ -fno-compiler-rt Prevent including compiler-rt symbols in output \\ -rdynamic Add all symbols to the dynamic symbol table \\ -rpath [path] Add directory to the runtime library search path + \\ -wrap [symbol] Override the symbol with the wrapped symbol definition \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries @@ -874,6 +875,9 @@ fn buildOutputType( var rpath_list = std.ArrayList([]const u8).init(gpa); defer rpath_list.deinit(); + var wrap_list = std.ArrayList([]const u8).init(gpa); + defer wrap_list.deinit(); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa); defer c_source_files.deinit(); @@ -1475,6 +1479,9 @@ fn buildOutputType( try system_libs.put(arg["-needed-l".len..], .{ .needed = true }); } else if (mem.startsWith(u8, arg, "-weak-l")) { try system_libs.put(arg["-weak-l".len..], .{ .weak = true }); + } else if (mem.eql(u8, arg, "-wrap")){ + try wrap_list.append(arg); + try wrap_list.append(args_iter.nextOrFatal()); } else if (mem.startsWith(u8, arg, "-D")) { try clang_argv.append(arg); } else if (mem.startsWith(u8, arg, "-I")) { @@ -2155,6 +2162,12 @@ fn buildOutputType( next_arg, }); }; + } else if (mem.eql(u8, arg, "-wrap")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try wrap_list.append(linker_args.items[i]); } else if (mem.startsWith(u8, arg, "/subsystem:")) { var split_it = mem.splitBackwards(u8, arg, ":"); subsystem = try parseSubSystem(split_it.first()); @@ -3039,6 +3052,7 @@ fn buildOutputType( .clang_argv = clang_argv.items, .lib_dirs = lib_dirs.items, .rpath_list = rpath_list.items, + .wrap_list = wrap_list.items, .c_source_files = c_source_files.items, .link_objects = link_objects.items, .framework_dirs = framework_dirs.items, From b086b7da9e9adbb4445363f635d3bb37177b2f18 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Apr 2023 07:51:39 -0700 Subject: [PATCH 2/2] zig cc: complete the -wrap flag implementation * use a set instead of a list * use of this flag currently requires LLD * add documentation * make it only a zig cc compatibility flag for now because I personally think this is an anti-feature. --- src/Compilation.zig | 9 +++++---- src/link.zig | 8 +++++++- src/link/Elf.zig | 12 +++--------- src/main.zig | 16 ++++------------ 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index e1e665c192..f6f4851e5e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -494,7 +494,7 @@ pub const InitOptions = struct { clang_argv: []const []const u8 = &[0][]const u8{}, lib_dirs: []const []const u8 = &[0][]const u8{}, rpath_list: []const []const u8 = &[0][]const u8{}, - wrap_list: []const []const u8 = &[0][]const u8{}, + symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}, c_source_files: []const CSourceFile = &[0]CSourceFile{}, link_objects: []LinkObject = &[0]LinkObject{}, framework_dirs: []const []const u8 = &[0][]const u8{}, @@ -826,7 +826,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { options.output_mode == .Lib or options.linker_script != null or options.version_script != null or options.emit_implib != null or - build_id) + build_id or + options.symbol_wrap_set.count() > 0) { break :blk true; } @@ -1439,7 +1440,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .wasi_emulated_libs = options.wasi_emulated_libs, .lib_dirs = options.lib_dirs, .rpath_list = options.rpath_list, - .wrap_list = options.wrap_list, + .symbol_wrap_set = options.symbol_wrap_set, .strip = strip, .is_native_os = options.is_native_os, .is_native_abi = options.is_native_abi, @@ -2261,7 +2262,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.rdynamic); man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); man.hash.addListOfBytes(comp.bin_file.options.rpath_list); - man.hash.addListOfBytes(comp.bin_file.options.wrap_list); + man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys()); man.hash.add(comp.bin_file.options.each_lib_rpath); man.hash.add(comp.bin_file.options.build_id); man.hash.add(comp.bin_file.options.skip_linker_dependencies); diff --git a/src/link.zig b/src/link.zig index fb0f79f5f2..18002f64a6 100644 --- a/src/link.zig +++ b/src/link.zig @@ -183,12 +183,18 @@ pub const Options = struct { wasi_emulated_libs: []const wasi_libc.CRTFile, lib_dirs: []const []const u8, rpath_list: []const []const u8, - wrap_list: []const []const u8, /// List of symbols forced as undefined in the symbol table /// thus forcing their resolution by the linker. /// Corresponds to `-u ` for ELF/MachO and `/include:` for COFF/PE. force_undefined_symbols: std.StringArrayHashMapUnmanaged(void), + /// Use a wrapper function for symbol. Any undefined reference to symbol + /// will be resolved to __wrap_symbol. Any undefined reference to + /// __real_symbol will be resolved to symbol. This can be used to provide a + /// wrapper for a system function. The wrapper function should be called + /// __wrap_symbol. If it wishes to call the system function, it should call + /// __real_symbol. + symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), version: ?std.builtin.Version, compatibility_version: ?std.builtin.Version, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 56bf3d8002..b25a6f8f8a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1386,7 +1386,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.add(stack_size); man.hash.add(self.base.options.build_id); } - man.hash.addListOfBytes(self.base.options.wrap_list); + man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys()); man.hash.add(self.base.options.skip_linker_dependencies); man.hash.add(self.base.options.z_nodelete); man.hash.add(self.base.options.z_notext); @@ -1667,14 +1667,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - // wrap - var wrap_table = std.StringHashMap(void).init(self.base.allocator); - defer wrap_table.deinit(); - for (self.base.options.wrap_list) |wrap| { - if ((try wrap_table.fetchPut(wrap, {})) == null) { - try argv.append("-wrap"); - try argv.append(wrap); - } + for (self.base.options.symbol_wrap_set.keys()) |symbol_name| { + try argv.appendSlice(&.{ "-wrap", symbol_name }); } if (self.base.options.each_lib_rpath) { diff --git a/src/main.zig b/src/main.zig index 167bdfbafb..74a5a47643 100644 --- a/src/main.zig +++ b/src/main.zig @@ -487,7 +487,6 @@ const usage_build_generic = \\ -fno-compiler-rt Prevent including compiler-rt symbols in output \\ -rdynamic Add all symbols to the dynamic symbol table \\ -rpath [path] Add directory to the runtime library search path - \\ -wrap [symbol] Override the symbol with the wrapped symbol definition \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries @@ -875,8 +874,7 @@ fn buildOutputType( var rpath_list = std.ArrayList([]const u8).init(gpa); defer rpath_list.deinit(); - var wrap_list = std.ArrayList([]const u8).init(gpa); - defer wrap_list.deinit(); + var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}; var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa); defer c_source_files.deinit(); @@ -1479,9 +1477,6 @@ fn buildOutputType( try system_libs.put(arg["-needed-l".len..], .{ .needed = true }); } else if (mem.startsWith(u8, arg, "-weak-l")) { try system_libs.put(arg["-weak-l".len..], .{ .weak = true }); - } else if (mem.eql(u8, arg, "-wrap")){ - try wrap_list.append(arg); - try wrap_list.append(args_iter.nextOrFatal()); } else if (mem.startsWith(u8, arg, "-D")) { try clang_argv.append(arg); } else if (mem.startsWith(u8, arg, "-I")) { @@ -2163,11 +2158,8 @@ fn buildOutputType( }); }; } else if (mem.eql(u8, arg, "-wrap")) { - i += 1; - if (i >= linker_args.items.len) { - fatal("expected linker arg after '{s}'", .{arg}); - } - try wrap_list.append(linker_args.items[i]); + const next_arg = linker_args_it.nextOrFatal(); + try symbol_wrap_set.put(arena, next_arg, {}); } else if (mem.startsWith(u8, arg, "/subsystem:")) { var split_it = mem.splitBackwards(u8, arg, ":"); subsystem = try parseSubSystem(split_it.first()); @@ -3052,7 +3044,7 @@ fn buildOutputType( .clang_argv = clang_argv.items, .lib_dirs = lib_dirs.items, .rpath_list = rpath_list.items, - .wrap_list = wrap_list.items, + .symbol_wrap_set = symbol_wrap_set, .c_source_files = c_source_files.items, .link_objects = link_objects.items, .framework_dirs = framework_dirs.items,