mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-11 02:16:50 +00:00
[PECOFF] Refactor IdataPass.
This patch is to basically move the functionality to construct Data Directory from IdataPass to WriterPECOFF. Data Directory is a part of the PE/COFF header and contains the addresses of the import tables. We used to represent the link from Data Directory to the import tables as relocation references. The idea behind it is that, because relocation references are processed by the Writer, we wouldn't have to do anything special to fill the addresses of the import tables. I thought that the addresses would be set "automatically". But it turned out that that design made the pass and the writer rather complicated. In order to make relocation references between Data Directory to the import tables, these data structures needed to be represented as Atom. However, because Data Directory is not a section content but a part of the PE/COFF header, it did not fit well as an Atom. So we ended up having complicated code both in IdataPass and the writer. This patch simplifies it. One side effect of this patch is that we now have ".idata.a", ".idata.d" and "idata.t" sections for the import address table, the import directory table, and the import lookup table. The writer looks for the sections by name to find the start addresses of the sections. We probably should have a better way to find a specific atom from the core linking result, but currently using the section name seems to be the easiest way to do that. The Windows loader do not care about the import table's section layout. llvm-svn: 197016
This commit is contained in:
parent
4fc8098979
commit
6a2e745351
@ -144,7 +144,6 @@ public:
|
||||
typeTLVInitialData, // initial data for a TLV [Darwin]
|
||||
typeTLVInitialZeroFill, // TLV initial zero fill data [Darwin]
|
||||
typeTLVInitializerPtr, // pointer to thread local initializer [Darwin]
|
||||
typeDataDirectoryEntry, // linker created for data directory header [PECOFF]
|
||||
typeThreadZeroFill, // Uninitialized thread local data(TBSS) [ELF]
|
||||
typeThreadData, // Initialized thread local data(TDATA) [ELF]
|
||||
typeRONote, // Identifies readonly note sections [ELF]
|
||||
|
@ -53,7 +53,6 @@ DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
|
||||
case typeLazyPointer:
|
||||
case typeLazyDylibPointer:
|
||||
case typeThunkTLV:
|
||||
case typeDataDirectoryEntry:
|
||||
case typeRWNote:
|
||||
return permRW_;
|
||||
|
||||
|
@ -246,18 +246,6 @@ private:
|
||||
std::vector<uint8_t> _data;
|
||||
};
|
||||
|
||||
// A COFFDataDirectoryAtom represents an entry of Optional Data Directory in the
|
||||
// COFF header.
|
||||
class COFFDataDirectoryAtom : public COFFLinkerInternalAtom {
|
||||
public:
|
||||
COFFDataDirectoryAtom(const File &file, uint64_t ordinal,
|
||||
std::vector<uint8_t> contents)
|
||||
: COFFLinkerInternalAtom(file, ordinal, contents) {}
|
||||
|
||||
virtual ContentType contentType() const { return typeDataDirectoryEntry; }
|
||||
virtual ContentPermissions permissions() const { return permR__; }
|
||||
};
|
||||
|
||||
// A COFFSharedLibraryAtom represents a symbol for data in an import library. A
|
||||
// reference to a COFFSharedLibraryAtom will be transformed to a real reference
|
||||
// to an import address table entry in Idata pass.
|
||||
|
@ -85,36 +85,36 @@ ImportTableEntryAtom::assembleRawContent(uint32_t contents) {
|
||||
void ImportDirectoryAtom::addRelocations(
|
||||
Context &context, StringRef loadName,
|
||||
const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) {
|
||||
size_t lookupEnd = context.importLookupTables.size();
|
||||
size_t addressEnd = context.importAddressTables.size();
|
||||
|
||||
// Create parallel arrays. The contents of the two are initially the
|
||||
// same. The PE/COFF loader overwrites the import address tables with the
|
||||
// pointers to the referenced items after loading the executable into
|
||||
// memory.
|
||||
addImportTableAtoms(context, sharedAtoms, false, context.importLookupTables);
|
||||
addImportTableAtoms(context, sharedAtoms, true, context.importAddressTables);
|
||||
std::vector<ImportTableEntryAtom *> importLookupTables =
|
||||
createImportTableAtoms(context, sharedAtoms, false, ".idata.t");
|
||||
std::vector<ImportTableEntryAtom *> importAddressTables =
|
||||
createImportTableAtoms(context, sharedAtoms, true, ".idata.a");
|
||||
|
||||
addDir32NBReloc(this, context.importLookupTables[lookupEnd],
|
||||
addDir32NBReloc(this, importLookupTables[0],
|
||||
offsetof(ImportDirectoryTableEntry, ImportLookupTableRVA));
|
||||
addDir32NBReloc(this, context.importAddressTables[addressEnd],
|
||||
addDir32NBReloc(this, importAddressTables[0],
|
||||
offsetof(ImportDirectoryTableEntry, ImportAddressTableRVA));
|
||||
addDir32NBReloc(this, new (_alloc) DLLNameAtom(context, loadName),
|
||||
offsetof(ImportDirectoryTableEntry, NameRVA));
|
||||
}
|
||||
|
||||
void ImportDirectoryAtom::addImportTableAtoms(
|
||||
std::vector<ImportTableEntryAtom *> ImportDirectoryAtom::createImportTableAtoms(
|
||||
Context &context, const std::vector<COFFSharedLibraryAtom *> &sharedAtoms,
|
||||
bool shouldAddReference, std::vector<ImportTableEntryAtom *> &ret) const {
|
||||
bool shouldAddReference, StringRef sectionName) const {
|
||||
std::vector<ImportTableEntryAtom *> ret;
|
||||
for (COFFSharedLibraryAtom *atom : sharedAtoms) {
|
||||
ImportTableEntryAtom *entry = nullptr;
|
||||
if (atom->importName().empty()) {
|
||||
// Import by ordinal
|
||||
uint32_t hint = (1U << 31) | atom->hint();
|
||||
entry = new (_alloc) ImportTableEntryAtom(context, hint);
|
||||
entry = new (_alloc) ImportTableEntryAtom(context, hint, sectionName);
|
||||
} else {
|
||||
// Import by name
|
||||
entry = new (_alloc) ImportTableEntryAtom(context, 0);
|
||||
entry = new (_alloc) ImportTableEntryAtom(context, 0, sectionName);
|
||||
HintNameAtom *hintName = createHintNameAtom(context, atom);
|
||||
addDir32NBReloc(entry, hintName);
|
||||
}
|
||||
@ -123,7 +123,8 @@ void ImportDirectoryAtom::addImportTableAtoms(
|
||||
atom->setImportTableEntry(entry);
|
||||
}
|
||||
// Add the NULL entry.
|
||||
ret.push_back(new (_alloc) ImportTableEntryAtom(context, 0));
|
||||
ret.push_back(new (_alloc) ImportTableEntryAtom(context, 0, sectionName));
|
||||
return ret;
|
||||
}
|
||||
|
||||
HintNameAtom *ImportDirectoryAtom::createHintNameAtom(
|
||||
@ -151,7 +152,6 @@ void IdataPass::perform(std::unique_ptr<MutableFile> &file) {
|
||||
new (_alloc) idata::NullImportDirectoryAtom(context);
|
||||
|
||||
connectAtoms(context);
|
||||
createDataDirectoryAtoms(context);
|
||||
replaceSharedLibraryAtoms(context);
|
||||
}
|
||||
|
||||
@ -183,48 +183,11 @@ void IdataPass::appendAtoms(std::vector<T *> &vec1,
|
||||
|
||||
void IdataPass::connectAtoms(idata::Context &context) {
|
||||
std::vector<COFFBaseDefinedAtom *> atoms;
|
||||
appendAtoms(atoms, context.importDirectories);
|
||||
appendAtoms(atoms, context.importLookupTables);
|
||||
appendAtoms(atoms, context.importAddressTables);
|
||||
appendAtoms(atoms, context.dllNameAtoms);
|
||||
appendAtoms(atoms, context.hintNameAtoms);
|
||||
coff::connectAtomsWithLayoutEdge(atoms);
|
||||
}
|
||||
|
||||
/// The addresses of the import dirctory and the import address table needs to
|
||||
/// be set to the COFF Optional Data Directory header. A COFFDataDirectoryAtom
|
||||
/// represents the data directory header. We create a COFFDataDirectoryAtom
|
||||
/// and set relocations to them, so that the address will be set by the
|
||||
/// writer.
|
||||
void IdataPass::createDataDirectoryAtoms(idata::Context &context) {
|
||||
// CLR_RUNTIME_HEADER is the last index of the data directory.
|
||||
int nentries = llvm::COFF::CLR_RUNTIME_HEADER + 1;
|
||||
int entSize = sizeof(llvm::object::data_directory);
|
||||
std::vector<uint8_t> contents(nentries * entSize, 0);
|
||||
|
||||
auto importTableOffset =
|
||||
llvm::COFF::DataDirectoryIndex::IMPORT_TABLE * entSize;
|
||||
auto iatOffset = llvm::COFF::DataDirectoryIndex::IAT * entSize;
|
||||
|
||||
auto *importTableEntry = reinterpret_cast<llvm::object::data_directory *>(
|
||||
&contents[0] + importTableOffset);
|
||||
auto *iatEntry = reinterpret_cast<llvm::object::data_directory *>(
|
||||
&contents[0] + iatOffset);
|
||||
|
||||
importTableEntry->Size =
|
||||
context.importDirectories.size() * context.importDirectories[0]->size();
|
||||
iatEntry->Size = context.importAddressTables.size() *
|
||||
context.importAddressTables[0]->size();
|
||||
|
||||
auto *dir = new (_alloc) coff::COFFDataDirectoryAtom(
|
||||
context.dummyFile, context.dummyFile.getNextOrdinal(),
|
||||
std::move(contents));
|
||||
addDir32NBReloc(dir, context.importDirectories[0], importTableOffset);
|
||||
addDir32NBReloc(dir, context.importAddressTables[0], iatOffset);
|
||||
|
||||
context.file.addAtom(*dir);
|
||||
}
|
||||
|
||||
/// Transforms a reference to a COFFSharedLibraryAtom to a real reference.
|
||||
void IdataPass::replaceSharedLibraryAtoms(idata::Context &context) {
|
||||
for (const DefinedAtom *atom : context.file.defined()) {
|
||||
|
@ -58,8 +58,6 @@ struct Context {
|
||||
// accumulate all atoms created in the pass in the following vectors, and add
|
||||
// layout edges when finishing the pass.
|
||||
std::vector<COFFBaseDefinedAtom *> importDirectories;
|
||||
std::vector<ImportTableEntryAtom *> importLookupTables;
|
||||
std::vector<ImportTableEntryAtom *> importAddressTables;
|
||||
std::vector<HintNameAtom *> hintNameAtoms;
|
||||
std::vector<DLLNameAtom *> dllNameAtoms;
|
||||
|
||||
@ -106,11 +104,16 @@ private:
|
||||
|
||||
class ImportTableEntryAtom : public IdataAtom {
|
||||
public:
|
||||
ImportTableEntryAtom(Context &context, uint32_t contents)
|
||||
: IdataAtom(context, assembleRawContent(contents)) {}
|
||||
ImportTableEntryAtom(Context &context, uint32_t contents,
|
||||
StringRef sectionName)
|
||||
: IdataAtom(context, assembleRawContent(contents)),
|
||||
_sectionName(sectionName) {}
|
||||
|
||||
virtual StringRef customSectionName() const { return _sectionName; };
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> assembleRawContent(uint32_t contents);
|
||||
StringRef _sectionName;
|
||||
};
|
||||
|
||||
/// An ImportDirectoryAtom includes information to load a DLL, including a DLL
|
||||
@ -126,12 +129,15 @@ public:
|
||||
context.importDirectories.push_back(this);
|
||||
}
|
||||
|
||||
virtual StringRef customSectionName() const { return ".idata.d"; }
|
||||
|
||||
private:
|
||||
void addRelocations(Context &context, StringRef loadName,
|
||||
const std::vector<COFFSharedLibraryAtom *> &sharedAtoms);
|
||||
void addImportTableAtoms(
|
||||
|
||||
std::vector<ImportTableEntryAtom *> createImportTableAtoms(
|
||||
Context &context, const std::vector<COFFSharedLibraryAtom *> &sharedAtoms,
|
||||
bool shouldAddReference, std::vector<ImportTableEntryAtom *> &ret) const;
|
||||
bool shouldAddReference, StringRef sectionName) const;
|
||||
HintNameAtom *createHintNameAtom(Context &context,
|
||||
const COFFSharedLibraryAtom *atom) const;
|
||||
|
||||
@ -145,6 +151,8 @@ public:
|
||||
: IdataAtom(context, std::vector<uint8_t>(20, 0)) {
|
||||
context.importDirectories.push_back(this);
|
||||
}
|
||||
|
||||
virtual StringRef customSectionName() const { return ".idata.d"; }
|
||||
};
|
||||
|
||||
// An instance of this class represents "input file" for atoms created in this
|
||||
@ -181,7 +189,6 @@ private:
|
||||
void appendAtoms(std::vector<T *> &vec1, const std::vector<U *> &vec2);
|
||||
|
||||
void connectAtoms(idata::Context &context);
|
||||
void createDataDirectoryAtoms(idata::Context &context);
|
||||
void replaceSharedLibraryAtoms(idata::Context &context);
|
||||
|
||||
// A dummy file with which all the atoms created in the pass will be
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
using llvm::support::ulittle16_t;
|
||||
using llvm::support::ulittle32_t;
|
||||
using llvm::COFF::DataDirectoryIndex;
|
||||
|
||||
namespace lld {
|
||||
namespace pecoff {
|
||||
@ -218,29 +219,20 @@ protected:
|
||||
/// header in the output file. An entry consists of an 8 byte field that
|
||||
/// indicates a relative virtual address (the starting address of the entry data
|
||||
/// in memory) and 8 byte entry data size.
|
||||
class DataDirectoryChunk : public AtomChunk {
|
||||
class DataDirectoryChunk : public HeaderChunk {
|
||||
public:
|
||||
explicit DataDirectoryChunk(const DefinedAtom *atom)
|
||||
: AtomChunk(kindDataDirectory) {
|
||||
if (atom)
|
||||
_atomLayouts.push_back(new (_alloc) AtomLayout(atom, 0, 0));
|
||||
}
|
||||
DataDirectoryChunk()
|
||||
: HeaderChunk(), _data(std::vector<llvm::object::data_directory>(16)) {}
|
||||
|
||||
virtual uint64_t size() const {
|
||||
return sizeof(llvm::object::data_directory) * 16;
|
||||
}
|
||||
|
||||
void setBaseRelocField(uint32_t addr, uint32_t size) {
|
||||
_baseRelocAddr = addr;
|
||||
_baseRelocSize = size;
|
||||
return sizeof(llvm::object::data_directory) * _data.size();
|
||||
}
|
||||
|
||||
void setField(DataDirectoryIndex index, uint32_t addr, uint32_t size);
|
||||
virtual void write(uint8_t *buffer);
|
||||
|
||||
private:
|
||||
uint32_t _baseRelocAddr;
|
||||
uint32_t _baseRelocSize;
|
||||
mutable llvm::BumpPtrAllocator _alloc;
|
||||
std::vector<llvm::object::data_directory> _data;
|
||||
};
|
||||
|
||||
/// A SectionChunk represents a section containing atoms. It consists of a
|
||||
@ -561,22 +553,15 @@ void AtomChunk::addBaseRelocations(std::vector<uint64_t> &relocSites) const {
|
||||
}
|
||||
}
|
||||
|
||||
void DataDirectoryChunk::write(uint8_t *buffer) {
|
||||
if (!_atomLayouts.empty()) {
|
||||
assert(_atomLayouts.size() == 1);
|
||||
const AtomLayout *layout = _atomLayouts[0];
|
||||
ArrayRef<uint8_t> content =
|
||||
static_cast<const DefinedAtom *>(layout->_atom)->rawContent();
|
||||
std::memcpy(buffer, content.data(), content.size());
|
||||
}
|
||||
void DataDirectoryChunk::setField(DataDirectoryIndex index, uint32_t addr,
|
||||
uint32_t size) {
|
||||
llvm::object::data_directory &dir = _data[index];
|
||||
dir.RelativeVirtualAddress = addr;
|
||||
dir.Size = size;
|
||||
}
|
||||
|
||||
// Write base relocation table entry.
|
||||
int baseRelocOffset = llvm::COFF::DataDirectoryIndex::BASE_RELOCATION_TABLE *
|
||||
sizeof(llvm::object::data_directory);
|
||||
auto *baseReloc = reinterpret_cast<llvm::object::data_directory *>(
|
||||
buffer + baseRelocOffset);
|
||||
baseReloc->RelativeVirtualAddress = _baseRelocAddr;
|
||||
baseReloc->Size = _baseRelocSize;
|
||||
void DataDirectoryChunk::write(uint8_t *buffer) {
|
||||
std::memcpy(buffer, &_data[0], size());
|
||||
}
|
||||
|
||||
void SectionChunk::appendAtom(const DefinedAtom *atom) {
|
||||
@ -842,7 +827,7 @@ StringRef chooseSectionByContent(const DefinedAtom *atom) {
|
||||
typedef std::map<StringRef, std::vector<const DefinedAtom *> > AtomVectorMap;
|
||||
|
||||
void groupAtoms(const PECOFFLinkingContext &ctx, const File &file,
|
||||
AtomVectorMap &result, const DefinedAtom *&datadir) {
|
||||
AtomVectorMap &result) {
|
||||
for (const DefinedAtom *atom : file.defined()) {
|
||||
if (atom->sectionChoice() == DefinedAtom::sectionCustomRequired) {
|
||||
StringRef section = customSectionName(atom);
|
||||
@ -850,12 +835,8 @@ void groupAtoms(const PECOFFLinkingContext &ctx, const File &file,
|
||||
continue;
|
||||
}
|
||||
if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
|
||||
if (atom->contentType() == DefinedAtom::typeDataDirectoryEntry) {
|
||||
datadir = atom;
|
||||
} else {
|
||||
StringRef section = chooseSectionByContent(atom);
|
||||
result[ctx.getOutputSectionName(section)].push_back(atom);
|
||||
}
|
||||
StringRef section = chooseSectionByContent(atom);
|
||||
result[ctx.getOutputSectionName(section)].push_back(atom);
|
||||
continue;
|
||||
}
|
||||
llvm_unreachable("Unknown section choice");
|
||||
@ -865,13 +846,12 @@ void groupAtoms(const PECOFFLinkingContext &ctx, const File &file,
|
||||
// Create all chunks that consist of the output file.
|
||||
void ExecutableWriter::build(const File &linkedFile) {
|
||||
AtomVectorMap atoms;
|
||||
const DefinedAtom *dataDirAtom = nullptr;
|
||||
groupAtoms(_PECOFFLinkingContext, linkedFile, atoms, dataDirAtom);
|
||||
groupAtoms(_PECOFFLinkingContext, linkedFile, atoms);
|
||||
|
||||
// Create file chunks and add them to the list.
|
||||
auto *dosStub = new DOSStubChunk(_PECOFFLinkingContext);
|
||||
auto *peHeader = new PEHeaderChunk(_PECOFFLinkingContext);
|
||||
auto *dataDirectory = new DataDirectoryChunk(dataDirAtom);
|
||||
auto *dataDirectory = new DataDirectoryChunk();
|
||||
auto *sectionTable = new SectionHeaderTableChunk();
|
||||
addChunk(dosStub);
|
||||
addChunk(peHeader);
|
||||
@ -894,8 +874,9 @@ void ExecutableWriter::build(const File &linkedFile) {
|
||||
baseReloc->setContents(_chunks);
|
||||
if (baseReloc->size()) {
|
||||
addSectionChunk(baseReloc, sectionTable);
|
||||
dataDirectory->setBaseRelocField(baseReloc->getVirtualAddress(),
|
||||
baseReloc->rawSize());
|
||||
dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE,
|
||||
baseReloc->getVirtualAddress(),
|
||||
baseReloc->rawSize());
|
||||
}
|
||||
}
|
||||
|
||||
@ -911,6 +892,12 @@ void ExecutableWriter::build(const File &linkedFile) {
|
||||
}
|
||||
if (section->getSectionName() == ".data")
|
||||
peHeader->setBaseOfData(section->getVirtualAddress());
|
||||
if (section->getSectionName() == ".idata.a")
|
||||
dataDirectory->setField(DataDirectoryIndex::IAT,
|
||||
section->getVirtualAddress(), section->rawSize());
|
||||
if (section->getSectionName() == ".idata.d")
|
||||
dataDirectory->setField(DataDirectoryIndex::IMPORT_TABLE,
|
||||
section->getVirtualAddress(), section->rawSize());
|
||||
}
|
||||
|
||||
// Now that we know the size and file offset of sections. Set the file
|
||||
|
@ -445,8 +445,6 @@ template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> {
|
||||
io.enumCase(value, "lto-temp", lld::DefinedAtom::typeTempLTO);
|
||||
io.enumCase(value, "compact-unwind",
|
||||
lld::DefinedAtom::typeCompactUnwindInfo);
|
||||
io.enumCase(value, "dataDirectoryEntry",
|
||||
lld::DefinedAtom::typeDataDirectoryEntry);
|
||||
io.enumCase(value, "tlv-thunk", lld::DefinedAtom::typeThunkTLV);
|
||||
io.enumCase(value, "tlv-data", lld::DefinedAtom::typeTLVInitialData);
|
||||
io.enumCase(value, "tlv-zero-fill",
|
||||
|
Loading…
x
Reference in New Issue
Block a user