19 changes: 18 additions & 1 deletion lld/COFF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Symbol {
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
LazyDLLSymbolKind,

LastDefinedCOFFKind = DefinedCommonKind,
LastDefinedKind = DefinedSyntheticKind,
Expand Down Expand Up @@ -92,7 +93,8 @@ class Symbol {
bool isLive() const;

bool isLazy() const {
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind ||
symbolKind == LazyDLLSymbolKind;
}

private:
Expand Down Expand Up @@ -309,6 +311,18 @@ class LazyObject : public Symbol {
LazyObjFile *file;
};

class LazyDLLSymbol : public Symbol {
public:
LazyDLLSymbol(DLLFile *f, DLLFile::Symbol *s, StringRef n)
: Symbol(LazyDLLSymbolKind, n), file(f), sym(s) {}
static bool classof(const Symbol *s) {
return s->kind() == LazyDLLSymbolKind;
}

DLLFile *file;
DLLFile::Symbol *sym;
};

// Undefined symbols.
class Undefined : public Symbol {
public:
Expand Down Expand Up @@ -423,6 +437,7 @@ inline uint64_t Defined::getRVA() {
return cast<DefinedRegular>(this)->getRVA();
case LazyArchiveKind:
case LazyObjectKind:
case LazyDLLSymbolKind:
case UndefinedKind:
llvm_unreachable("Cannot get the address for an undefined symbol.");
}
Expand All @@ -447,6 +462,7 @@ inline Chunk *Defined::getChunk() {
return cast<DefinedCommon>(this)->getChunk();
case LazyArchiveKind:
case LazyObjectKind:
case LazyDLLSymbolKind:
case UndefinedKind:
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
}
Expand All @@ -467,6 +483,7 @@ union SymbolUnion {
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
alignas(LazyObject) char j[sizeof(LazyObject)];
alignas(LazyDLLSymbol) char k[sizeof(LazyDLLSymbol)];
};

template <typename T, typename... ArgT>
Expand Down
1 change: 1 addition & 0 deletions lld/COFF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,7 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
break;
case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
case Symbol::LazyDLLSymbolKind:
case Symbol::UndefinedKind:
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
// symbols shouldn't have relocations.
Expand Down
19 changes: 9 additions & 10 deletions lld/MinGW/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,10 @@ searchLibrary(StringRef name, ArrayRef<StringRef> searchPaths, bool bStatic) {
if (!bStatic) {
if (Optional<std::string> s = findFile(dir, name + ".lib"))
return *s;
if (Optional<std::string> s = findFile(dir, "lib" + name + ".dll")) {
error("lld doesn't support linking directly against " + *s +
", use an import library");
return "";
}
if (Optional<std::string> s = findFile(dir, name + ".dll")) {
error("lld doesn't support linking directly against " + *s +
", use an import library");
return "";
}
if (Optional<std::string> s = findFile(dir, "lib" + name + ".dll"))
return *s;
if (Optional<std::string> s = findFile(dir, name + ".dll"))
return *s;
}
}
error("unable to find library -l" + name);
Expand Down Expand Up @@ -295,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))
Expand Down
4 changes: 4 additions & 0 deletions lld/MinGW/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
64 changes: 64 additions & 0 deletions lld/test/COFF/link-dll-i386.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# REQUIRES: x86

## Test creating a DLL and linking against the DLL without using an import
## library.

## Test on i386 with cdecl decorated symbols.

## Linking the executable with -opt:noref, to make sure that we don't
## pull in more import entries than what's needed, even if not running GC.

# 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 -verbose 2>&1 | FileCheck --check-prefix=LOG %s
# RUN: llvm-readobj --coff-imports %t.main.exe | FileCheck %s

#--- lib.s
.text
.global _func1
_func1:
ret
.global _func2
_func2:
ret
.global _func3
_func3:
ret
.data
.global _variable
_variable:
.int 42

#--- lib.def
EXPORTS
func1
func2
func3
variable

#--- main.s
.text
.global _mainCRTStartup
_mainCRTStartup:
call _func2
movl .refptr._variable, %eax
movl (%eax), %eax
ret

.section .rdata$.refptr._variable,"dr",discard,.refptr._variable
.globl .refptr._variable
.refptr._variable:
.long _variable

# CHECK: Import {
# CHECK-NEXT: Name: link-dll-i386.s.tmp.lib.dll
# CHECK-NEXT: ImportLookupTableRVA:
# CHECK-NEXT: ImportAddressTableRVA
# CHECK-NEXT: Symbol: func2
# CHECK-NEXT: Symbol: variable
# CHECK-NEXT: }

# LOG: Automatically importing _variable from link-dll-i386.s.tmp.lib.dll
88 changes: 88 additions & 0 deletions lld/test/COFF/link-dll-stdcall.s
Original file line number Diff line number Diff line change
@@ -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
66 changes: 66 additions & 0 deletions lld/test/COFF/link-dll.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# REQUIRES: x86

## Test creating a DLL and linking against the DLL without using an import
## library.

## Explicitly creating an import library but naming it differently than the
## DLL, to avoid any risk of implicitly referencing it instead of the DLL
## itself.

## Linking the executable with -opt:noref, to make sure that we don't
## pull in more import entries than what's needed, even if not running GC.

# RUN: split-file %s %t.dir

# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-gnu %t.dir/lib.s -o %t.lib.o
# RUN: lld-link -noentry -dll -def:%t.dir/lib.def %t.lib.o -out:%t.lib.dll -implib:%t.implib.lib
# RUN: llvm-mc -filetype=obj -triple=x86_64-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 -verbose 2>&1 | FileCheck --check-prefix=LOG %s
# RUN: llvm-readobj --coff-imports %t.main.exe | FileCheck %s

#--- lib.s
.text
.global func1
func1:
ret
.global func2
func2:
ret
.global func3
func3:
ret
.data
.global variable
variable:
.int 42

#--- lib.def
EXPORTS
func1
func2
func3
variable

#--- main.s
.text
.global mainCRTStartup
mainCRTStartup:
call func2
movq .refptr.variable(%rip), %rax
movl (%rax), %eax
ret

.section .rdata$.refptr.variable,"dr",discard,.refptr.variable
.globl .refptr.variable
.refptr.variable:
.quad variable

# CHECK: Import {
# CHECK-NEXT: Name: link-dll.s.tmp.lib.dll
# CHECK-NEXT: ImportLookupTableRVA:
# CHECK-NEXT: ImportAddressTableRVA
# CHECK-NEXT: Symbol: func2
# CHECK-NEXT: Symbol: variable
# CHECK-NEXT: }

# LOG: Automatically importing variable from link-dll.s.tmp.lib.dll
9 changes: 9 additions & 0 deletions lld/test/MinGW/driver.test
Original file line number Diff line number Diff line change
Expand Up @@ -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{{ }}
8 changes: 4 additions & 4 deletions lld/test/MinGW/lib.test
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ OTHERSTYLES-SAME: msvcstyle.lib

RUN: echo > %t/lib/libnoimplib.dll
RUN: echo > %t/lib/noprefix_noimplib.dll
RUN: not ld.lld -### -m i386pep -L%t/lib -lnoimplib 2>&1 | FileCheck -check-prefix=UNSUPPORTED-DLL1 %s
RUN: not ld.lld -### -m i386pep -L%t/lib -lnoprefix_noimplib 2>&1 | FileCheck -check-prefix=UNSUPPORTED-DLL2 %s
UNSUPPORTED-DLL1: lld doesn't support linking directly against {{.*}}libnoimplib.dll, use an import library
UNSUPPORTED-DLL2: lld doesn't support linking directly against {{.*}}noprefix_noimplib.dll, use an import library
RUN: ld.lld -### -m i386pep -L%t/lib -lnoimplib 2>&1 | FileCheck -check-prefix=DLL1 %s
RUN: ld.lld -### -m i386pep -L%t/lib -lnoprefix_noimplib 2>&1 | FileCheck -check-prefix=DLL2 %s
DLL1: libnoimplib.dll
DLL2: noprefix_noimplib.dll