Fix Weak External symbol handling.

The fallback atom was used only when it's searching for a symbol in a library;
if an undefined symbol was not found in a library, the LLD looked for its
fallback symbol in the library.

Although it worked in most cases, because symbols with fallbacks usually occur
only in OLDNAMES.LIB (a standard library), that behavior was incompatible with
link.exe. This patch fixes the issue so that the semantics is the same as
MSVC's link.exe

The new (and correct, I believe) behavior is this:

 - If there's no definition for an undefined atom, replace the undefined atom
   with its fallback and then proceed (e.g. look in the next file or stop
   linking as usual.)

Weak External symbols are underspecified in the Microsoft PE/COFF spec. However,
as long as I observed the behavior of link.exe, this seems to be what we want
for compatibility.

Differential Revision: http://llvm-reviews.chandlerc.com/D2162

llvm-svn: 195269
This commit is contained in:
Rui Ueyama 2013-11-20 20:51:55 +00:00
parent f9329ff650
commit 70f11d7589
4 changed files with 36 additions and 27 deletions

View File

@ -83,6 +83,16 @@ void Resolver::handleFile(const File &file) {
for (const UndefinedAtom *undefAtom : file.undefined()) { for (const UndefinedAtom *undefAtom : file.undefined()) {
doUndefinedAtom(*undefAtom); doUndefinedAtom(*undefAtom);
resolverState |= StateNewUndefinedAtoms; resolverState |= StateNewUndefinedAtoms;
// If the undefined symbol has an alternative name, try to resolve the
// symbol with the name to give it a second chance. This feature is used for
// COFF "weak external" symbol.
if (!_symbolTable.isDefined(undefAtom->name())) {
if (const UndefinedAtom *fallbackAtom = undefAtom->fallback()) {
doUndefinedAtom(*fallbackAtom);
_symbolTable.addReplacement(undefAtom, fallbackAtom);
}
}
} }
for (const SharedLibraryAtom *shlibAtom : file.sharedLibrary()) { for (const SharedLibraryAtom *shlibAtom : file.sharedLibrary()) {
doSharedLibraryAtom(*shlibAtom); doSharedLibraryAtom(*shlibAtom);
@ -108,15 +118,6 @@ Resolver::forEachUndefines(UndefCallback callback, bool searchForOverrides) {
// load for previous undefine may also have loaded this undefine // load for previous undefine may also have loaded this undefine
if (!_symbolTable.isDefined(undefName)) if (!_symbolTable.isDefined(undefName))
callback(undefName, false); callback(undefName, false);
// If the undefined symbol has an alternative name, try to resolve the
// symbol with the name to give it a second chance. This feature is used
// for COFF "weak external" symbol.
if (!_symbolTable.isDefined(undefName)) {
if (const UndefinedAtom *fallbackUndefAtom = undefAtom->fallback()) {
_symbolTable.addReplacement(undefAtom, fallbackUndefAtom);
_symbolTable.add(*fallbackUndefAtom);
}
}
} }
// search libraries for overrides of common symbols // search libraries for overrides of common symbols
if (searchForOverrides) { if (searchForOverrides) {

View File

@ -5,14 +5,33 @@
--- ---
defined-atoms: defined-atoms:
- name: bar - name: def1
type: code scope: global
undefined-atoms: undefined-atoms:
- name: foo - name: undef1
fallback: fallback:
name: bar name: fallback1
- name: undef2
fallback:
name: fallback2
---
defined-atoms:
- name: fallback1
undefined-atoms:
- name: def1
fallback:
name: fallback3
... ...
# CHECK: defined-atoms: # CHECK: defined-atoms:
# CHECK-NEXT: - name: bar # CHECK-NEXT: - name: def1
# CHECK-NEXT: scope: global
# CHECK-NEXT: - name: fallback1
# CHECK-NEXT: ref-name: fallback1
# CHECK-NEXT: undefined-atoms:
# CHECK-NEXT: - name: fallback1
# CHECK-NEXT: - name: fallback2
# CHECK-NOT: - name: fallback3

View File

@ -1,5 +1,4 @@
# RUN: lld -core %s 2> %t.err | FileCheck %s # RUN: lld -core %s | FileCheck %s
# RUN: FileCheck -check-prefix=ERROR %s < %t.err
# #
# Test that undefined symbols preserve their attributes and merge properly # Test that undefined symbols preserve their attributes and merge properly
@ -29,12 +28,8 @@ undefined-atoms:
can-be-null: never can-be-null: never
- name: bar8 - name: bar8
can-be-null: at-runtime can-be-null: at-runtime
fallback:
name: baz1
- name: bar9 - name: bar9
can-be-null: at-buildtime can-be-null: at-buildtime
fallback:
name: baz2
--- ---
undefined-atoms: undefined-atoms:
- name: bar1 - name: bar1
@ -55,8 +50,6 @@ undefined-atoms:
can-be-null: never can-be-null: never
- name: bar9 - name: bar9
can-be-null: at-runtime can-be-null: at-runtime
fallback:
name: baz3
... ...
# CHECK: - name: regular_func # CHECK: - name: regular_func
@ -77,7 +70,3 @@ undefined-atoms:
# CHECK-NEXT: - name: bar8 # CHECK-NEXT: - name: bar8
# CHECK-NEXT: - name: bar9 # CHECK-NEXT: - name: bar9
# CHECK-NEXT: can-be-null: at-runtime # CHECK-NEXT: can-be-null: at-runtime
# CHECK-NEXT: fallback:
# CHECK-NEXT: name: baz3
# ERROR: undefined symbol bar9 has different fallback: baz2 in and baz3 in

View File

@ -4,6 +4,6 @@
# RUN: /entry:fn -- %t.obj %p/Inputs/static.lib 2> %t2.out # RUN: /entry:fn -- %t.obj %p/Inputs/static.lib 2> %t2.out
# RUN: FileCheck %s < %t2.out # RUN: FileCheck %s < %t2.out
CHECK-NOT: _no_such_symbol1 CHECK: _no_such_symbol1
CHECK-NOT: _no_such_symbol2 CHECK-NOT: _no_such_symbol2
CHECK: _no_such_symbol3 CHECK: _no_such_symbol3