[MinGW] [X86] Add stubs for references to data variables that might end up imported from a dll

Variables declared with the dllimport attribute are accessed via a
stub variable named __imp_<var>. In MinGW configurations, variables that
aren't declared with a dllimport attribute might still end up imported
from another DLL with runtime pseudo relocs.

For x86_64, this avoids the risk that the target is out of range
for a 32 bit PC relative reference, in case the target DLL is loaded
further than 4 GB from the reference. It also avoids having to make the
text section writable at runtime when doing the runtime fixups, which
makes it worthwhile to do for i386 as well.

Add stub variables for all dso local data references where a definition
of the variable isn't visible within the module, since the DLL data
autoimporting might make them imported even though they are marked as
dso local within LLVM.

Don't do this for variables that actually are defined within the same
module, since we then know for sure that it actually is dso local.

Don't do this for references to functions, since there's no need for
runtime pseudo relocations for autoimporting them; if a function from
a different DLL is called without the appropriate dllimport attribute,
the call just gets routed via a thunk instead.

GCC does something similar since 4.9 (when compiling with -mcmodel=medium
or large; from that version, medium is the default code model for x86_64
mingw), but only for x86_64.

Differential Revision: https://reviews.llvm.org/D51288

llvm-svn: 340942
This commit is contained in:
Martin Storsjo 2018-08-29 17:28:34 +00:00
parent 5ff7a8e67b
commit 489993db94
13 changed files with 204 additions and 22 deletions

View File

@ -80,6 +80,28 @@ public:
SymbolListTy GetGVStubList() { return getSortedStubs(GVStubs); }
};
/// MachineModuleInfoCOFF - This is a MachineModuleInfoImpl implementation
/// for COFF targets.
class MachineModuleInfoCOFF : public MachineModuleInfoImpl {
/// GVStubs - These stubs are used to materialize global addresses in PIC
/// mode.
DenseMap<MCSymbol *, StubValueTy> GVStubs;
virtual void anchor(); // Out of line virtual method.
public:
MachineModuleInfoCOFF(const MachineModuleInfo &) {}
StubValueTy &getGVStubEntry(MCSymbol *Sym) {
assert(Sym && "Key cannot be null");
return GVStubs[Sym];
}
/// Accessor methods to return the set of stubs in sorted order.
SymbolListTy GetGVStubList() { return getSortedStubs(GVStubs); }
};
} // end namespace llvm
#endif // LLVM_CODEGEN_MACHINEMODULEINFOIMPLS_H

View File

