[PECOFF] Create .edata section for the DLL export table.

This is the first patch to emit data for the DLL export table. The DLL export
table is the data used by the Windows loader to find the address of exported
function from DLL. With this patch, LLD is able to emit a valid DLL export
table which the Windows loader can interpret and load.

The data structure of the DLL export table is described in the Microsoft
PE/COFF Specification, section 5.3.

DLL support is not complete yet; the linker needs to emit an import library
for a DLL, otherwise the linker cannot link against the DLL. We also do not
support export-only-by-ordinal yet.

llvm-svn: 197212
This commit is contained in:
Rui Ueyama 2013-12-13 06:58:27 +00:00
parent 04adff775f
commit c91c24e33d
7 changed files with 272 additions and 0 deletions

View File

@ -1,4 +1,5 @@
add_lld_library(lldPECOFF add_lld_library(lldPECOFF
EdataPass.cpp
IdataPass.cpp IdataPass.cpp
PECOFFLinkingContext.cpp PECOFFLinkingContext.cpp
Pass.cpp Pass.cpp

View File

@ -0,0 +1,142 @@
//===- lib/ReaderWriter/PECOFF/EdataPass.cpp ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Pass.h"
#include "EdataPass.h"
#include "lld/Core/File.h"
#include "lld/Core/Pass.h"
#include "lld/ReaderWriter/Simple.h"
#include "llvm/Support/Path.h"
#include <ctime>
using lld::pecoff::edata::EdataAtom;
using llvm::object::export_address_table_entry;
using llvm::object::export_directory_table_entry;
namespace lld {
namespace pecoff {
static bool
getExportedAtoms(const PECOFFLinkingContext &ctx, MutableFile *file,
std::vector<const DefinedAtom *> &ret) {
std::map<StringRef, const DefinedAtom *> definedAtoms;
for (const DefinedAtom *atom : file->defined())
definedAtoms[atom->name()] = atom;
for (StringRef dllExport : ctx.getDllExports()) {
auto it = definedAtoms.find(ctx.decorateSymbol(dllExport));
if (it == definedAtoms.end()) {
llvm::errs() << "Symbol <" << dllExport
<< "> is exported but not defined.\n";
return false;
}
const DefinedAtom *atom = it->second;
ret.push_back(atom);
}
return true;
}
static bool compare(const DefinedAtom *a, const DefinedAtom *b) {
return a->name().compare(b->name()) < 0;
}
edata::EdataAtom *
EdataPass::createAddressTable(const std::vector<const DefinedAtom *> &atoms) {
EdataAtom *addressTable = new (_alloc) EdataAtom(
_file, sizeof(export_address_table_entry) * atoms.size());
size_t offset = 0;
for (const DefinedAtom *atom : atoms) {
addDir32NBReloc(addressTable, atom, offset);
offset += sizeof(export_address_table_entry);
}
return addressTable;
}
edata::EdataAtom *
EdataPass::createNamePointerTable(const std::vector<const DefinedAtom *> &atoms,
MutableFile *file) {
EdataAtom *table = new (_alloc) EdataAtom(_file, sizeof(uint32_t) * atoms.size());
size_t offset = 0;
for (const DefinedAtom *atom : atoms) {
COFFStringAtom *stringAtom = new (_alloc) COFFStringAtom(
_file, _file.getNextOrdinal(), ".edata", atom->name());
file->addAtom(*stringAtom);
addDir32NBReloc(table, stringAtom, offset);
offset += sizeof(uint32_t);
}
return table;
}
edata::EdataAtom *EdataPass::createExportDirectoryTable(size_t numEntries) {
EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry));
auto *data = ret->getContents<export_directory_table_entry>();
data->TimeDateStamp = time(nullptr);
data->OrdinalBase = 1;
data->AddressTableEntries = numEntries;
data->NumberOfNamePointers = numEntries;
return ret;
}
edata::EdataAtom *
EdataPass::createOrdinalTable(const std::vector<const DefinedAtom *> &atoms,
const std::vector<const DefinedAtom *> &sortedAtoms) {
EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(uint16_t) * atoms.size());
uint16_t *data = ret->getContents<uint16_t>();
std::map<const DefinedAtom *, size_t> ordinals;
size_t ordinal = 0;
for (const DefinedAtom *atom : atoms)
ordinals[atom] = ordinal++;
size_t index = 0;
for (const DefinedAtom *atom : sortedAtoms)
data[index++] = ordinals[atom];
return ret;
}
void EdataPass::perform(std::unique_ptr<MutableFile> &file) {
std::vector<const DefinedAtom *> atoms;
if (!getExportedAtoms(_ctx, file.get(), atoms))
return;
if (atoms.empty())
return;
EdataAtom *table = createExportDirectoryTable(atoms.size());
file->addAtom(*table);
COFFStringAtom *dllName = new (_alloc) COFFStringAtom(
_file, _file.getNextOrdinal(),
".edata", llvm::sys::path::filename(_ctx.outputPath()));
file->addAtom(*dllName);
addDir32NBReloc(table, dllName, offsetof(export_directory_table_entry, NameRVA));
EdataAtom *addressTable = createAddressTable(atoms);
file->addAtom(*addressTable);
addDir32NBReloc(table, addressTable,
offsetof(export_directory_table_entry, ExportAddressTableRVA));
std::vector<const DefinedAtom *> sortedAtoms(atoms);
std::sort(sortedAtoms.begin(), sortedAtoms.end(), compare);
EdataAtom *namePointerTable = createNamePointerTable(sortedAtoms, file.get());
file->addAtom(*namePointerTable);
addDir32NBReloc(table, namePointerTable,
offsetof(export_directory_table_entry, NamePointerRVA));
EdataAtom *ordinalTable = createOrdinalTable(atoms, sortedAtoms);
file->addAtom(*ordinalTable);
addDir32NBReloc(table, ordinalTable,
offsetof(export_directory_table_entry, OrdinalTableRVA));
}
} // namespace pecoff
} // namespace lld

