forked from OSchip/llvm-project
[LLD] [COFF] Fix up missing stdcall decorations in MinGW mode
If linking directly against a DLL without an import library, the DLL export symbols might not contain stdcall decorations. If we have an undefined symbol with decoration, and we happen to have a matching undecorated symbol (which either is lazy and can be loaded, or already defined), then alias it against that instead. This matches what's done in reverse, when we have a def file declaring to export a symbol without decoration, but we only have a defined decorated symbol. In that case we do a fuzzy match (SymbolTable::findMangle). This case is more straightforward; if we have a decorated undefined symbol, just strip the decoration and look for the corresponding undecorated symbol name. Add warnings and options for either silencing the warning or disabling the whole feature, corresponding to how ld.bfd does it. (This feature works for any symbol decoration mismatch, not only when linking against a DLL directly; ld.bfd also tolerates it anywhere, and also fixes up mismatches in the other direction, like SymbolTable::findMangle, for any symbol, not only exports. But in practice, at least for lld, it would primarily end up used for linking against DLLs.) Differential Revision: https://reviews.llvm.org/D104532
This commit is contained in:
parent
c09e5e50b1
commit
ce211c505b
|
@ -263,6 +263,7 @@ struct Configuration {
|
|||
bool warnLocallyDefinedImported = true;
|
||||
bool warnDebugInfoUnusable = true;
|
||||
bool warnLongSectionNames = true;
|
||||
bool warnStdcallFixup = true;
|
||||
bool incremental = true;
|
||||
bool integrityCheck = false;
|
||||
bool killAt = false;
|
||||
|
@ -273,6 +274,7 @@ struct Configuration {
|
|||
bool thinLTOIndexOnly;
|
||||
bool autoImport = false;
|
||||
bool pseudoRelocs = false;
|
||||
bool stdcallFixup = false;
|
||||
};
|
||||
|
||||
extern Configuration *config;
|
||||
|
|
|
@ -1773,6 +1773,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
|||
OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw);
|
||||
config->callGraphProfileSort = args.hasFlag(
|
||||
OPT_call_graph_profile_sort, OPT_call_graph_profile_sort_no, true);
|
||||
config->stdcallFixup =
|
||||
args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, config->mingw);
|
||||
config->warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup);
|
||||
|
||||
// Don't warn about long section names, such as .debug_info, for mingw or
|
||||
// when -debug:dwarf is requested.
|
||||
|
@ -2106,10 +2109,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
|||
if (!wrapped.empty())
|
||||
while (run());
|
||||
|
||||
if (config->autoImport) {
|
||||
if (config->autoImport || config->stdcallFixup) {
|
||||
// MinGW specific.
|
||||
// Load any further object files that might be needed for doing automatic
|
||||
// imports.
|
||||
// imports, and do stdcall fixups.
|
||||
//
|
||||
// For cases with no automatically imported symbols, this iterates once
|
||||
// over the symbol table and doesn't do anything.
|
||||
|
@ -2121,7 +2124,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
|||
// normal object file as well (although that won't be used for the
|
||||
// actual autoimport later on). If this pass adds new undefined references,
|
||||
// we won't iterate further to resolve them.
|
||||
symtab->loadMinGWAutomaticImports();
|
||||
//
|
||||
// If stdcall fixups only are needed for loading import entries from
|
||||
// a DLL without import library, this also just needs running once.
|
||||
// If it ends up pulling in more object files from static libraries,
|
||||
// (and maybe doing more stdcall fixups along the way), this would need
|
||||
// to loop these two calls.
|
||||
symtab->loadMinGWSymbols();
|
||||
run();
|
||||
}
|
||||
|
||||
|
|
|
@ -957,12 +957,6 @@ Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
|
|||
return dwarf->getDILineInfo(offset, sectionIndex);
|
||||
}
|
||||
|
||||
static StringRef ltrim1(StringRef s, const char *chars) {
|
||||
if (!s.empty() && strchr(chars, s[0]))
|
||||
return s.substr(1);
|
||||
return s;
|
||||
}
|
||||
|
||||
void ImportFile::parse() {
|
||||
const char *buf = mb.getBufferStart();
|
||||
const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
|
||||
|
|
|
@ -221,6 +221,7 @@ def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
|||
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
||||
def start_lib : F<"start-lib">,
|
||||
HelpText<"Start group of objects treated as if they were in a library">;
|
||||
defm stdcall_fixup : B_priv<"stdcall-fixup">;
|
||||
def thinlto_emit_imports_files :
|
||||
F<"thinlto-emit-imports-files">,
|
||||
HelpText<"Emit .imports files with -thinlto-index-only">;
|
||||
|
|
|
@ -28,6 +28,12 @@ using namespace llvm;
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
StringRef ltrim1(StringRef s, const char *chars) {
|
||||
if (!s.empty() && strchr(chars, s[0]))
|
||||
return s.substr(1);
|
||||
return s;
|
||||
}
|
||||
|
||||
static Timer ltoTimer("LTO", Timer::root());
|
||||
|
||||
SymbolTable *symtab;
|
||||
|
@ -249,7 +255,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
|
|||
errorOrWarn(os.str());
|
||||
}
|
||||
|
||||
void SymbolTable::loadMinGWAutomaticImports() {
|
||||
void SymbolTable::loadMinGWSymbols() {
|
||||
for (auto &i : symMap) {
|
||||
Symbol *sym = i.second;
|
||||
auto *undef = dyn_cast<Undefined>(sym);
|
||||
|
@ -260,6 +266,38 @@ void SymbolTable::loadMinGWAutomaticImports() {
|
|||
|
||||
StringRef name = undef->getName();
|
||||
|
||||
if (config->machine == I386 && config->stdcallFixup) {
|
||||
// Check if we can resolve an undefined decorated symbol by finding
|
||||
// the indended target as an undecorated symbol (only with a leading
|
||||
// underscore).
|
||||
StringRef origName = name;
|
||||
StringRef baseName = name;
|
||||
// Trim down stdcall/fastcall/vectorcall symbols to the base name.
|
||||
baseName = ltrim1(baseName, "_@");
|
||||
baseName = baseName.substr(0, baseName.find('@'));
|
||||
// Add a leading underscore, as it would be in cdecl form.
|
||||
std::string newName = ("_" + baseName).str();
|
||||
Symbol *l;
|
||||
if (newName != origName && (l = find(newName)) != nullptr) {
|
||||
// If we found a symbol and it is lazy; load it.
|
||||
if (l->isLazy() && !l->pendingArchiveLoad) {
|
||||
log("Loading lazy " + l->getName() + " from " +
|
||||
l->getFile()->getName() + " for stdcall fixup");
|
||||
forceLazy(l);
|
||||
}
|
||||
// If it's lazy or already defined, hook it up as weak alias.
|
||||
if (l->isLazy() || isa<Defined>(l)) {
|
||||
if (config->warnStdcallFixup)
|
||||
warn("Resolving " + origName + " by linking to " + newName);
|
||||
else
|
||||
log("Resolving " + origName + " by linking to " + newName);
|
||||
undef->weakAlias = l;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config->autoImport) {
|
||||
if (name.startswith("__imp_"))
|
||||
continue;
|
||||
// If we have an undefined symbol, but we have a lazy symbol we could
|
||||
|
@ -273,6 +311,7 @@ void SymbolTable::loadMinGWAutomaticImports() {
|
|||
forceLazy(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Defined *SymbolTable::impSymbol(StringRef name) {
|
||||
if (name.startswith("__imp_"))
|
||||
|
|
|
@ -57,7 +57,9 @@ public:
|
|||
// symbols and warn about imported local symbols.
|
||||
void resolveRemainingUndefines();
|
||||
|
||||
void loadMinGWAutomaticImports();
|
||||
// Load lazy objects that are needed for MinGW automatic import and for
|
||||
// doing stdcall fixups.
|
||||
void loadMinGWSymbols();
|
||||
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
||||
|
||||
// Returns a list of chunks of selected symbols.
|
||||
|
@ -135,6 +137,8 @@ extern SymbolTable *symtab;
|
|||
|
||||
std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
|
||||
|
||||
StringRef ltrim1(StringRef s, const char *chars);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
|
|
|
@ -289,6 +289,11 @@ bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
|
|||
else
|
||||
add("-WX:no");
|
||||
|
||||
if (args.hasFlag(OPT_enable_stdcall_fixup, OPT_disable_stdcall_fixup, false))
|
||||
add("-stdcall-fixup");
|
||||
else if (args.hasArg(OPT_disable_stdcall_fixup))
|
||||
add("-stdcall-fixup:no");
|
||||
|
||||
if (args.hasArg(OPT_shared))
|
||||
add("-dll");
|
||||
if (args.hasArg(OPT_verbose))
|
||||
|
|
|
@ -35,11 +35,15 @@ def disable_auto_import: F<"disable-auto-import">,
|
|||
HelpText<"Don't automatically import data symbols from other DLLs without dllimport">;
|
||||
def disable_runtime_pseudo_reloc: F<"disable-runtime-pseudo-reloc">,
|
||||
HelpText<"Don't do automatic imports that require runtime fixups">;
|
||||
def disable_stdcall_fixup: F<"disable-stdcall-fixup">,
|
||||
HelpText<"Don't resolve stdcall/fastcall/vectorcall to undecorated symbols">;
|
||||
defm dynamicbase: B<"dynamicbase", "Enable ASLR", "Disable ASLR">;
|
||||
def enable_auto_import: F<"enable-auto-import">,
|
||||
HelpText<"Automatically import data symbols from other DLLs where needed">;
|
||||
def enable_runtime_pseudo_reloc: F<"enable-runtime-pseudo-reloc">,
|
||||
HelpText<"Allow automatic imports that require runtime fixups">;
|
||||
def enable_stdcall_fixup: F<"enable-stdcall-fixup">,
|
||||
HelpText<"Resolve stdcall/fastcall/vectorcall to undecorated symbols without warnings">;
|
||||
defm entry: Eq<"entry", "Name of entry point symbol">, MetaVarName<"<entry>">;
|
||||
def exclude_all_symbols: F<"exclude-all-symbols">,
|
||||
HelpText<"Don't automatically export any symbols">;
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
# REQUIRES: x86
|
||||
|
||||
## Test creating a DLL and linking against the DLL without using an import
|
||||
## library.
|
||||
|
||||
## Test on i386 with stdcall/fastcall/vectorcall decorated symbols.
|
||||
|
||||
## Check that we normally warn about these fixups. If -stdcall-fixup:no
|
||||
## (--disable-stdcall-fixup on the MinGW linker level) is passed, we don't
|
||||
## do these fixups. If we -stdcall-fixup (--enable-stdcall-fixup on the MinGW
|
||||
## linker level) is passed, we don't warn about it at all.
|
||||
|
||||
# RUN: split-file %s %t.dir
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=i386-windows-gnu %t.dir/lib.s -o %t.lib.o
|
||||
# RUN: lld-link -safeseh:no -noentry -dll -def:%t.dir/lib.def %t.lib.o -out:%t.lib.dll -implib:%t.implib.lib
|
||||
# RUN: llvm-mc -filetype=obj -triple=i386-windows-gnu %t.dir/main.s -o %t.main.o
|
||||
# RUN: lld-link -lldmingw %t.main.o -out:%t.main.exe %t.lib.dll -opt:noref 2>&1 | FileCheck --check-prefix=LOG %s
|
||||
# RUN: llvm-readobj --coff-imports %t.main.exe | FileCheck %s
|
||||
# RUN: not lld-link -lldmingw %t.main.o -out:%t.main.exe %t.lib.dll -opt:noref -stdcall-fixup:no 2>&1 | FileCheck --check-prefix=ERROR %s
|
||||
# RUN: lld-link -lldmingw %t.main.o -out:%t.main.exe %t.lib.dll -opt:noref -stdcall-fixup 2>&1 | count 0
|
||||
|
||||
#--- lib.s
|
||||
.text
|
||||
.globl _stdcall@8
|
||||
.globl @fastcall@8
|
||||
.globl vectorcall@@8
|
||||
.globl __underscored
|
||||
_stdcall@8:
|
||||
movl 8(%esp), %eax
|
||||
addl 4(%esp), %eax
|
||||
retl $8
|
||||
@fastcall@8:
|
||||
movl 8(%esp), %eax
|
||||
addl 4(%esp), %eax
|
||||
retl $8
|
||||
vectorcall@@8:
|
||||
movl 8(%esp), %eax
|
||||
addl 4(%esp), %eax
|
||||
retl $8
|
||||
__underscored:
|
||||
ret
|
||||
|
||||
#--- lib.def
|
||||
EXPORTS
|
||||
stdcall
|
||||
fastcall
|
||||
vectorcall
|
||||
_underscored
|
||||
|
||||
#--- main.s
|
||||
.text
|
||||
.global _mainCRTStartup
|
||||
_mainCRTStartup:
|
||||
pushl $2
|
||||
pushl $1
|
||||
calll _stdcall@8
|
||||
movl $1, %ecx
|
||||
movl $2, %edx
|
||||
calll @fastcall@8
|
||||
movl $1, %ecx
|
||||
movl $2, %edx
|
||||
calll vectorcall@@8
|
||||
pushl $2
|
||||
pushl $1
|
||||
calll __underscored
|
||||
addl $8, %esp
|
||||
xorl %eax, %eax
|
||||
popl %ebp
|
||||
retl
|
||||
|
||||
# CHECK: Import {
|
||||
# CHECK-NEXT: Name: link-dll-stdcall.s.tmp.lib.dll
|
||||
# CHECK-NEXT: ImportLookupTableRVA:
|
||||
# CHECK-NEXT: ImportAddressTableRVA
|
||||
# CHECK-NEXT: Symbol: _underscored
|
||||
# CHECK-NEXT: Symbol: fastcall
|
||||
# CHECK-NEXT: Symbol: stdcall
|
||||
# CHECK-NEXT: Symbol: vectorcall
|
||||
# CHECK-NEXT: }
|
||||
|
||||
# LOG-DAG: Resolving vectorcall@@8 by linking to _vectorcall
|
||||
# LOG-DAG: Resolving @fastcall@8 by linking to _fastcall
|
||||
# LOG-DAG: Resolving _stdcall@8 by linking to _stdcall
|
||||
|
||||
# ERROR-DAG: undefined symbol: _stdcall@8
|
||||
# ERROR-DAG: undefined symbol: @fastcall@8
|
||||
# ERROR-DAG: undefined symbol: vectorcall@@8
|
|
@ -303,3 +303,12 @@ RUN: ld.lld -### -m i386pep foo.o 2>&1 | FileCheck -check-prefix NO-FATAL_WARNIN
|
|||
RUN: ld.lld -### -m i386pep foo.o -no-fatal-warnings 2>&1 | FileCheck -check-prefix NO-FATAL_WARNINGS %s
|
||||
RUN: ld.lld -### -m i386pep foo.o --no-fatal-warnings 2>&1 | FileCheck -check-prefix NO-FATAL_WARNINGS %s
|
||||
NO-FATAL_WARNINGS: -WX:no
|
||||
|
||||
RUN: ld.lld -### -m i386pep foo.o 2>&1 | FileCheck -check-prefix NO-FIXUP %s
|
||||
NO-FIXUP-NOT: -stdcall-fixup
|
||||
|
||||
RUN: ld.lld -### -m i386pep foo.o --enable-stdcall-fixup --disable-stdcall-fixup 2>&1 | FileCheck -check-prefix DISABLE-FIXUP %s
|
||||
DISABLE-FIXUP: -stdcall-fixup:no
|
||||
|
||||
RUN: ld.lld -### -m i386pep foo.o --disable-stdcall-fixup --enable-stdcall-fixup 2>&1 | FileCheck -check-prefix ENABLE-FIXUP %s
|
||||
ENABLE-FIXUP: -stdcall-fixup{{ }}
|
||||
|
|
Loading…
Reference in New Issue