[PECOFF] Define implicit symbols for exported ones.

This patch is to fix a compatibility issue with MSVC link.exe as to
use of dllexported symbols inside DLL.

A DLL exports two symbols for a function. One is non-decorated one,
and the other is with __imp_ prefix. The former is a function that
you can directly call, and the latter is a pointer to the function.
These dllexported symbols are created by linker for programs that
link against the DLL. So, I naturally believed that __imp_ symbols
become available when you once create a DLL and link against it, but
they don't exist until then. And that's not true.

MSVC link.exe is smart enough to allow users to use __imp_ symbols
locally. That is, if a symbol is specified with /export option, it
implicitly creates a new symbol with __imp_ prefix as a pointer to
the exported symbol. This feature allows the following program to
be linked and run, although _imp__hello is not defined in this code.

  #include <stdio.h>

  __declspec(dllexport)
  void hello(void) { printf("Hello\n"); }

  extern void (*_imp__hello)(void);

  int main() {
    _imp__hello();
    return 0;
  }

MSVC link.exe prints out the following warning when linking it.

  LNK4217: locally defined symbol _hello imported in function _main

Using __imp_ symbols locally is I think not a good coding style. One
should just take an address using "&" operator rather than appending
__imp_ prefix. However, there are programs in the wild that depends
on this link.exe's behavior, so we need this feature.

llvm-svn: 207141
This commit is contained in:
Rui Ueyama 2014-04-24 20:12:01 +00:00
parent b83dd55eb6
commit f550eba39c
1 changed files with 34 additions and 0 deletions

View File

@ -11,10 +11,25 @@
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "lld/ReaderWriter/Simple.h"
#include "llvm/Support/Allocator.h"
namespace lld {
namespace pecoff {
/// The defined atom for dllexported symbols with __imp_ prefix.
class ImpPointerAtom : public COFFLinkerInternalAtom {
public:
ImpPointerAtom(const File &file, StringRef symbolName)
: COFFLinkerInternalAtom(file, /*oridnal*/ 0, std::vector<uint8_t>(4),
symbolName) {}
uint64_t ordinal() const override { return 0; }
Scope scope() const override { return scopeGlobal; }
ContentType contentType() const override { return typeData; }
Alignment alignment() const override { return Alignment(4); }
ContentPermissions permissions() const override { return permR__; }
};
// A virtual file containing absolute symbol __ImageBase. __ImageBase (or
// ___ImageBase on x86) is a linker-generated symbol whose address is the same
// as the image base address.
@ -25,10 +40,29 @@ public:
_imageBaseAtom(*this, ctx.decorateSymbol("__ImageBase"),
Atom::scopeGlobal, ctx.getBaseAddress()) {
addAtom(_imageBaseAtom);
// Create implciit symbols for exported symbols.
for (const PECOFFLinkingContext::ExportDesc exp : ctx.getDllExports()) {
UndefinedAtom *target = new (_alloc) SimpleUndefinedAtom(*this, exp.name);
COFFLinkerInternalAtom *imp = createImpPointerAtom(ctx, exp.name);
imp->addReference(std::unique_ptr<COFFReference>(
new COFFReference(target, 0, llvm::COFF::IMAGE_REL_I386_DIR32)));
addAtom(*target);
addAtom(*imp);
}
};
private:
COFFLinkerInternalAtom *createImpPointerAtom(const PECOFFLinkingContext &ctx,
StringRef name) {
std::string sym = "_imp_";
sym.append(name);
sym = ctx.decorateSymbol(sym);
return new (_alloc) ImpPointerAtom(*this, ctx.allocate(sym));
}
COFFAbsoluteAtom _imageBaseAtom;
llvm::BumpPtrAllocator _alloc;
};
} // end namespace pecoff