View File

@ -0,0 +1,81 @@
//===- lib/ReaderWriter/PECOFF/EdataPass.h --------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file \brief This linker pass creates atoms for the DLL export
/// information. The defined atoms constructed in this pass will go into .edata
/// section.
///
/// For the details of the .edata section format, see Microsoft PE/COFF
/// Specification section 5.3, The .edata Section.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_PE_COFF_EDATA_PASS_H
#define LLD_READER_WRITER_PE_COFF_EDATA_PASS_H
#include "Atoms.h"
#include "lld/Core/File.h"
#include "lld/Core/Pass.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "lld/ReaderWriter/Simple.h"
#include "llvm/Support/COFF.h"
#include <map>
using llvm::COFF::ImportDirectoryTableEntry;
namespace lld {
namespace pecoff {
namespace edata {
/// The root class of all edata atoms.
class EdataAtom : public COFFLinkerInternalAtom {
public:
EdataAtom(VirtualFile &file, size_t size)
: COFFLinkerInternalAtom(file, file.getNextOrdinal(),
std::vector<uint8_t>(size)) {}
virtual SectionChoice sectionChoice() const { return sectionCustomRequired; }
virtual StringRef customSectionName() const { return ".edata"; }
virtual ContentType contentType() const { return typeData; }
virtual ContentPermissions permissions() const { return permR__; }
template<typename T> T *getContents() const {
return (T *)rawContent().data();
}
};
} // namespace edata
class EdataPass : public lld::Pass {
public:
EdataPass(const PECOFFLinkingContext &ctx) : _ctx(ctx), _file(ctx) {}
virtual void perform(std::unique_ptr<MutableFile> &file);
private:
edata::EdataAtom *createExportDirectoryTable(size_t numEntries);
edata::EdataAtom *createAddressTable(
const std::vector<const DefinedAtom *> &atoms);
edata::EdataAtom *createNamePointerTable(
const std::vector<const DefinedAtom *> &atoms, MutableFile *file);
edata::EdataAtom *createOrdinalTable(
const std::vector<const DefinedAtom *> &atoms,
const std::vector<const DefinedAtom *> &sortedAtoms);
const PECOFFLinkingContext &_ctx;
VirtualFile _file;
mutable llvm::BumpPtrAllocator _alloc;
};
} // namespace pecoff
} // namespace lld
#endif

View File

@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "Atoms.h" #include "Atoms.h"
#include "EdataPass.h"
#include "GroupedSectionsPass.h" #include "GroupedSectionsPass.h"
#include "IdataPass.h" #include "IdataPass.h"
#include "LinkerGeneratedSymbolFile.h" #include "LinkerGeneratedSymbolFile.h"
@ -249,6 +250,7 @@ uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName,
void PECOFFLinkingContext::addPasses(PassManager &pm) { void PECOFFLinkingContext::addPasses(PassManager &pm) {
pm.add(std::unique_ptr<Pass>(new pecoff::SetSubsystemPass(*this))); pm.add(std::unique_ptr<Pass>(new pecoff::SetSubsystemPass(*this)));
pm.add(std::unique_ptr<Pass>(new pecoff::EdataPass(*this)));
pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this))); pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this)));
pm.add(std::unique_ptr<Pass>(new LayoutPass())); pm.add(std::unique_ptr<Pass>(new LayoutPass()));
pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass())); pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));

View File

@ -885,6 +885,9 @@ void ExecutableWriter::build(const File &linkedFile) {
if (section->getSectionName() == ".idata.d") if (section->getSectionName() == ".idata.d")
dataDirectory->setField(DataDirectoryIndex::IMPORT_TABLE, dataDirectory->setField(DataDirectoryIndex::IMPORT_TABLE,
section->getVirtualAddress(), section->size()); section->getVirtualAddress(), section->size());
if (section->getSectionName() == ".edata")
dataDirectory->setField(DataDirectoryIndex::EXPORT_TABLE,
section->getVirtualAddress(), section->size());
} }
// Now that we know the size and file offset of sections. Set the file // Now that we know the size and file offset of sections. Set the file

View File

@ -0,0 +1,31 @@
---
header:
Machine: IMAGE_FILE_MACHINE_I386
Characteristics: []
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: B800000000506800000000680000000050E80000000050E800000000
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
NumberOfAuxSymbols: 1
AuxiliaryData: 1C0000000400000000000000000000000000
- Name: _init
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: _exportfn
Value: 8
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...

View File

@ -0,0 +1,12 @@
# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj
#
# RUN: lld -flavor link /out:%t.dll /dll /subsystem:console /entry:_init \
# RUN: /export:exportfn -- %t.obj
# RUN: llvm-objdump -s %t.dll | FileCheck %s
CHECK: Contents of section .edata:
CHECK-NEXT: 1000 00000000 {{........}} 00000000 28100000
CHECK-NEXT: 1010 01000000 01000000 01000000 3c100000
CHECK-NEXT: 1020 40100000 4e100000 6578706f 72742e74
CHECK-NEXT: 1030 6573742e 746d702e 646c6c00 08200000
CHECK-NEXT: 1040 44100000 5f657870 6f727466 6e000000