COFF: Move .idata constructor from Writer to Chunk.

Previously, half of the constructor for .idata contents was in Chunks.cpp
and the rest was in Writer.cpp. This patch moves the latter to Chunks.cpp.
Now IdataContents class manages everything for .idata section.

llvm-svn: 239230
This commit is contained in:
Rui Ueyama 2015-06-06 22:46:15 +00:00
parent 3f0fb98d01
commit c6ea057d7f
4 changed files with 126 additions and 119 deletions

View File

@ -17,6 +17,7 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::COFF;
@ -25,6 +26,9 @@ using llvm::RoundUpToAlignment;
namespace lld {
namespace coff {
const size_t LookupChunk::Size = sizeof(uint64_t);
const size_t DirectoryChunk::Size = sizeof(ImportDirectoryTableEntry);
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H, uint32_t SI)
: File(F), Header(H), SectionIndex(SI) {
// Initialize SectionName.
@ -196,31 +200,80 @@ void DirectoryChunk::writeTo(uint8_t *Buf) {
E->ImportAddressTableRVA = AddressTab->getRVA();
}
ImportTable::ImportTable(StringRef N,
std::vector<DefinedImportData *> &Symbols) {
// Create the import table hader.
DLLName = new StringChunk(N);
DirTab = new DirectoryChunk(DLLName);
// Create lookup and address tables. If they have external names,
// we need to create HintName chunks to store the names.
// If they don't (if they are import-by-ordinals), we store only
// ordinal values to the table.
for (DefinedImportData *S : Symbols) {
if (S->getExternalName().empty()) {
LookupTables.push_back(new OrdinalOnlyChunk(S->getOrdinal()));
AddressTables.push_back(new OrdinalOnlyChunk(S->getOrdinal()));
continue;
}
Chunk *C = new HintNameChunk(S->getExternalName(), S->getOrdinal());
HintNameTables.push_back(C);
LookupTables.push_back(new LookupChunk(C));
AddressTables.push_back(new LookupChunk(C));
// Returns a list of .idata contents.
// See Microsoft PE/COFF spec 5.4 for details.
std::vector<Chunk *> IdataContents::getChunks() {
create();
std::vector<Chunk *> V;
// The loader assumes a specific order of data.
// Add each type in the correct order.
for (std::unique_ptr<Chunk> &C : Dirs)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Lookups)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Addresses)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Hints)
V.push_back(C.get());
for (auto &P : DLLNames) {
std::unique_ptr<Chunk> &C = P.second;
V.push_back(C.get());
}
for (int I = 0, E = Symbols.size(); I < E; ++I)
Symbols[I]->setLocation(AddressTables[I]);
DirTab->LookupTab = LookupTables[0];
DirTab->AddressTab = AddressTables[0];
return V;
}
void IdataContents::create() {
// Group DLL-imported symbols by DLL name because that's how
// symbols are layed out in the import descriptor table.
std::map<StringRef, std::vector<DefinedImportData *>> Map;
for (DefinedImportData *Sym : Imports)
Map[Sym->getDLLName()].push_back(Sym);
// Create .idata contents for each DLL.
for (auto &P : Map) {
StringRef Name = P.first;
std::vector<DefinedImportData *> &Syms = P.second;
// Sort symbols by name for each group.
std::sort(Syms.begin(), Syms.end(),
[](DefinedImportData *A, DefinedImportData *B) {
return A->getName() < B->getName();
});
// Create lookup and address tables. If they have external names,
// we need to create HintName chunks to store the names.
// If they don't (if they are import-by-ordinals), we store only
// ordinal values to the table.
size_t Base = Lookups.size();
for (DefinedImportData *S : Syms) {
uint16_t Ord = S->getOrdinal();
if (S->getExternalName().empty()) {
Lookups.push_back(make_unique<OrdinalOnlyChunk>(Ord));
Addresses.push_back(make_unique<OrdinalOnlyChunk>(Ord));
continue;
}
auto C = make_unique<HintNameChunk>(S->getExternalName(), Ord);
Lookups.push_back(make_unique<LookupChunk>(C.get()));
Addresses.push_back(make_unique<LookupChunk>(C.get()));
Hints.push_back(std::move(C));
}
// Terminate with null values.
Lookups.push_back(make_unique<NullChunk>(sizeof(uint64_t)));
Addresses.push_back(make_unique<NullChunk>(sizeof(uint64_t)));
for (int I = 0, E = Syms.size(); I < E; ++I)
Syms[I]->setLocation(Addresses[Base + I].get());
// Create the import table header.
if (!DLLNames.count(Name))
DLLNames[Name] = make_unique<StringChunk>(Name);
auto Dir = make_unique<DirectoryChunk>(DLLNames[Name].get());
Dir->LookupTab = Lookups[Base].get();
Dir->AddressTab = Addresses[Base].get();
Dirs.push_back(std::move(Dir));
}
// Add null terminator.
Dirs.push_back(make_unique<NullChunk>(DirectoryChunk::Size));
}
} // namespace coff

