forked from OSchip/llvm-project
COFF: Symbol resolution for common and comdat symbols defined in bitcode.
In the case where either a bitcode file and a regular file or two bitcode files export a common or comdat symbol with the same name, the linker needs to pick one of them following COFF semantics. This patch implements a design for resolving such symbols that pushes most of the work onto either LLD's regular mechanism for resolving common or comdat symbols or the IR linker's mechanism for doing the same. We modify SymbolBody::compare to always prefer non-bitcode symbols, so that during the initial phase of symbol resolution, the symbol table always contains a regular symbol in any case where we need to choose between a regular and a bitcode symbol. In SymbolTable::addCombinedLTOObject, we force export any bitcode symbols that were initially pre-empted by a regular symbol, and later use SymbolBody::compare to choose between the regular symbol in the symbol table and the regular symbol from the combined LTO object file. This design seems to be sound, so long as the resolution mechanism is defined to be commutative and associative modulo arbitrary choices between symbols (which seems to be the case for COFF). Differential Revision: http://reviews.llvm.org/D10329 llvm-svn: 239563
This commit is contained in:
parent
767c45719f
commit
1b6fd1f5fd
|
@ -266,7 +266,9 @@ std::error_code BitcodeFile::parse() {
|
|||
if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
|
||||
SymbolBodies.push_back(new (Alloc) Undefined(SymName));
|
||||
} else {
|
||||
SymbolBodies.push_back(new (Alloc) DefinedBitcode(SymName));
|
||||
bool Replaceable = (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE ||
|
||||
(Attrs & LTO_SYMBOL_COMDAT));
|
||||
SymbolBodies.push_back(new (Alloc) DefinedBitcode(SymName, Replaceable));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -268,6 +268,14 @@ std::error_code SymbolTable::addCombinedLTOObject() {
|
|||
return make_error_code(LLDError::BrokenFile);
|
||||
}
|
||||
Sym->Body = Body;
|
||||
} else {
|
||||
int comp = Sym->Body->compare(Body);
|
||||
if (comp < 0)
|
||||
Sym->Body = Body;
|
||||
if (comp == 0) {
|
||||
llvm::errs() << "LTO: unexpected duplicate symbol: " << Name << "\n";
|
||||
return make_error_code(LLDError::BrokenFile);
|
||||
}
|
||||
}
|
||||
|
||||
// We may see new references to runtime library symbols such as __chkstk
|
||||
|
@ -300,6 +308,13 @@ ErrorOr<ObjectFile *> SymbolTable::createLTOObject(LTOCodeGenerator *CG) {
|
|||
if (auto *S = dyn_cast<DefinedBitcode>(Body->getReplacement()))
|
||||
CG->addMustPreserveSymbol(S->getName());
|
||||
|
||||
// Likewise for bitcode symbols which we initially resolved to non-bitcode.
|
||||
for (std::unique_ptr<BitcodeFile> &File : BitcodeFiles)
|
||||
for (SymbolBody *Body : File->getSymbols())
|
||||
if (isa<DefinedBitcode>(Body) &&
|
||||
!isa<DefinedBitcode>(Body->getReplacement()))
|
||||
CG->addMustPreserveSymbol(Body->getName());
|
||||
|
||||
// Likewise for other symbols that must be preserved.
|
||||
for (StringRef Name : Config->GCRoots)
|
||||
if (isa<DefinedBitcode>(Symtab[Name]->Body))
|
||||
|
|
|
@ -21,14 +21,31 @@ using llvm::sys::fs::file_magic;
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
// As an approximation, regular symbols win over bitcode symbols, but we
|
||||
// definitely have a conflict if the regular symbol is not replaceable and
|
||||
// neither is the bitcode symbol. We do not replicate the rest of the symbol
|
||||
// resolution logic here; symbol resolution will be done accurately after
|
||||
// lowering bitcode symbols to regular symbols in addCombinedLTOObject().
|
||||
static int compareRegularBitcode(DefinedRegular *R, DefinedBitcode *B) {
|
||||
if (!R->isCommon() && !R->isCOMDAT() && !B->isReplaceable())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns 1, 0 or -1 if this symbol should take precedence over the
|
||||
// Other in the symbol table, tie or lose, respectively.
|
||||
int Defined::compare(SymbolBody *Other) {
|
||||
if (!isa<Defined>(Other))
|
||||
return 1;
|
||||
auto *X = dyn_cast<DefinedRegular>(this);
|
||||
if (!X)
|
||||
return 0;
|
||||
|
||||
if (auto *B = dyn_cast<DefinedBitcode>(Other))
|
||||
return compareRegularBitcode(X, B);
|
||||
|
||||
auto *Y = dyn_cast<DefinedRegular>(Other);
|
||||
if (!X || !Y)
|
||||
if (!Y)
|
||||
return 0;
|
||||
|
||||
// Common symbols are weaker than other types of defined symbols.
|
||||
|
@ -46,6 +63,22 @@ int Defined::compare(SymbolBody *Other) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int DefinedBitcode::compare(SymbolBody *Other) {
|
||||
if (!isa<Defined>(Other))
|
||||
return 1;
|
||||
|
||||
if (auto *R = dyn_cast<DefinedRegular>(Other))
|
||||
return -compareRegularBitcode(R, this);
|
||||
|
||||
if (auto *B = dyn_cast<DefinedBitcode>(Other)) {
|
||||
if (!isReplaceable() && !B->isReplaceable())
|
||||
return 0;
|
||||
// Non-replaceable symbols win.
|
||||
return isReplaceable() ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Lazy::compare(SymbolBody *Other) {
|
||||
if (isa<Defined>(Other))
|
||||
return -1;
|
||||
|
|
|
@ -267,7 +267,8 @@ private:
|
|||
|
||||
class DefinedBitcode : public Defined {
|
||||
public:
|
||||
DefinedBitcode(StringRef N) : Defined(DefinedBitcodeKind), Name(N) {}
|
||||
DefinedBitcode(StringRef N, bool R)
|
||||
: Defined(DefinedBitcodeKind), Name(N), Replaceable(R) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == DefinedBitcodeKind;
|
||||
|
@ -276,9 +277,12 @@ public:
|
|||
StringRef getName() override { return Name; }
|
||||
uint64_t getRVA() override { llvm_unreachable("bitcode reached writer"); }
|
||||
uint64_t getFileOff() override { llvm_unreachable("bitcode reached writer"); }
|
||||
int compare(SymbolBody *Other) override;
|
||||
bool isReplaceable() const { return Replaceable; }
|
||||
|
||||
private:
|
||||
StringRef Name;
|
||||
bool Replaceable;
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
$comdat = comdat any
|
||||
|
||||
define void @f1() {
|
||||
call void @comdat()
|
||||
ret void
|
||||
}
|
||||
|
||||
define linkonce_odr void @comdat() comdat {
|
||||
ret void
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
$comdat = comdat any
|
||||
|
||||
define void @f2() {
|
||||
call void @comdat()
|
||||
ret void
|
||||
}
|
||||
|
||||
define linkonce_odr void @comdat() comdat {
|
||||
ret void
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
; RUN: llvm-as -o %T/comdat-main.lto.obj %s
|
||||
; RUN: llvm-as -o %T/comdat1.lto.obj %S/Inputs/lto-comdat1.ll
|
||||
; RUN: llvm-as -o %T/comdat2.lto.obj %S/Inputs/lto-comdat2.ll
|
||||
; RUN: rm -f %T/comdat.lto.lib
|
||||
; RUN: llvm-ar cru %T/comdat.lto.lib %T/comdat1.lto.obj %T/comdat2.lto.obj
|
||||
|
||||
; RUN: llc -filetype=obj -o %T/comdat-main.obj %s
|
||||
; RUN: llc -filetype=obj -o %T/comdat1.obj %S/Inputs/lto-comdat1.ll
|
||||
; RUN: llc -filetype=obj -o %T/comdat2.obj %S/Inputs/lto-comdat2.ll
|
||||
; RUN: rm -f %T/comdat.lib
|
||||
; RUN: llvm-ar cru %T/comdat.lib %T/comdat1.obj %T/comdat2.obj
|
||||
|
||||
; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.lto.obj %T/comdat2.lto.obj
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s
|
||||
; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat.lto.lib
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s
|
||||
|
||||
; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.obj %T/comdat1.lto.obj %T/comdat2.lto.obj
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s
|
||||
; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.obj %T/comdat.lto.lib
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s
|
||||
|
||||
; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.obj %T/comdat2.obj
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-10 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-10 %s
|
||||
; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat.lib
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-10 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-10 %s
|
||||
|
||||
; HEADERS-11: AddressOfEntryPoint: 0x1000
|
||||
; TEXT-11: Disassembly of section .text:
|
||||
; TEXT-11-NEXT: .text:
|
||||
; TEXT-11-NEXT: xorl %eax, %eax
|
||||
; TEXT-11-NEXT: retq
|
||||
|
||||
; HEADERS-01: AddressOfEntryPoint: 0x2000
|
||||
; TEXT-01: Disassembly of section .text:
|
||||
; TEXT-01-NEXT: .text:
|
||||
; TEXT-01-NEXT: subq $40, %rsp
|
||||
; TEXT-01-NEXT: callq 39
|
||||
; TEXT-01-NEXT: callq 50
|
||||
; TEXT-01-NEXT: callq 13
|
||||
; TEXT-01-NEXT: xorl %eax, %eax
|
||||
; TEXT-01-NEXT: addq $40, %rsp
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01-NEXT: nopw %cs:(%rax,%rax)
|
||||
; TEXT-01-NEXT: retq
|
||||
|
||||
; HEADERS-10: AddressOfEntryPoint: 0x2030
|
||||
; TEXT-10: Disassembly of section .text:
|
||||
; TEXT-10-NEXT: .text:
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq 7
|
||||
; TEXT-10-NEXT: nop
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq -25
|
||||
; TEXT-10-NEXT: nop
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq -57
|
||||
; TEXT-10-NEXT: callq -30
|
||||
; TEXT-10-NEXT: xorl %eax, %eax
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
|
||||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
$comdat = comdat any
|
||||
|
||||
define i32 @main() {
|
||||
call void @f1()
|
||||
call void @f2()
|
||||
call void @comdat()
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define linkonce_odr void @comdat() comdat {
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @f1()
|
||||
declare void @f2()
|
Loading…
Reference in New Issue