COFF: Emit a symbol table if /debug is specified

Providing a symbol table in the executable is quite useful when
debugging a fully-linked executable without having to reconstruct one
from DWARF.

Differential Revision: http://reviews.llvm.org/D11023

llvm-svn: 241689
This commit is contained in:
David Majnemer 2015-07-08 16:37:50 +00:00
parent c307c27035
commit 2c345a337c
6 changed files with 185 additions and 40 deletions

View File

@ -60,7 +60,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, uint64_t S,
case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break;
case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
case IMAGE_REL_AMD64_SECTION: add16(Off, Out->getSectionIndex() + 1); break;
case IMAGE_REL_AMD64_SECTION: add16(Off, Out->SectionIndex); break;
case IMAGE_REL_AMD64_SECREL: add32(Off, S - Out->getRVA()); break;
default:
llvm::report_fatal_error("Unsupported relocation type");
@ -74,7 +74,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, uint64_t S,
case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
case IMAGE_REL_I386_SECTION: add16(Off, Out->getSectionIndex() + 1); break;
case IMAGE_REL_I386_SECTION: add16(Off, Out->SectionIndex); break;
case IMAGE_REL_I386_SECREL: add32(Off, S - Out->getRVA()); break;
default:
llvm::report_fatal_error("Unsupported relocation type");

View File

@ -141,7 +141,8 @@ std::error_code ObjectFile::initializeChunks() {
Directives = std::string((const char *)Data.data(), Data.size());
continue;
}
if (Name.startswith(".debug"))
// We want to preserve DWARF debug sections only when /debug is on.
if (!Config->Debug && Name.startswith(".debug"))
continue;
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
continue;

View File

