forked from OSchip/llvm-project
[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:
parent
04adff775f
commit
c91c24e33d
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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()));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
...
|
|
@ -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
|
Loading…
Reference in New Issue