forked from OSchip/llvm-project
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:
parent
c307c27035
commit
2c345a337c
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
...
|
Loading…
Reference in New Issue