diff --git a/cmdline.cc b/cmdline.cc index 27c26063..750ea199 100644 --- a/cmdline.cc +++ b/cmdline.cc @@ -111,6 +111,7 @@ Options: --no-warn-common --whole-archive Include all objects from static archives --no-whole-archive + --wrap SYMBOL Use wrapper function for a given symbol -z now Disable lazy function resolution -z lazy Enable lazy function resolution (default) -z execstack Require executable stack @@ -496,6 +497,8 @@ void parse_nonpositional_args(Context &ctx, ctx.arg.compress_debug_sections = false; else Fatal(ctx) << "invalid --compress-debug-sections argument: " << arg; + } else if (read_arg(ctx, args, arg, "wrap")) { + ctx.arg.wrap.insert(arg); } else if (read_flag(args, "omagic") || read_flag(args, "N")) { ctx.arg.omagic = true; ctx.arg.is_static = true; diff --git a/docs/mold.1 b/docs/mold.1 index bc78628c..92158ffb 100644 --- a/docs/mold.1 +++ b/docs/mold.1 @@ -342,6 +342,8 @@ Warn about common symbols .IP "\fB\-\-no\-whole\-archive\fR" .PD Include all objects from static archives +.IP "\fB\-\-wrap\fR=\fIsymbol\fR" +Use wrapper functions for \fIsymbol\fR .IP "\fB\-z now\fR" Disable lazy function resolution .IP "\fB\-z lazy\fR" diff --git a/main.cc b/main.cc index e8eafd08..c7cb18e1 100644 --- a/main.cc +++ b/main.cc @@ -319,6 +319,10 @@ int do_main(int argc, char **argv) { Fatal(ctx) << "chdir failed: " << ctx.arg.directory << ": " << strerror(errno); + // Handle -wrap options if any. + for (std::string_view name : ctx.arg.wrap) + Symbol::intern(ctx, name, name)->wrap = true; + // Preload input files std::function on_complete; diff --git a/mold.h b/mold.h index 8659ae7d..0c23cd80 100644 --- a/mold.h +++ b/mold.h @@ -1427,6 +1427,7 @@ struct Context { std::string rpaths; std::string soname; std::string sysroot; + std::unordered_set wrap; std::vector auxiliary; std::vector exclude_libs; std::vector filter; @@ -1956,6 +1957,7 @@ public: u8 is_weak : 1 = false; u8 write_to_symtab : 1 = false; u8 traced : 1 = false; + u8 wrap : 1 = false; u8 has_copyrel : 1 = false; u8 copyrel_readonly : 1 = false; diff --git a/object_file.cc b/object_file.cc index ababcca3..e5ff337b 100644 --- a/object_file.cc +++ b/object_file.cc @@ -535,9 +535,12 @@ void ObjectFile::initialize_symbols(Context &ctx) { // Initialize global symbols for (i64 i = first_global; i < elf_syms.size(); i++) { const ElfSym &esym = elf_syms[i]; + + // Get a symbol name std::string_view key = symbol_strtab.data() + esym.st_name; std::string_view name = key; + // Parse symbol version after atsign if (i64 pos = name.find('@'); pos != name.npos) { std::string_view ver = name.substr(pos + 1); name = name.substr(0, pos); @@ -547,7 +550,21 @@ void ObjectFile::initialize_symbols(Context &ctx) { symvers[i - first_global] = ver.data(); } - this->symbols[i] = Symbol::intern(ctx, key, name); + Symbol *sym = Symbol::intern(ctx, key, name); + + // Handle -wrap option + if (esym.is_undef()) { + if (sym->wrap) { + sym = Symbol::intern(ctx, + save_string(ctx, "__wrap_" + std::string(key)), + save_string(ctx, "__wrap_" + std::string(name))); + } else if (name.starts_with("__real_") && + ctx.arg.wrap.count(name.substr(7))) { + sym = Symbol::intern(ctx, key.substr(7), name.substr(7)); + } + } + + this->symbols[i] = sym; if (esym.is_common()) has_common_symbol = true; diff --git a/test/wrap.sh b/test/wrap.sh new file mode 100755 index 00000000..9e6db5fb --- /dev/null +++ b/test/wrap.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e +cd $(dirname $0) +echo -n "Testing $(basename -s .sh $0) ... " +t=$(pwd)/tmp/$(basename -s .sh $0) +mkdir -p $t + +cat < + +void foo() { + printf("foo\n"); +} +EOF + +cat < + +void foo(); + +void __wrap_foo() { + printf("wrap_foo\n"); +} + +int main() { + foo(); +} +EOF + +cat < + +void __real_foo(); + +int main() { + __real_foo(); +} +EOF + +clang -fuse-ld=`pwd`/../mold -o $t/exe $t/a.o $t/b.o +$t/exe | grep -q '^foo$' + +clang -fuse-ld=`pwd`/../mold -o $t/exe $t/a.o $t/b.o -Wl,-wrap,foo +$t/exe | grep -q '^wrap_foo$' + +clang -fuse-ld=`pwd`/../mold -o $t/exe $t/a.o $t/c.o -Wl,-wrap,foo +$t/exe | grep -q '^foo$' + +echo OK