View File

@ -169,6 +169,8 @@ private:
// All chunks below are for the DLL import descriptor table and
// Windows-specific. You may need to read the Microsoft PE/COFF spec
// to understand details about the data structures.
// If you are not particularly interested, you can skip them and
// still be able to understand the rest of the linker.
static const uint8_t ImportThunkData[] = {
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0
@ -202,8 +204,9 @@ private:
class LookupChunk : public Chunk {
public:
explicit LookupChunk(Chunk *C) : HintName(C) {}
size_t getSize() const override { return sizeof(uint64_t); }
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) override;
static const size_t Size;
Chunk *HintName;
};
@ -222,17 +225,16 @@ public:
class DirectoryChunk : public Chunk {
public:
explicit DirectoryChunk(Chunk *N) : DLLName(N) {}
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) override;
static const size_t Size;
Chunk *DLLName;
Chunk *LookupTab;
Chunk *AddressTab;
};
// A chunk for the import descriptor table representing.
// A chunk representing null terminator in the import table.
// Contents of this chunk is always null bytes.
// This is used for terminating import tables.
class NullChunk : public Chunk {
public:
explicit NullChunk(size_t N) : Size(N) {}
@ -243,16 +245,28 @@ private:
size_t Size;
};
// ImportTable creates a set of import table chunks for a given
// DLL-imported symbols.
class ImportTable {
// IdataContents creates all chunks for the .idata section.
// You are supposed to call add() to add symbols and then
// call getChunks() to get a list of chunks.
class IdataContents {
public:
ImportTable(StringRef DLLName, std::vector<DefinedImportData *> &Symbols);
StringChunk *DLLName;
DirectoryChunk *DirTab;
std::vector<Chunk *> LookupTables;
std::vector<Chunk *> AddressTables;
std::vector<Chunk *> HintNameTables;
void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
std::vector<Chunk *> getChunks();
uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
uint64_t getDirSize() { return Dirs.size() * DirectoryChunk::Size; }
uint64_t getIATRVA() { return Addresses[0]->getRVA(); }
uint64_t getIATSize() { return Addresses.size() * LookupChunk::Size; }
private:
void create();
std::vector<DefinedImportData *> Imports;
std::vector<std::unique_ptr<Chunk>> Dirs;
std::vector<std::unique_ptr<Chunk>> Lookups;
std::vector<std::unique_ptr<Chunk>> Addresses;
std::vector<std::unique_ptr<Chunk>> Hints;
std::map<StringRef, std::unique_ptr<Chunk>> DLLNames;
};
} // namespace coff

View File

