Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -126,7 +126,8 @@ bool HasDynamicList = false; bool HasDynSymTab; bool ICF; - bool ICFData; + bool IgnoreDataAddressEquality; + bool IgnoreFunctionAddressEquality; bool MergeArmExidx; bool MipsN32Abi = false; bool NoGnuUnique; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -618,7 +618,10 @@ Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false); - Config->ICFData = Args.hasArg(OPT_icf_data); + Config->IgnoreDataAddressEquality = + Args.hasArg(OPT_ignore_data_address_equality); + Config->IgnoreFunctionAddressEquality = + Args.hasArg(OPT_ignore_function_address_equality); Config->Init = Args.getLastArgValue(OPT_init, "_init"); Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline); Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes); Index: ELF/ICF.cpp =================================================================== --- ELF/ICF.cpp +++ ELF/ICF.cpp @@ -161,8 +161,9 @@ // Returns true if section S is subject of ICF. static bool isEligible(InputSection *S) { - // Don't merge read only data sections unless --icf-data was passed. - if (!(S->Flags & SHF_EXECINSTR) && !Config->ICFData) + // Don't merge read only data sections unless + // --ignore-data-address-equality was passed. + if (!(S->Flags & SHF_EXECINSTR) && !Config->IgnoreDataAddressEquality) return false; // .init and .fini contains instructions that must be executed to Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -143,11 +143,14 @@ def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">; -def icf_data: F<"icf-data">, - HelpText<"Enable ICF to also fold identical read only data">; - def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">; +def ignore_function_address_equality: F<"ignore-function-address-equality">, + HelpText<"LLD can break the address equality of functions">; + +def ignore_data_address_equality: F<"ignore-data-address-equality">, + HelpText<"LLD can break the address equality of data">; + defm image_base : Eq<"image-base">, HelpText<"Set the base address">; defm init: Eq<"init">, HelpText<"Specify an initializer function">, Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -552,6 +552,23 @@ warn(Msg); } +// Return true if we can define a symbol in the executable that +// contains the value/function of a symbol defined in a shared +// library. +static bool canDefineSymbolInExecutable(Symbol &Sym) { + // If the symbol has default visibility the symbol defined in the + // executable will preempt it. + if (Sym.getVisibility() == STV_DEFAULT) + return true; + + // If we are allowed to break address equality of functions, defining + // a plt entry will allow the program to call the function in the + // .so, but the .so and the executable will no agree on the address + // of the function. Similar logic for objects. + return ((Sym.isFunc() && Config->IgnoreFunctionAddressEquality) || + (Sym.isObject() && Config->IgnoreDataAddressEquality)); +} + // Returns PLT relocation expression. // // This handles a non PIC program call to function in a shared library. In @@ -626,7 +643,7 @@ if (!Sym.isShared() || Config->Shared) return Expr; - if (Sym.getVisibility() != STV_DEFAULT) { + if (!canDefineSymbolInExecutable(Sym)) { error("cannot preempt symbol: " + toString(Sym) + getLocation(S, Sym, RelOff)); return Expr; Index: test/ELF/Inputs/protected-data-access.s =================================================================== --- /dev/null +++ test/ELF/Inputs/protected-data-access.s @@ -0,0 +1,7 @@ + .section .rodata,"a" + .global foo + .protected foo + .type foo, @object + .size foo, 8 +foo: + .quad 42 Index: test/ELF/Inputs/protected-function-access.s =================================================================== --- /dev/null +++ test/ELF/Inputs/protected-function-access.s @@ -0,0 +1,5 @@ + .global foo + .protected foo + .type foo, @function +foo: + ret Index: test/ELF/icf9.s =================================================================== --- test/ELF/icf9.s +++ test/ELF/icf9.s @@ -11,7 +11,7 @@ # CHECK-NOT: selected .rodata.d2 # We do merge rodata if passed --icf-data -# RUN: ld.lld %t -o %t2 --icf=all --verbose --icf-data 2>&1 | FileCheck --check-prefix=DATA %s +# RUN: ld.lld %t -o %t2 --icf=all --verbose --ignore-data-address-equality 2>&1 | FileCheck --check-prefix=DATA %s # RUN: llvm-readelf -S -W %t2 | FileCheck --check-prefix=DATA-SEC %s # DATA: selected .rodata.d1 Index: test/ELF/protected-data-access.s =================================================================== --- /dev/null +++ test/ELF/protected-data-access.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 +# RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %p/Inputs/protected-data-access.s -o %t2.o +# RUN: ld.lld %t2.o -o %t2.so -shared +# RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %s -o %t.o + +# RUN: not ld.lld %t.o %t2.so -o %t 2>&1 | FileCheck --check-prefix=ERR %s +# ERR: error: cannot preempt symbol: foo + +# RUN: ld.lld --ignore-data-address-equality %t.o %t2.so -o %t +# RUN: llvm-readobj --dyn-symbols --relocations %t | FileCheck %s + +# Check that we have a copy relocation. + +# CHECK: R_X86_64_COPY foo 0x0 + +# CHECK: Name: foo +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .bss.rel.ro + +.global _start +_start: + .quad foo + Index: test/ELF/protected-function-access.s =================================================================== --- /dev/null +++ test/ELF/protected-function-access.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 +# RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %p/Inputs/protected-function-access.s -o %t2.o +# RUN: ld.lld %t2.o -o %t2.so -shared +# RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %s -o %t.o + +# RUN: not ld.lld %t.o %t2.so -o %t 2>&1 | FileCheck --check-prefix=ERR %s +# ERR: error: cannot preempt symbol: foo + +# RUN: ld.lld --ignore-function-address-equality %t.o %t2.so -o %t +# RUN: llvm-readobj --dyn-symbols --relocations %t | FileCheck %s + +# Check that we have a relocation and an undefined symbol with a non zero address + +# CHECK: R_X86_64_JUMP_SLOT foo 0x0 + +# CHECK: Name: foo +# CHECK-NEXT: Value: 0x201020 +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined + +.global _start +_start: + .quad foo +