summaryrefslogtreecommitdiff
path: root/COFF
diff options
context:
space:
mode:
Diffstat (limited to 'COFF')
-rw-r--r--COFF/Chunks.cpp44
-rw-r--r--COFF/Config.h1
-rw-r--r--COFF/Driver.cpp17
-rw-r--r--COFF/Driver.h2
-rw-r--r--COFF/DriverUtils.cpp27
-rw-r--r--COFF/InputFiles.h3
-rw-r--r--COFF/Librarian.cpp20
-rw-r--r--COFF/ModuleDef.cpp7
-rw-r--r--COFF/SymbolTable.h5
9 files changed, 100 insertions, 26 deletions
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 10eeedd88e551..2e49f417a2064 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -319,8 +319,48 @@ void SEHTableChunk::writeTo(uint8_t *Buf) const {
std::sort(Begin, Begin + Cnt);
}
-// Windows-specific.
-// This class represents a block in .reloc section.
+// Windows-specific. This class represents a block in .reloc section.
+// The format is described here.
+//
+// On Windows, each DLL is linked against a fixed base address and
+// usually loaded to that address. However, if there's already another
+// DLL that overlaps, the loader has to relocate it. To do that, DLLs
+// contain .reloc sections which contain offsets that need to be fixed
+// up at runtime. If the loader find that a DLL cannot be loaded to its
+// desired base address, it loads it to somewhere else, and add <actual
+// base address> - <desired base address> to each offset that is
+// specified by .reloc section.
+//
+// In ELF terms, .reloc sections contain arrays of relocation offsets.
+// All these offsets in the section are implicitly R_*_RELATIVE, and
+// addends are read from section contents (so it is REL as opposed to
+// RELA).
+//
+// This already reduce the size of relocations to 1/3 compared to ELF
+// .dynrel, but Windows does more to reduce it (probably because it was
+// invented for PCs in the late '80s or early '90s.) Offsets in .reloc
+// are grouped by page where page size is 16 bits, and offsets sharing
+// the same page address are stored consecutively to represent them with
+// less space. This is a very similar to the page table which is grouped
+// by (multiple stages of) pages.
+//
+// For example, let's say we have 0x00030, 0x00500, 0x01000, 0x01100,
+// 0x20004, and 0x20008 in a .reloc section. In the section, they are
+// represented like this:
+//
+// 0x00000 -- page address (4 bytes)
+// 16 -- size of this block (4 bytes)
+// 0x0030 -- entries (2 bytes each)
+// 0x0500
+// 0x1000
+// 0x1100
+// 0x20000 -- page address (4 bytes)
+// 12 -- size of this block (4 bytes)
+// 0x0004 -- entries (2 bytes each)
+// 0x0008
+//
+// Usually we have a lot of relocatinos for each page, so the number of
+// bytes for one .reloc entry is close to 2 bytes.
BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) {
// Block header consists of 4 byte page RVA and 4 byte block size.
// Each entry is 2 byte. Last entry may be padding.
diff --git a/COFF/Config.h b/COFF/Config.h
index 31534aeb39719..fafd3bcde2e32 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -43,6 +43,7 @@ struct Export {
bool Noname = false;
bool Data = false;
bool Private = false;
+ bool Constant = false;
// If an export is a form of /export:foo=dllname.bar, that means
// that foo should be exported as an alias to bar in the DLL.
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index daddfb86d4cf2..5a15b5b11507e 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -512,6 +512,23 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
std::string Rsp = "/nologo\n";
std::vector<std::string> Temps;
+ // Write out archive members that we used in symbol resolution and pass these
+ // to MSVC before any archives, so that MSVC uses the same objects to satisfy
+ // references.
+ for (const auto *O : Symtab.ObjectFiles) {
+ if (O->ParentName.empty())
+ continue;
+ SmallString<128> S;
+ int Fd;
+ if (auto EC = sys::fs::createTemporaryFile(
+ "lld-" + sys::path::filename(O->ParentName), ".obj", Fd, S))
+ fatal(EC, "cannot create a temporary file");
+ raw_fd_ostream OS(Fd, /*shouldClose*/ true);
+ OS << O->MB.getBuffer();
+ Temps.push_back(S.str());
+ Rsp += quote(S) + "\n";
+ }
+
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
case OPT_linkrepro:
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 4566f73eef318..ad725625e030f 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -48,7 +48,7 @@ public:
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
// Concatenate LINK environment varirable and given arguments and parse them.
- llvm::opt::InputArgList parseLINK(llvm::ArrayRef<const char *> Args);
+ llvm::opt::InputArgList parseLINK(std::vector<const char *> Args);
// Tokenizes a given string and then parses as command line options.
llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 2c9ba797f73b3..252590c7d8703 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -479,6 +479,10 @@ Export parseExport(StringRef Arg) {
E.Data = true;
continue;
}
+ if (Tok.equals_lower("constant")) {
+ E.Constant = true;
+ continue;
+ }
if (Tok.equals_lower("private")) {
E.Private = true;
continue;
@@ -695,17 +699,20 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
return Args;
}
-// link.exe has an interesting feature. If LINK environment exists,
-// its contents are handled as a command line string. So you can pass
-// extra arguments using the environment variable.
-opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
+// link.exe has an interesting feature. If LINK or _LINK_ environment
+// variables exist, their contents are handled as command line strings.
+// So you can pass extra arguments using them.
+opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Args) {
// Concatenate LINK env and command line arguments, and then parse them.
- Optional<std::string> Env = Process::GetEnv("LINK");
- if (!Env)
- return parse(Args);
- std::vector<const char *> V = tokenize(*Env);
- V.insert(V.end(), Args.begin(), Args.end());
- return parse(V);
+ if (Optional<std::string> S = Process::GetEnv("LINK")) {
+ std::vector<const char *> V = tokenize(*S);
+ Args.insert(Args.begin(), V.begin(), V.end());
+ }
+ if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
+ std::vector<const char *> V = tokenize(*S);
+ Args.insert(Args.begin(), V.begin(), V.end());
+ }
+ return parse(Args);
}
std::vector<const char *> ArgParser::tokenize(StringRef S) {
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 9e02b2fc68bb8..3078de687525d 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -58,6 +58,8 @@ public:
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
+ MemoryBufferRef MB;
+
// An archive file name if this file is created from an archive.
StringRef ParentName;
@@ -67,7 +69,6 @@ public:
protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
- MemoryBufferRef MB;
std::string Directives;
private:
diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp
index 3ce72822180b0..91316ee6b0c91 100644
--- a/COFF/Librarian.cpp
+++ b/COFF/Librarian.cpp
@@ -162,7 +162,7 @@ public:
// Create a short import file which is described in PE/COFF spec 7. Import
// Library Format.
NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
- ImportNameType NameType, bool isData);
+ ImportType Type, ImportNameType NameType);
};
}
@@ -440,8 +440,8 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
uint16_t Ordinal,
- ImportNameType NameType,
- bool isData) {
+ ImportType ImportType,
+ ImportNameType NameType) {
size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs
size_t Size = sizeof(coff_import_header) + ImpSize;
char *Buf = Alloc.Allocate<char>(Size);
@@ -456,8 +456,7 @@ NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
Imp->SizeOfData = ImpSize;
if (Ordinal > 0)
Imp->OrdinalHint = Ordinal;
- Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE);
- Imp->TypeInfo |= NameType << 2;
+ Imp->TypeInfo = (NameType << 2) | ImportType;
// Write symbol name and DLL name.
memcpy(P, Sym.data(), Sym.size());
@@ -490,11 +489,18 @@ void lld::coff::writeImportLibrary() {
if (E.Private)
continue;
- ImportNameType Type = getNameType(E.SymbolName, E.Name);
+ ImportType ImportType = IMPORT_CODE;
+ if (E.Data)
+ ImportType = IMPORT_DATA;
+ if (E.Constant)
+ ImportType = IMPORT_CONST;
+
+ ImportNameType NameType = getNameType(E.SymbolName, E.Name);
std::string Name = E.ExtName.empty()
? std::string(E.SymbolName)
: replace(E.SymbolName, E.Name, E.ExtName);
- Members.push_back(OF.createShortImport(Name, E.Ordinal, Type, E.Data));
+ Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
+ NameType));
}
std::pair<StringRef, std::error_code> Result =
diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp
index c9a40ac5ab8c6..740ce867a7c4a 100644
--- a/COFF/ModuleDef.cpp
+++ b/COFF/ModuleDef.cpp
@@ -38,6 +38,7 @@ enum Kind {
Comma,
Equal,
KwBase,
+ KwConstant,
KwData,
KwExports,
KwHeapsize,
@@ -92,6 +93,7 @@ public:
StringRef Word = Buf.substr(0, End);
Kind K = llvm::StringSwitch<Kind>(Word)
.Case("BASE", KwBase)
+ .Case("CONSTANT", KwConstant)
.Case("DATA", KwData)
.Case("EXPORTS", KwExports)
.Case("HEAPSIZE", KwHeapsize)
@@ -227,6 +229,11 @@ private:
E.Data = true;
continue;
}
+ if (Tok.K == KwConstant) {
+ warn("CONSTANT keyword is obsolete; use DATA");
+ E.Constant = true;
+ continue;
+ }
if (Tok.K == KwPrivate) {
E.Private = true;
continue;
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 764dd53187750..bf8d6618d964a 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -108,14 +108,9 @@ public:
std::vector<Chunk *> LocalImportChunks;
private:
- void readArchive();
- void readObjects();
-
std::pair<Symbol *, bool> insert(StringRef Name);
StringRef findByPrefix(StringRef Prefix);
- void addCombinedLTOObject(ObjectFile *Obj);
-
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
std::vector<BitcodeFile *> BitcodeFiles;