Index: llvm/trunk/test/tools/gold/X86/v1.16/Inputs/wrap-bar.ll =================================================================== --- llvm/trunk/test/tools/gold/X86/v1.16/Inputs/wrap-bar.ll +++ llvm/trunk/test/tools/gold/X86/v1.16/Inputs/wrap-bar.ll @@ -0,0 +1,14 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define hidden void @bar() { + ret void +} + +define hidden void @__real_bar() { + ret void +} + +define hidden void @__wrap_bar() { + ret void +} Index: llvm/trunk/test/tools/gold/X86/v1.16/lit.local.cfg =================================================================== --- llvm/trunk/test/tools/gold/X86/v1.16/lit.local.cfg +++ llvm/trunk/test/tools/gold/X86/v1.16/lit.local.cfg @@ -0,0 +1,28 @@ +import re +import subprocess + +def is_gold_v1_16_linker_available(): + + if not config.gold_executable: + return False + try: + ld_cmd = subprocess.Popen([config.gold_executable, '-v'], + stdout = subprocess.PIPE, + stderr = subprocess.PIPE) + ld_out, _ = ld_cmd.communicate() + ld_out = ld_out.decode() + except: + return False + + match = re.search(r'GNU gold \(.*\) (\d+)\.(\d+)', ld_out) + if not match: + return False + major = int(match.group(1)) + minor = int(match.group(2)) + if major < 1 or (major == 1 and minor < 16): + return False + + return True + +if not is_gold_v1_16_linker_available(): + config.unsupported = True Index: llvm/trunk/test/tools/gold/X86/v1.16/wrap-1.ll =================================================================== --- llvm/trunk/test/tools/gold/X86/v1.16/wrap-1.ll +++ llvm/trunk/test/tools/gold/X86/v1.16/wrap-1.ll @@ -0,0 +1,42 @@ +; LTO +; RUN: llvm-as %s -o %t.o +; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext %t.o -o %t.out -wrap=bar -plugin-opt=save-temps +; RUN: llvm-readobj -t %t.out | FileCheck %s +; RUN: cat %t.out.resolution.txt | FileCheck -check-prefix=RESOLS %s + +; ThinLTO +; RUN: opt -module-summary %s -o %t.o +; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext %t.o -o %t.out -wrap=bar -plugin-opt=save-temps +; RUN: llvm-readobj -t %t.out | FileCheck %s +; RUN: cat %t.out.resolution.txt | FileCheck -check-prefix=RESOLS %s + +; CHECK: Name: __wrap_bar +; CHECK-NEXT: Value: +; CHECK-NEXT: Size: +; CHECK-NEXT: Binding: Global +; CHECK-NEXT: Type: Function + +; Make sure that the 'r' (linker redefined) bit is set for bar and __real_bar +; in the resolutions file, and that the 'x' (visible to regular obj) bit is set +; for bar and __wrap_bar. +; RESOLS: ,bar,lxr +; RESOLS: ,__wrap_bar,plx +; RESOLS: ,__real_bar,plr + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @bar() + +define void @_start() { + call void @bar() + ret void +} + +define void @__wrap_bar() { + ret void +} + +define void @__real_bar() { + ret void +} Index: llvm/trunk/test/tools/gold/X86/v1.16/wrap-2.ll =================================================================== --- llvm/trunk/test/tools/gold/X86/v1.16/wrap-2.ll +++ llvm/trunk/test/tools/gold/X86/v1.16/wrap-2.ll @@ -0,0 +1,55 @@ +; LTO +; This doesn't currently work with gold, because it does not apply defsym +; renaming to symbols in the same module (apparently by design for consistency +; with GNU ld). Because regular LTO hands back a single object file to gold, +; it doesn't perform the desired defsym renaming. This isn't an issue with +; ThinLTO which hands back multiple native objects to gold. For regular +; LTO defsym handling, gold will need a fix (not the gold plugin). +; RUN-TODO: llvm-as %s -o %t.o +; RUN-TODO: llvm-as %S/Inputs/wrap-bar.ll -o %t1.o +; RUN-TODO: %gold -plugin %llvmshlibdir/LLVMgold%shlibext %t.o %t1.o -shared -o %t.so -wrap=bar +; RUN-TODO: llvm-objdump -d %t.so | FileCheck %s +; RUN-TODO: llvm-readobj -t %t.so | FileCheck -check-prefix=BIND %s + +; ThinLTO +; RUN: opt -module-summary %s -o %t.o +; RUN: opt -module-summary %S/Inputs/wrap-bar.ll -o %t1.o +; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext %t.o %t1.o -shared -o %t.so -wrap=bar +; RUN: llvm-objdump -d %t.so | FileCheck %s -check-prefix=THIN +; RUN: llvm-readobj -t %t.so | FileCheck -check-prefix=BIND %s + +; Make sure that calls in foo() are not eliminated and that bar is +; routed to __wrap_bar and __real_bar is routed to bar. + +; CHECK: foo: +; CHECK-NEXT: pushq %rax +; CHECK-NEXT: callq{{.*}}<__wrap_bar> +; CHECK-NEXT: callq{{.*}} + +; THIN: foo: +; THIN-NEXT: pushq %rax +; THIN-NEXT: callq{{.*}}<__wrap_bar> +; THIN-NEXT: popq %rax +; THIN-NEXT: jmp{{.*}} + +; Check that bar and __wrap_bar retain their original binding. +; BIND: Name: bar +; BIND-NEXT: Value: +; BIND-NEXT: Size: +; BIND-NEXT: Binding: Local +; BIND: Name: __wrap_bar +; BIND-NEXT: Value: +; BIND-NEXT: Size: +; BIND-NEXT: Binding: Local + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @bar() +declare void @__real_bar() + +define void @foo() { + call void @bar() + call void @__real_bar() + ret void +} Index: llvm/trunk/tools/gold/gold-plugin.cpp =================================================================== --- llvm/trunk/tools/gold/gold-plugin.cpp +++ llvm/trunk/tools/gold/gold-plugin.cpp @@ -44,9 +44,18 @@ #define LDPT_GET_SYMBOLS_V3 28 +// FIXME: Remove when binutils 2.31 (containing gold 1.16) is the minimum +// required version. +#define LDPT_GET_WRAP_SYMBOLS 32 + using namespace llvm; using namespace lto; +// FIXME: Remove when binutils 2.31 (containing gold 1.16) is the minimum +// required version. +typedef enum ld_plugin_status (*ld_plugin_get_wrap_symbols)( + uint64_t *num_symbols, const char ***wrap_symbol_list); + static ld_plugin_status discard_message(int level, const char *format, ...) { // Die loudly. Recent versions of Gold pass ld_plugin_message as the first // callback in the transfer vector. This should never be called. @@ -56,6 +65,7 @@ static ld_plugin_release_input_file release_input_file = nullptr; static ld_plugin_get_input_file get_input_file = nullptr; static ld_plugin_message message = discard_message; +static ld_plugin_get_wrap_symbols get_wrap_symbols = nullptr; namespace { struct claimed_file { @@ -93,6 +103,8 @@ struct ResolutionInfo { bool CanOmitFromDynSym = true; bool DefaultVisibility = true; + bool CanInline = true; + bool IsUsedInRegularObj = false; }; } @@ -367,6 +379,13 @@ case LDPT_MESSAGE: message = tv->tv_u.tv_message; break; + case LDPT_GET_WRAP_SYMBOLS: + // FIXME: When binutils 2.31 (containing gold 1.16) is the minimum + // required version, this should be changed to: + // get_wrap_symbols = tv->tv_u.tv_get_wrap_symbols; + get_wrap_symbols = + (ld_plugin_get_wrap_symbols)tv->tv_u.tv_register_new_input; + break; default: break; } @@ -563,6 +582,29 @@ } } + // Handle any --wrap options passed to gold, which are than passed + // along to the plugin. + if (get_wrap_symbols) { + const char **wrap_symbols; + uint64_t count = 0; + if (get_wrap_symbols(&count, &wrap_symbols) != LDPS_OK) { + message(LDPL_ERROR, "Unable to get wrap symbols!"); + return LDPS_ERR; + } + for (uint64_t i = 0; i < count; i++) { + StringRef Name = wrap_symbols[i]; + ResolutionInfo &Res = ResInfo[Name]; + ResolutionInfo &WrapRes = ResInfo["__wrap_" + Name.str()]; + ResolutionInfo &RealRes = ResInfo["__real_" + Name.str()]; + // Tell LTO not to inline symbols that will be overwritten. + Res.CanInline = false; + RealRes.CanInline = false; + // Tell LTO not to eliminate symbols that will be used after renaming. + Res.IsUsedInRegularObj = true; + WrapRes.IsUsedInRegularObj = true; + } + } + return LDPS_OK; } @@ -686,6 +728,12 @@ (IsExecutable || !Res.DefaultVisibility)) R.FinalDefinitionInLinkageUnit = true; + if (!Res.CanInline) + R.LinkerRedefined = true; + + if (Res.IsUsedInRegularObj) + R.VisibleToRegularObj = true; + freeSymName(Sym); }