@ -50,6 +50,7 @@ std::error_code Writer::write(StringRef OutputPath) {
createSection(".reloc");
assignAddresses();
removeEmptySections();
createSymbolAndStringTable();
if (auto EC = openFile(OutputPath))
return EC;
if (Is64) {
@ -104,8 +105,9 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
// If name is too long, write offset into the string table as a name.
sprintf(Hdr->Name, "/%d", StringTableOff);
} else {
assert(Name.size() <= COFF::NameSize);
strncpy(Hdr->Name, Name.data(), Name.size());
assert(!Config->Debug || Name.size() <= COFF::NameSize);
strncpy(Hdr->Name, Name.data(),
std::min(Name.size(), (size_t)COFF::NameSize));
}
}
@ -200,8 +202,7 @@ void Writer::createSections() {
StringRef Name = getOutputSection(Pair.first);
OutputSection *&Sec = Sections[Name];
if (!Sec) {
size_t SectIdx = OutputSections.size();
Sec = new (CAlloc.Allocate()) OutputSection(Name, SectIdx);
Sec = new (CAlloc.Allocate()) OutputSection(Name);
OutputSections.push_back(Sec);
}
std::vector<Chunk *> &Chunks = Pair.second;
@ -280,6 +281,75 @@ void Writer::removeEmptySections() {
OutputSections.erase(
std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty),
OutputSections.end());
uint32_t Idx = 1;
for (OutputSection *Sec : OutputSections)
Sec->SectionIndex = Idx++;
}
size_t Writer::addEntryToStringTable(StringRef Str) {
assert(Str.size() > COFF::NameSize);
size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field
Strtab.insert(Strtab.end(), Str.begin(), Str.end());
Strtab.push_back('\0');
return OffsetOfEntry;
}
void Writer::createSymbolAndStringTable() {
if (!Config->Debug)
return;
// Name field in the section table is 8 byte long. Longer names need
// to be written to the string table. First, construct string table.
for (OutputSection *Sec : OutputSections) {
StringRef Name = Sec->getName();
if (Name.size() <= COFF::NameSize)
continue;
Sec->setStringTableOff(addEntryToStringTable(Name));
}
for (ObjectFile *File : Symtab->ObjectFiles) {
for (SymbolBody *B : File->getSymbols()) {
auto *D = dyn_cast<DefinedRegular>(B);
if (!D || !D->isLive())
continue;
uint64_t RVA = D->getRVA();
OutputSection *SymSec = nullptr;
for (OutputSection *Sec : OutputSections) {
if (Sec->getRVA() > RVA)
break;
SymSec = Sec;
}
uint64_t SectionRVA = SymSec->getRVA();
uint64_t SymbolValue = RVA - SectionRVA;
StringRef Name = D->getName();
coff_symbol16 Sym;
if (Name.size() > COFF::NameSize) {
Sym.Name.Offset.Zeroes = 0;
Sym.Name.Offset.Offset = addEntryToStringTable(Name);
} else {
memset(Sym.Name.ShortName, 0, COFF::NameSize);
memcpy(Sym.Name.ShortName, Name.data(), Name.size());
}
Sym.Value = SymbolValue;
Sym.SectionNumber = SymSec->SectionIndex;
Sym.StorageClass = IMAGE_SYM_CLASS_NULL;
Sym.NumberOfAuxSymbols = 0;
OutputSymtab.push_back(Sym);
}
}
OutputSection *LastSection = OutputSections.back();
// We position the symbol table to be adjacent to the end of the last section.
uint64_t FileOff =
LastSection->getFileOff() +
RoundUpToAlignment(LastSection->getRawSize(), FileAlignment);
if (!OutputSymtab.empty()) {
PointerToSymbolTable = FileOff;
FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
}
if (!Strtab.empty())
FileOff += Strtab.size() + 4;
FileSize = SizeOfHeaders +
RoundUpToAlignment(FileOff - SizeOfHeaders, FileAlignment);
}
// Visits all sections to assign incremental, non-overlapping RVAs and
@ -430,39 +500,25 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
}
}
// Section table
// Name field in the section table is 8 byte long. Longer names need
// to be written to the string table. First, construct string table.
std::vector<char> Strtab;
for (OutputSection *Sec : OutputSections) {
StringRef Name = Sec->getName();
if (Name.size() <= COFF::NameSize)
continue;
Sec->setStringTableOff(Strtab.size() + 4); // +4 for the size field
Strtab.insert(Strtab.end(), Name.begin(), Name.end());
Strtab.push_back('\0');
}
// Write section table
for (OutputSection *Sec : OutputSections) {
Sec->writeHeaderTo(Buf);
Buf += sizeof(coff_section);
}
// Write string table if we need to. The string table immediately
// follows the symbol table, so we create a dummy symbol table
// first. The symbol table contains one dummy symbol.
if (Strtab.empty())
if (OutputSymtab.empty())
return;
COFF->PointerToSymbolTable = Buf - Buffer->getBufferStart();
COFF->NumberOfSymbols = 1;
auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(Buf);
Buf += sizeof(*SymbolTable);
// (Set 4 to make the dummy symbol point to the first string table
// entry, so that tools to print out symbols don't read NUL bytes.)
SymbolTable->Name.Offset.Offset = 4;
// Then create the symbol table. The first 4 bytes is length
// including itself.
COFF->PointerToSymbolTable = PointerToSymbolTable;
uint32_t NumberOfSymbols = OutputSymtab.size();
COFF->NumberOfSymbols = NumberOfSymbols;
auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(
Buffer->getBufferStart() + COFF->PointerToSymbolTable);
for (size_t I = 0; I != NumberOfSymbols; ++I)
SymbolTable[I] = OutputSymtab[I];
// Create the string table, it follows immediately after the symbol table.
// The first 4 bytes is length including itself.
Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]);
write32le(Buf, Strtab.size() + 4);
memcpy(Buf + 4, Strtab.data(), Strtab.size());
}
@ -540,8 +596,7 @@ OutputSection *Writer::createSection(StringRef Name) {
.Default(0);
if (!Perms)
llvm_unreachable("unknown section name");
size_t SectIdx = OutputSections.size();
auto Sec = new (CAlloc.Allocate()) OutputSection(Name, SectIdx);
auto Sec = new (CAlloc.Allocate()) OutputSection(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
return Sec;

View File

@ -34,13 +34,11 @@ void doICF(const std::vector<Chunk *> &Chunks);
// non-overlapping file offsets and RVAs.
class OutputSection {
public:
OutputSection(StringRef N, uint32_t SI)
: Name(N), SectionIndex(SI), Header({}) {}
OutputSection(StringRef N) : Name(N), Header({}) {}
void setRVA(uint64_t);
void setFileOffset(uint64_t);
void addChunk(Chunk *C);
StringRef getName() { return Name; }
uint64_t getSectionIndex() { return SectionIndex; }
std::vector<Chunk *> &getChunks() { return Chunks; }
void addPermissions(uint32_t C);
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
@ -63,9 +61,11 @@ public:
// Used only when the name is longer than 8 bytes.
void setStringTableOff(uint32_t V) { StringTableOff = V; }
// N.B. The section index is one based.
uint32_t SectionIndex = 0;
private:
StringRef Name;
uint32_t SectionIndex;
coff_section Header;
uint32_t StringTableOff = 0;
std::vector<Chunk *> Chunks;
@ -86,6 +86,8 @@ private:
void createExportTable();
void assignAddresses();
void removeEmptySections();
void createSymbolAndStringTable();
size_t addEntryToStringTable(StringRef Str);
std::error_code openFile(StringRef OutputPath);
template <typename PEHeaderTy> void writeHeader();
void writeSections();
@ -105,12 +107,15 @@ private:
llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
IdataContents Idata;
DelayLoadContents DelayIdata;
EdataContents Edata;
bool Is64;
uint64_t FileSize;
uint32_t PointerToSymbolTable = 0;
uint64_t SizeOfImage;
uint64_t SizeOfHeaders;

View File

@ -1,5 +1,5 @@
# RUN: yaml2obj < %s > %t.obj
# RUN: lld -flavor link2 /out:%t.exe /entry:main %t.obj
# RUN: lld -flavor link2 /debug /out:%t.exe /entry:main %t.obj
# RUN: llvm-readobj -sections %t.exe | FileCheck %s
# CHECK: Name: .data_long_section_name

84
lld/test/COFF/symtab.test Normal file
View File

@ -0,0 +1,84 @@
# RUN: yaml2obj < %s > %t.obj
# RUN: lld -flavor link2 /debug /out:%t.exe /entry:main %t.obj
# RUN: llvm-readobj -symbols %t.exe | FileCheck %s
# CHECK: Symbols [
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: .text
# CHECK-NEXT: Value: 0
# CHECK-NEXT: Section: .text
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
# CHECK-NEXT: StorageClass: Null (0x0)
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: f
# CHECK-NEXT: Value: 2
# CHECK-NEXT: Section: .text
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
# CHECK-NEXT: StorageClass: Null (0x0)
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: g
# CHECK-NEXT: Value: 4
# CHECK-NEXT: Section: .text
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
# CHECK-NEXT: StorageClass: Null (0x0)
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: main
# CHECK-NEXT: Value: 0
# CHECK-NEXT: Section: .text
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
# CHECK-NEXT: StorageClass: Null (0x0)
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: ]
---
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: B82A00
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 6
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- Name: f
Value: 2
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: g
Value: 4
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: main
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...