void createMiscChunks();
void createImportTables();
void createExportTable();
+ void mergeSections();
void assignAddresses();
void removeEmptySections();
void createSymbolAndStringTable();
OutputSection *TextSec;
OutputSection *RdataSec;
OutputSection *DataSec;
+ OutputSection *PdataSec;
OutputSection *IdataSec;
OutputSection *EdataSec;
OutputSection *DidatSec;
C->setOutputSection(this);
}
-void OutputSection::addPermissions(uint32_t C) {
- Header.Characteristics |= C & PermMask;
-}
-
void OutputSection::setPermissions(uint32_t C) {
Header.Characteristics = C & PermMask;
}
+void OutputSection::merge(OutputSection *Other) {
+ for (Chunk *C : Other->Chunks)
+ C->setOutputSection(this);
+ Chunks.insert(Chunks.end(), Other->Chunks.begin(), Other->Chunks.end());
+ Other->Chunks.clear();
+}
+
// Write the section header to a given buffer.
void OutputSection::writeHeaderTo(uint8_t *Buf) {
auto *Hdr = reinterpret_cast<coff_section *>(Buf);
createMiscChunks();
createImportTables();
createExportTable();
+ mergeSections();
assignAddresses();
removeEmptySections();
setSectionPermissions();
const uint32_t W = IMAGE_SCN_MEM_WRITE;
const uint32_t X = IMAGE_SCN_MEM_EXECUTE;
- SmallDenseMap<StringRef, OutputSection *> Sections;
- auto CreateSection = [&](StringRef Name, uint32_t Perms) {
- auto I = Config->Merge.find(Name);
- if (I != Config->Merge.end())
- Name = I->second;
- OutputSection *&Sec = Sections[Name];
+ SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> Sections;
+ auto CreateSection = [&](StringRef Name, uint32_t OutChars) {
+ OutputSection *&Sec = Sections[{Name, OutChars}];
if (!Sec) {
- Sec = make<OutputSection>(Name);
+ Sec = make<OutputSection>(Name, OutChars);
OutputSections.push_back(Sec);
}
- Sec->addPermissions(Perms);
return Sec;
};
CreateSection(".bss", BSS | R | W);
RdataSec = CreateSection(".rdata", DATA | R);
DataSec = CreateSection(".data", DATA | R | W);
- CreateSection(".pdata", DATA | R);
+ PdataSec = CreateSection(".pdata", DATA | R);
IdataSec = CreateSection(".idata", DATA | R);
EdataSec = CreateSection(".edata", DATA | R);
DidatSec = CreateSection(".didat", DATA | R);
RsrcSec = CreateSection(".rsrc", DATA | R);
RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R);
- // Then bin chunks by name.
- std::map<StringRef, std::vector<Chunk *>> Map;
+ // Then bin chunks by name and output characteristics.
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map;
for (Chunk *C : Symtab->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
if (SC && !SC->isLive()) {
SC->printDiscardedMessage();
continue;
}
- Map[C->getSectionName()].push_back(C);
+ Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C);
}
// Process an /order option.
// discarded when determining output section. So, .text$foo
// contributes to .text, for example. See PE/COFF spec 3.2.
for (auto Pair : Map) {
- StringRef Name = getOutputSectionName(Pair.first);
- if (Name == ".pdata") {
- if (!FirstPdata)
- FirstPdata = Pair.second.front();
- LastPdata = Pair.second.back();
- }
- OutputSection *Sec = CreateSection(Name, 0);
+ StringRef Name = getOutputSectionName(Pair.first.first);
+ uint32_t OutChars = Pair.first.second;
+
+ // In link.exe, there is a special case for the I386 target where .CRT
+ // sections are treated as if they have output characteristics DATA | R if
+ // their characteristics are DATA | R | W. This implements the same special
+ // case for all architectures.
+ if (Name == ".CRT")
+ OutChars = DATA | R;
+
+ OutputSection *Sec = CreateSection(Name, OutChars);
std::vector<Chunk *> &Chunks = Pair.second;
- for (Chunk *C : Chunks) {
+ for (Chunk *C : Chunks)
Sec->addChunk(C);
- Sec->addPermissions(C->getOutputCharacteristics());
- }
}
// Finally, move some output sections to the end.
FileSize = alignTo(FileOff, SectorSize);
}
+void Writer::mergeSections() {
+ if (!PdataSec->getChunks().empty()) {
+ FirstPdata = PdataSec->getChunks().front();
+ LastPdata = PdataSec->getChunks().back();
+ }
+
+ for (auto &P : Config->Merge) {
+ StringRef ToName = P.second;
+ if (P.first == ToName)
+ continue;
+ StringSet<> Names;
+ while (1) {
+ if (!Names.insert(ToName).second)
+ fatal("/merge: cycle found for section '" + P.first + "'");
+ auto I = Config->Merge.find(ToName);
+ if (I == Config->Merge.end())
+ break;
+ ToName = I->second;
+ }
+ OutputSection *From = findSection(P.first);
+ OutputSection *To = findSection(ToName);
+ if (!From)
+ continue;
+ if (!To) {
+ From->Name = ToName;
+ continue;
+ }
+ To->merge(From);
+ }
+}
+
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
for (auto &P : Config->Section) {
StringRef Name = P.first;
uint32_t Perm = P.second;
- if (auto *Sec = findSection(Name))
- Sec->setPermissions(Perm);
+ for (OutputSection *Sec : OutputSections)
+ if (Sec->Name == Name)
+ Sec->setPermissions(Perm);
}
}
// non-overlapping file offsets and RVAs.
class OutputSection {
public:
- OutputSection(llvm::StringRef N) : Name(N), Header({}) {}
+ OutputSection(llvm::StringRef N, uint32_t Chars) : Name(N) {
+ Header.Characteristics = Chars;
+ }
void addChunk(Chunk *C);
+ void merge(OutputSection *Other);
ArrayRef<Chunk *> getChunks() { return Chunks; }
void addPermissions(uint32_t C);
void setPermissions(uint32_t C);
uint32_t SectionIndex = 0;
llvm::StringRef Name;
- llvm::object::coff_section Header;
+ llvm::object::coff_section Header = {};
private:
uint32_t StringTableOff = 0;
--- /dev/null
+# RUN: yaml2obj %s > %t.obj
+# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj
+# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s
+
+# CHECK: Name: .CRT
+# CHECK: Characteristics [
+# CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# CHECK-NEXT: IMAGE_SCN_MEM_READ
+# CHECK-NEXT: ]
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 010203
+# CHECK-NEXT: )
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .CRT$XCZ
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 03
+ - Name: .CRT$XCU
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 1
+ SectionData: 02
+ - Name: .CRT$XCA
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 01
+symbols:
+...
# RUN: /merge:.foo=.abc /merge:.bar=.def %t.obj /debug
# RUN: llvm-readobj -sections %t.exe | FileCheck %s
+# RUN: lld-link /out:%t.exe /entry:main /subsystem:console /force \
+# RUN: /merge:.foo=.bar /merge:.bar=.abc %t.obj /debug
+# RUN: llvm-readobj -sections %t.exe | FileCheck --check-prefix=CHECK2 %s
+
# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \
# RUN: /merge:.rsrc=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RSRC %s
# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \
# RUN: /merge:.reloc=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RELOC %s
# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \
# RUN: /merge:.foo=.reloc %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RELOC %s
+# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \
+# RUN: /merge:.foo=.foo1 /merge:.foo1=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-CYCLE %s
+# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \
+# RUN: /merge:.foo=.foo1 /merge:.foo1=.foo2 /merge:.foo2=.foo1 %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-CYCLE %s
# CHECK: Name: .def
# CHECK: Name: .abc
+# CHECK2-NOT: Name: .bar
+# CHECK2: Name: .abc
+# CHECK2-NOT: Name: .bar
+
# NO-RSRC: /merge: cannot merge '.rsrc' with any section
# NO-RELOC: /merge: cannot merge '.reloc' with any section
+# NO-CYCLE: /merge: cycle found for section '.foo'
+
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
--- /dev/null
+# RUN: yaml2obj %s > %t.obj
+# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj
+# RUN: llvm-readobj -sections %t.dll | FileCheck %s
+# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj /section:.foo,rwe
+# RUN: llvm-readobj -sections %t.dll | FileCheck --check-prefix=SECTION %s
+# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj /merge:.foo=.bar
+# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck --check-prefix=MERGE %s
+# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj /merge:.foo=.bar /section:.foo,rwe
+# RUN: llvm-readobj -sections %t.dll | FileCheck --check-prefix=MERGE-SECTION %s
+
+# CHECK: Name: .foo
+# CHECK: Characteristics [
+# CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# CHECK-NEXT: IMAGE_SCN_MEM_READ
+# CHECK-NEXT: ]
+
+# CHECK: Name: .foo
+# CHECK: Characteristics [
+# CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# CHECK-NEXT: IMAGE_SCN_MEM_READ
+# CHECK-NEXT: IMAGE_SCN_MEM_WRITE
+# CHECK-NEXT: ]
+
+# SECTION: Name: .foo
+# SECTION: Characteristics [
+# SECTION-NEXT: IMAGE_SCN_MEM_EXECUTE
+# SECTION-NEXT: IMAGE_SCN_MEM_READ
+# SECTION-NEXT: IMAGE_SCN_MEM_WRITE
+# SECTION-NEXT: ]
+
+# SECTION: Name: .foo
+# SECTION: Characteristics [
+# SECTION-NEXT: IMAGE_SCN_MEM_EXECUTE
+# SECTION-NEXT: IMAGE_SCN_MEM_READ
+# SECTION-NEXT: IMAGE_SCN_MEM_WRITE
+# SECTION-NEXT: ]
+
+# MERGE: Name: .bar
+# MERGE: Characteristics [
+# MERGE-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# MERGE-NEXT: IMAGE_SCN_MEM_READ
+# MERGE-NEXT: ]
+# MERGE-NEXT: SectionData (
+# MERGE-NEXT: 0000: 0301
+
+# MERGE: Name: .bar
+# MERGE: Characteristics [
+# MERGE-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# MERGE-NEXT: IMAGE_SCN_MEM_READ
+# MERGE-NEXT: IMAGE_SCN_MEM_WRITE
+# MERGE-NEXT: ]
+# MERGE-NEXT: SectionData (
+# MERGE-NEXT: 0000: 04
+
+# MERGE: Name: .foo
+# MERGE: Characteristics [
+# MERGE-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# MERGE-NEXT: IMAGE_SCN_MEM_READ
+# MERGE-NEXT: IMAGE_SCN_MEM_WRITE
+# MERGE-NEXT: ]
+# MERGE-NEXT: SectionData (
+# MERGE-NEXT: 0000: 02
+
+# MERGE-SECTION: Name: .bar
+# MERGE-SECTION: Characteristics [
+# MERGE-SECTION-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# MERGE-SECTION-NEXT: IMAGE_SCN_MEM_READ
+# MERGE-SECTION-NEXT: ]
+
+# MERGE-SECTION: Name: .bar
+# MERGE-SECTION: Characteristics [
+# MERGE-SECTION-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+# MERGE-SECTION-NEXT: IMAGE_SCN_MEM_READ
+# MERGE-SECTION-NEXT: IMAGE_SCN_MEM_WRITE
+# MERGE-SECTION-NEXT: ]
+
+# MERGE-SECTION: Name: .foo
+# MERGE-SECTION: Characteristics [
+# MERGE-SECTION-NEXT: IMAGE_SCN_MEM_EXECUTE
+# MERGE-SECTION-NEXT: IMAGE_SCN_MEM_READ
+# MERGE-SECTION-NEXT: IMAGE_SCN_MEM_WRITE
+# MERGE-SECTION-NEXT: ]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .foo
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 01
+ - Name: .foo
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 1
+ SectionData: 02
+ - Name: .bar
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 03
+ - Name: .bar
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 1
+ SectionData: 04
+symbols:
+...
# FIXME: llvm-readobj currently does not understand files with .pdata merged
# into .rdata. But we can at least check that the section headers look correct.
#
-# HEADER-MERGE: ExceptionTableRVA: 0x2000
+# HEADER-MERGE: ExceptionTableRVA: 0x2004
# HEADER-MERGE-NEXT: ExceptionTableSize: 0x30
# HEADER-MERGE: Name: .rdata
# HEADER-MERGE-NEXT: VirtualSize: 0x34