@ -32,6 +32,7 @@
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/CodeGen/GCMetadata.h"
@ -1402,6 +1403,33 @@ bool AsmPrinter::doFinalization(Module &M) {
}
}
if (TM.getTargetTriple().isOSBinFormatCOFF()) {
MachineModuleInfoCOFF &MMICOFF =
MMI->getObjFileInfo<MachineModuleInfoCOFF>();
// Output stubs for external and common global variables.
MachineModuleInfoCOFF::SymbolListTy Stubs = MMICOFF.GetGVStubList();
if (!Stubs.empty()) {
const DataLayout &DL = M.getDataLayout();
for (const auto &Stub : Stubs) {
SmallString<256> SectionName = StringRef(".rdata$");
SectionName += Stub.first->getName();
OutStreamer->SwitchSection(OutContext.getCOFFSection(
SectionName,
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ |
COFF::IMAGE_SCN_LNK_COMDAT,
SectionKind::getReadOnly(), Stub.first->getName(),
COFF::IMAGE_COMDAT_SELECT_ANY));
EmitAlignment(Log2_32(DL.getPointerSize()));
OutStreamer->EmitSymbolAttribute(Stub.first, MCSA_Global);
OutStreamer->EmitLabel(Stub.first);
OutStreamer->EmitSymbolValue(Stub.second.getPointer(),
DL.getPointerSize());
}
}
}
// Finalize debug and EH information.
for (const HandlerInfo &HI : Handlers) {
NamedRegionTimer T(HI.TimerName, HI.TimerDescription, HI.TimerGroupName,

View File

@ -25,6 +25,7 @@ using namespace llvm;
// Out of line virtual method.
void MachineModuleInfoMachO::anchor() {}
void MachineModuleInfoELF::anchor() {}
void MachineModuleInfoCOFF::anchor() {}
using PairTy = std::pair<MCSymbol *, MachineModuleInfoImpl::StubValueTy>;
static int SortSymbolPair(const PairTy *LHS, const PairTy *RHS) {

View File

@ -231,6 +231,11 @@ namespace X86II {
/// to be an absolute symbol in range [0,128), so we can use the @ABS8
/// symbol modifier.
MO_ABS8,
/// MO_COFFSTUB - On a symbol operand "FOO", this indicates that the
/// reference is actually to the ".refptr.FOO" symbol. This is used for
/// stub symbols on windows.
MO_COFFSTUB,
};
enum : uint64_t {

View File

@ -129,6 +129,9 @@ static void printSymbolOperand(X86AsmPrinter &P, const MachineOperand &MO,
if (MO.getTargetFlags() == X86II::MO_DLLIMPORT)
GVSym =
P.OutContext.getOrCreateSymbol(Twine("__imp_") + GVSym->getName());
else if (MO.getTargetFlags() == X86II::MO_COFFSTUB)
GVSym =
P.OutContext.getOrCreateSymbol(Twine(".refptr.") + GVSym->getName());
if (MO.getTargetFlags() == X86II::MO_DARWIN_NONLAZY ||
MO.getTargetFlags() == X86II::MO_DARWIN_NONLAZY_PIC_BASE) {
@ -161,6 +164,7 @@ static void printSymbolOperand(X86AsmPrinter &P, const MachineOperand &MO,
break;
case X86II::MO_DARWIN_NONLAZY:
case X86II::MO_DLLIMPORT:
case X86II::MO_COFFSTUB:
// These affect the name of the symbol, not any suffix.
break;
case X86II::MO_GOT_ABSOLUTE_ADDRESS:

View File

@ -7402,7 +7402,8 @@ X86InstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
{MO_DARWIN_NONLAZY_PIC_BASE, "x86-darwin-nonlazy-pic-base"},
{MO_TLVP, "x86-tlvp"},
{MO_TLVP_PIC_BASE, "x86-tlvp-pic-base"},
{MO_SECREL, "x86-secrel"}};
{MO_SECREL, "x86-secrel"},
{MO_COFFSTUB, "x86-coffstub"}};
return makeArrayRef(TargetFlags);
}

View File

@ -117,6 +117,7 @@ inline static bool isGlobalStubReference(unsigned char TargetFlag) {
case X86II::MO_GOT: // normal GOT reference.
case X86II::MO_DARWIN_NONLAZY_PIC_BASE: // Normal $non_lazy_ptr ref.
case X86II::MO_DARWIN_NONLAZY: // Normal $non_lazy_ptr ref.
case X86II::MO_COFFSTUB: // COFF .refptr stub.
return true;
default:
return false;

View File

@ -132,6 +132,9 @@ MCSymbol *X86MCInstLower::GetSymbolFromOperand(const MachineOperand &MO) const {
// Handle dllimport linkage.
Name += "__imp_";
break;
case X86II::MO_COFFSTUB:
Name += ".refptr.";
break;
case X86II::MO_DARWIN_NONLAZY:
case X86II::MO_DARWIN_NONLAZY_PIC_BASE:
Suffix = "$non_lazy_ptr";
@ -160,6 +163,18 @@ MCSymbol *X86MCInstLower::GetSymbolFromOperand(const MachineOperand &MO) const {
switch (MO.getTargetFlags()) {
default:
break;
case X86II::MO_COFFSTUB: {
MachineModuleInfoCOFF &MMICOFF =
MF.getMMI().getObjFileInfo<MachineModuleInfoCOFF>();
MachineModuleInfoImpl::StubValueTy &StubSym = MMICOFF.getGVStubEntry(Sym);
if (!StubSym.getPointer()) {
assert(MO.isGlobal() && "Extern symbol not handled yet");
StubSym = MachineModuleInfoImpl::StubValueTy(
AsmPrinter.getSymbol(MO.getGlobal()),
!MO.getGlobal()->hasInternalLinkage());
}
break;
}
case X86II::MO_DARWIN_NONLAZY:
case X86II::MO_DARWIN_NONLAZY_PIC_BASE: {
MachineModuleInfoImpl::StubValueTy &StubSym =
@ -191,6 +206,7 @@ MCOperand X86MCInstLower::LowerSymbolOperand(const MachineOperand &MO,
// These affect the name of the symbol, not any suffix.
case X86II::MO_DARWIN_NONLAZY:
case X86II::MO_DLLIMPORT:
case X86II::MO_COFFSTUB:
break;
case X86II::MO_TLVP:

View File

@ -138,6 +138,14 @@ unsigned char X86Subtarget::classifyGlobalReference(const GlobalValue *GV,
}
}
// For MinGW, if a data reference isn't marked as DSO local or DLLImport,
// and it's a pure declaration without a definition, it might potentially
// be automatically imported from another DLL, thus route accesses via a stub.
if (isTargetWindowsGNU() && GV && !GV->isDSOLocal() &&
!GV->hasDLLImportStorageClass() && GV->isDeclarationForLinker() &&
isa<GlobalVariable>(GV))
return X86II::MO_COFFSTUB;
if (TM.shouldAssumeDSOLocal(M, GV))
return classifyLocalReference(GV);

View File

@ -14,7 +14,8 @@
; X64_WINDOWS: orq %rax, %rcx
; X64_WINDOWS-NEXT: ud2
; X64_WINDOWS_GNU: orq %rax, %rcx
; X64_WINDOWS_GNU: movq .refptr._ZN11xercesc_2_513SchemaSymbols21fgURI_SCHEMAFORSCHEMAE(%rip), %rax
; X64_WINDOWS_GNU: orq .refptr._ZN11xercesc_2_56XMLUni16fgNotationStringE(%rip), %rax
; X64_WINDOWS_GNU-NEXT: ud2
; PS4: orq %rax, %rcx

View File

@ -0,0 +1,94 @@
; RUN: llc < %s -mtriple=x86_64-w64-mingw32 | FileCheck %s -check-prefix=CHECK-X64
; RUN: llc < %s -mtriple=i686-w64-mingw32 | FileCheck %s -check-prefix=CHECK-X86
@var = external local_unnamed_addr global i32, align 4
@dsolocalvar = external dso_local local_unnamed_addr global i32, align 4
@localvar = dso_local local_unnamed_addr global i32 0, align 4
@localcommon = common dso_local local_unnamed_addr global i32 0, align 4
@extvar = external dllimport local_unnamed_addr global i32, align 4
define dso_local i32 @getVar() {
; CHECK-X64-LABEL: getVar:
; CHECK-X64: movq .refptr.var(%rip), %rax
; CHECK-X64: movl (%rax), %eax
; CHECK-X64: retq
; CHECK-X86-LABEL: _getVar:
; CHECK-X86: movl .refptr._var, %eax
; CHECK-X86: movl (%eax), %eax
; CHECK-X86: retl
entry:
%0 = load i32, i32* @var, align 4
ret i32 %0
}
define dso_local i32 @getDsoLocalVar() {
; CHECK-X64-LABEL: getDsoLocalVar:
; CHECK-X64: movl dsolocalvar(%rip), %eax
; CHECK-X64: retq
; CHECK-X86-LABEL: _getDsoLocalVar:
; CHECK-X86: movl _dsolocalvar, %eax
; CHECK-X86: retl
entry:
%0 = load i32, i32* @dsolocalvar, align 4
ret i32 %0
}
define dso_local i32 @getLocalVar() {
; CHECK-X64-LABEL: getLocalVar:
; CHECK-X64: movl localvar(%rip), %eax
; CHECK-X64: retq
; CHECK-X86-LABEL: _getLocalVar:
; CHECK-X86: movl _localvar, %eax
; CHECK-X86: retl
entry:
%0 = load i32, i32* @localvar, align 4
ret i32 %0
}
define dso_local i32 @getLocalCommon() {
; CHECK-X64-LABEL: getLocalCommon:
; CHECK-X64: movl localcommon(%rip), %eax
; CHECK-X64: retq
; CHECK-X86-LABEL: _getLocalCommon:
; CHECK-X86: movl _localcommon, %eax
; CHECK-X86: retl
entry:
%0 = load i32, i32* @localcommon, align 4
ret i32 %0
}
define dso_local i32 @getExtVar() {
; CHECK-X64-LABEL: getExtVar:
; CHECK-X64: movq __imp_extvar(%rip), %rax
; CHECK-X64: movl (%rax), %eax
; CHECK-X64: retq
; CHECK-X86-LABEL: _getExtVar:
; CHECK-X86: movl __imp__extvar, %eax
; CHECK-X86: movl (%eax), %eax
; CHECK-X86: retl
entry:
%0 = load i32, i32* @extvar, align 4
ret i32 %0
}
define dso_local void @callFunc() {
; CHECK-X64-LABEL: callFunc:
; CHECK-X64: jmp otherFunc
; CHECK-X86-LABEL: _callFunc:
; CHECK-X86: jmp _otherFunc
entry:
tail call void @otherFunc()
ret void
}
declare dso_local void @otherFunc()
; CHECK-X64: .section .rdata$.refptr.var,"dr",discard,.refptr.var
; CHECK-X64: .globl .refptr.var
; CHECK-X64: .refptr.var:
; CHECK-X64: .quad var
; CHECK-X86: .section .rdata$.refptr._var,"dr",discard,.refptr._var
; CHECK-X86: .globl .refptr._var
; CHECK-X86: .refptr._var:
; CHECK-X86: .long _var

View File

@ -94,7 +94,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test1b:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -135,7 +135,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test1c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -176,7 +176,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test1d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -255,7 +255,7 @@ entry:
; DARWIN-X64: callq ___stack_chk_fail
; MINGW-X64-LABEL: test2b:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -298,7 +298,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test2c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -341,7 +341,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test2d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -465,7 +465,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test3c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -506,7 +506,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test3d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -632,7 +632,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test4c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -675,7 +675,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test4d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -828,7 +828,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test5d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a.addr = alloca i8*, align 8
@ -946,7 +946,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test6c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%retval = alloca i32, align 4
@ -987,7 +987,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test6d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%retval = alloca i32, align 4
@ -1099,7 +1099,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test7c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: .seh_endproc
%a = alloca i32, align 4
@ -1135,7 +1135,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test7d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%a = alloca i32, align 4
@ -1240,7 +1240,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test8c:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%b = alloca i32, align 4
@ -1275,7 +1275,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test8d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%b = alloca i32, align 4
@ -2850,7 +2850,7 @@ entry:
; MSVC-I386: calll @__security_check_cookie@4
; MINGW-X64-LABEL: test19d:
; MINGW-X64: mov{{l|q}} __stack_chk_guard
; MINGW-X64: mov{{l|q}} .refptr.__stack_chk_guard
; MINGW-X64: callq __stack_chk_fail
%c = alloca %struct.pair, align 4

View File

@ -9,9 +9,10 @@ declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
define dso_local void @func() sspstrong {
entry:
; MINGW-LABEL: func:
; MINGW: mov{{l|q}} __stack_chk_guard
; MINGW: mov{{l|q}} .refptr.__stack_chk_guard(%rip), [[REG:%[a-z]+]]
; MINGW: mov{{l|q}} ([[REG]])
; MINGW: callq other
; MINGW: mov{{l|q}} __stack_chk_guard
; MINGW: mov{{l|q}} ([[REG]])
; MINGW: callq __stack_chk_fail
; MINGW: .seh_endproc