#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;
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.
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;
+// 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());
+ }
+ 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));
}
- Chunk *C = new HintNameChunk(S->getExternalName(), S->getOrdinal());
- HintNameTables.push_back(C);
- LookupTables.push_back(new LookupChunk(C));
- AddressTables.push_back(new LookupChunk(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));
}
- for (int I = 0, E = Symbols.size(); I < E; ++I)
- Symbols[I]->setLocation(AddressTables[I]);
- DirTab->LookupTab = LookupTables[0];
- DirTab->AddressTab = AddressTables[0];
+ // Add null terminator.
+ Dirs.push_back(make_unique<NullChunk>(DirectoryChunk::Size));
}
} // namespace coff
// 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
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;
};
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) {}
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
}
}
-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;
+// Create .idata section contents.
+void Writer::createImportTables() {
+ if (Symtab->ImportFiles.empty())
+ return;
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);
+ 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>(B)->getChunk());
+ Text->addChunk(cast<DefinedImportThunk>(Body)->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 *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,
// 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
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;