@ -128,83 +128,26 @@ void Writer::createSections() {
}
}
std::map<StringRef, std::vector<DefinedImportData *>> Writer::binImports() {
// Group DLL-imported symbols by DLL name because that's how symbols
// are layed out in the import descriptor table.
std::map<StringRef, std::vector<DefinedImportData *>> Res;
OutputSection *Text = createSection(".text");
for (std::unique_ptr<ImportFile> &P : Symtab->ImportFiles) {
for (SymbolBody *B : P->getSymbols()) {
if (auto *Import = dyn_cast<DefinedImportData>(B)) {
Res[Import->getDLLName()].push_back(Import);
continue;
}
// Linker-created function thunks for DLL symbols are added to
// .text section.
Text->addChunk(cast<DefinedImportThunk>(B)->getChunk());
}
}
// Sort symbols by name for each group.
auto Comp = [](DefinedImportData *A, DefinedImportData *B) {
return A->getName() < B->getName();
};
for (auto &P : Res) {
std::vector<DefinedImportData *> &V = P.second;
std::sort(V.begin(), V.end(), Comp);
}
return Res;
}
// Create .idata section contents.
void Writer::createImportTables() {
if (Symtab->ImportFiles.empty())
return;
std::vector<ImportTable> Tabs;
for (auto &P : binImports()) {
StringRef DLLName = P.first;
std::vector<DefinedImportData *> &Imports = P.second;
Tabs.emplace_back(DLLName, Imports);
OutputSection *Text = createSection(".text");
Idata.reset(new IdataContents());
for (std::unique_ptr<ImportFile> &File : Symtab->ImportFiles) {
for (SymbolBody *Body : File->getSymbols()) {
if (auto *Import = dyn_cast<DefinedImportData>(Body)) {
Idata->add(Import);
continue;
}
// Linker-created function thunks for DLL symbols are added to
// .text section.
Text->addChunk(cast<DefinedImportThunk>(Body)->getChunk());
}
}
OutputSection *Idata = createSection(".idata");
size_t NumChunks = Idata->getChunks().size();
// Add the directory tables.
for (ImportTable &T : Tabs)
Idata->addChunk(T.DirTab);
Idata->addChunk(new NullChunk(sizeof(ImportDirectoryTableEntry)));
ImportDirectoryTableSize = (Tabs.size() + 1) * sizeof(ImportDirectoryTableEntry);
// Add the import lookup tables.
for (ImportTable &T : Tabs) {
for (Chunk *C : T.LookupTables)
Idata->addChunk(C);
Idata->addChunk(new NullChunk(sizeof(uint64_t)));
}
// Add the import address tables. Their contents are the same as the
// lookup tables.
for (ImportTable &T : Tabs) {
for (Chunk *C : T.AddressTables)
Idata->addChunk(C);
Idata->addChunk(new NullChunk(sizeof(uint64_t)));
ImportAddressTableSize += (T.AddressTables.size() + 1) * sizeof(uint64_t);
}
ImportAddressTable = Tabs[0].AddressTables[0];
// Add the hint name table.
for (ImportTable &T : Tabs)
for (Chunk *C : T.HintNameTables)
Idata->addChunk(C);
// Add DLL names.
for (ImportTable &T : Tabs)
Idata->addChunk(T.DLLName);
// Claim ownership of all chunks in the .idata section.
for (size_t I = NumChunks, E = Idata->getChunks().size(); I < E; ++I)
Chunks.push_back(std::unique_ptr<Chunk>(Idata->getChunks()[I]));
OutputSection *Sec = createSection(".idata");
for (Chunk *C : Idata->getChunks())
Sec->addChunk(C);
}
// The Windows loader doesn't seem to like empty sections,
@ -307,12 +250,11 @@ void Writer::writeHeader() {
// Write data directory
auto *DataDirectory = reinterpret_cast<data_directory *>(Buf);
Buf += sizeof(*DataDirectory) * NumberfOfDataDirectory;
if (OutputSection *Idata = findSection(".idata")) {
using namespace llvm::COFF;
DataDirectory[IMPORT_TABLE].RelativeVirtualAddress = Idata->getRVA();
DataDirectory[IMPORT_TABLE].Size = ImportDirectoryTableSize;
DataDirectory[IAT].RelativeVirtualAddress = ImportAddressTable->getRVA();
DataDirectory[IAT].Size = ImportAddressTableSize;
if (Idata) {
DataDirectory[IMPORT_TABLE].RelativeVirtualAddress = Idata->getDirRVA();
DataDirectory[IMPORT_TABLE].Size = Idata->getDirSize();
DataDirectory[IAT].RelativeVirtualAddress = Idata->getIATRVA();
DataDirectory[IAT].Size = Idata->getIATSize();
}
// Section table

View File

@ -94,9 +94,7 @@ private:
std::unique_ptr<llvm::FileOutputBuffer> Buffer;
llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
std::vector<OutputSection *> OutputSections;
Chunk *ImportAddressTable = nullptr;
uint32_t ImportDirectoryTableSize = 0;
uint32_t ImportAddressTableSize = 0;
std::unique_ptr<IdataContents> Idata;
uint64_t FileSize;
uint64_t SizeOfImage;