Index: lld/COFF/Config.h =================================================================== --- lld/COFF/Config.h +++ lld/COFF/Config.h @@ -88,6 +88,7 @@ bool Relocatable = true; bool Force = false; bool Debug = false; + bool DebugDwarf = false; unsigned DebugTypes = static_cast(DebugType::None); llvm::SmallString<128> PDBPath; std::vector Argv; Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -1012,6 +1012,7 @@ Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); + Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf); Config->MapFile = getMapFile(Args); Index: lld/COFF/Symbols.h =================================================================== --- lld/COFF/Symbols.h +++ lld/COFF/Symbols.h @@ -77,7 +77,8 @@ friend SymbolTable; explicit Symbol(Kind K, StringRef N = "") : SymbolKind(K), IsExternal(true), IsCOMDAT(false), - PendingArchiveLoad(false), IsGCRoot(false), Name(N) {} + WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false), + Name(N) {} const unsigned SymbolKind : 8; unsigned IsExternal : 1; @@ -86,6 +87,10 @@ unsigned IsCOMDAT : 1; public: + // This bit is used by Writer::createSymbolAndStringTable() to prevent + // symbols from being written to the symbol table more than once. + unsigned WrittenToSymtab : 1; + // True if this symbol was referenced by a regular (non-bitcode) object. unsigned IsUsedInRegularObj : 1; Index: lld/COFF/Writer.cpp =================================================================== --- lld/COFF/Writer.cpp +++ lld/COFF/Writer.cpp @@ -118,7 +118,7 @@ void createExportTable(); void assignAddresses(); void removeEmptySections(); - void createStringTable(); + void createSymbolAndStringTable(); void openFile(StringRef OutputPath); template void writeHeader(); void createSEHTable(OutputSection *RData); @@ -127,6 +127,9 @@ void writeBuildId(); void sortExceptionTable(); + llvm::Optional createSymbol(Defined *D); + size_t addEntryToStringTable(StringRef Str); + OutputSection *findSection(StringRef Name); OutputSection *createSection(StringRef Name); void addBaserels(OutputSection *Dest); @@ -151,7 +154,7 @@ ArrayRef SectionTable; uint64_t FileSize; - uint32_t PointerToStringTable = 0; + uint32_t PointerToSymbolTable = 0; uint64_t SizeOfImage; uint64_t SizeOfHeaders; }; @@ -290,7 +293,7 @@ assignAddresses(); removeEmptySections(); setSectionPermissions(); - createStringTable(); + createSymbolAndStringTable(); // We must do this before opening the output file, as it depends on being able // to read the contents of the existing output file. @@ -467,7 +470,69 @@ Sec->SectionIndex = Idx++; } -void Writer::createStringTable() { +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; +} + +Optional Writer::createSymbol(Defined *Def) { + // Relative symbols are unrepresentable in a COFF symbol table. + if (isa(Def)) + return None; + + // Don't write dead symbols or symbols in codeview sections to the symbol + // table. + if (!Def->isLive()) + return None; + if (auto *D = dyn_cast(Def)) + if (D->getChunk()->isCodeView()) + return None; + + coff_symbol16 Sym; + StringRef Name = Def->getName(); + 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()); + } + + if (auto *D = dyn_cast(Def)) { + COFFSymbolRef Ref = D->getCOFFSymbol(); + Sym.Type = Ref.getType(); + Sym.StorageClass = Ref.getStorageClass(); + } else { + Sym.Type = IMAGE_SYM_TYPE_NULL; + Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; + } + Sym.NumberOfAuxSymbols = 0; + + switch (Def->kind()) { + case Symbol::DefinedAbsoluteKind: + Sym.Value = Def->getRVA(); + Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + break; + default: { + uint64_t RVA = Def->getRVA(); + OutputSection *Sec = nullptr; + for (OutputSection *S : OutputSections) { + if (S->getRVA() > RVA) + break; + Sec = S; + } + Sym.Value = RVA - Sec->getRVA(); + Sym.SectionNumber = Sec->SectionIndex; + break; + } + } + return Sym; +} + +void Writer::createSymbolAndStringTable() { // 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) { @@ -481,19 +546,34 @@ // to libunwind. if ((Sec->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - Sec->setStringTableOff(Strtab.size() + 4); // +4 for the size field - Strtab.insert(Strtab.end(), Name.begin(), Name.end()); - Strtab.push_back('\0'); + Sec->setStringTableOff(addEntryToStringTable(Name)); + } + + if (Config->DebugDwarf) { + for (ObjFile *File : ObjFile::Instances) { + for (Symbol *B : File->getSymbols()) { + auto *D = dyn_cast_or_null(B); + if (!D || D->WrittenToSymtab) + continue; + D->WrittenToSymtab = true; + + if (Optional Sym = createSymbol(D)) + OutputSymtab.push_back(*Sym); + } + } } - if (Strtab.empty()) + if (OutputSymtab.empty() && Strtab.empty()) return; OutputSection *LastSection = OutputSections.back(); - // We position the string table to be adjacent to the end of the last section. - PointerToStringTable = LastSection->getFileOff() + - alignTo(LastSection->getRawSize(), SectorSize); - FileSize = alignTo(PointerToStringTable + Strtab.size() + 4, SectorSize); + // We position the symbol table to be adjacent to the end of the last section. + uint64_t FileOff = LastSection->getFileOff() + + alignTo(LastSection->getRawSize(), SectorSize); + PointerToSymbolTable = FileOff; + FileOff += OutputSymtab.size() * sizeof(coff_symbol16); + FileOff += 4 + Strtab.size(); + FileSize = alignTo(FileOff, SectorSize); } // Visits all sections to assign incremental, non-overlapping RVAs and @@ -680,18 +760,22 @@ SectionTable = ArrayRef( Buf - OutputSections.size() * sizeof(coff_section), Buf); - // The string table normally follows the symbol table, but because we always - // emit an empty symbol table, the string table appears at the location of the - // symbol table. - COFF->PointerToSymbolTable = PointerToStringTable; - COFF->NumberOfSymbols = 0; - if (Strtab.empty()) + if (OutputSymtab.empty() && Strtab.empty()) return; - auto *StringTable = Buffer->getBufferStart() + PointerToStringTable; - // Create the string table. The first 4 bytes is length including itself. - write32le(StringTable, Strtab.size() + 4); - memcpy(StringTable + 4, Strtab.data(), Strtab.size()); + COFF->PointerToSymbolTable = PointerToSymbolTable; + uint32_t NumberOfSymbols = OutputSymtab.size(); + COFF->NumberOfSymbols = NumberOfSymbols; + auto *SymbolTable = reinterpret_cast( + 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(&SymbolTable[NumberOfSymbols]); + write32le(Buf, Strtab.size() + 4); + if (!Strtab.empty()) + memcpy(Buf + 4, Strtab.data(), Strtab.size()); } void Writer::openFile(StringRef Path) { Index: lld/test/COFF/strtab-size.s =================================================================== --- /dev/null +++ lld/test/COFF/strtab-size.s @@ -0,0 +1,216 @@ +# REQUIRES: x86 + +# Test that the strtab size is included in the allocation even if the +# strtab itself is empty. To achieve this, we need a number of symbols N +# where alignTo(N*18, 512) < alignTo(N*18 + 4, 512), where the first +# positive N fulfilling that is 199. + +# RUN: llvm-mc -triple=x86_64-windows-msvc %s -filetype=obj -o %t.obj +# RUN: lld-link -out:%t.exe -entry:main %t.obj -debug:dwarf + +# If the size of the strtab isn't allocated for, llvm-readobj would +# output SymbolCount: 0 (and dumpbin.exe would error out with "invalid file +# or disk full, cannot seek to 0x1602"). + +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s +# CHECK: SymbolCount: 199 + +.global main +.text +main: +sym0: +sym1: +sym2: +sym3: +sym4: +sym5: +sym6: +sym7: +sym8: +sym9: +sym10: +sym11: +sym12: +sym13: +sym14: +sym15: +sym16: +sym17: +sym18: +sym19: +sym20: +sym21: +sym22: +sym23: +sym24: +sym25: +sym26: +sym27: +sym28: +sym29: +sym30: +sym31: +sym32: +sym33: +sym34: +sym35: +sym36: +sym37: +sym38: +sym39: +sym40: +sym41: +sym42: +sym43: +sym44: +sym45: +sym46: +sym47: +sym48: +sym49: +sym50: +sym51: +sym52: +sym53: +sym54: +sym55: +sym56: +sym57: +sym58: +sym59: +sym60: +sym61: +sym62: +sym63: +sym64: +sym65: +sym66: +sym67: +sym68: +sym69: +sym70: +sym71: +sym72: +sym73: +sym74: +sym75: +sym76: +sym77: +sym78: +sym79: +sym80: +sym81: +sym82: +sym83: +sym84: +sym85: +sym86: +sym87: +sym88: +sym89: +sym90: +sym91: +sym92: +sym93: +sym94: +sym95: +sym96: +sym97: +sym98: +sym99: +sym100: +sym101: +sym102: +sym103: +sym104: +sym105: +sym106: +sym107: +sym108: +sym109: +sym110: +sym111: +sym112: +sym113: +sym114: +sym115: +sym116: +sym117: +sym118: +sym119: +sym120: +sym121: +sym122: +sym123: +sym124: +sym125: +sym126: +sym127: +sym128: +sym129: +sym130: +sym131: +sym132: +sym133: +sym134: +sym135: +sym136: +sym137: +sym138: +sym139: +sym140: +sym141: +sym142: +sym143: +sym144: +sym145: +sym146: +sym147: +sym148: +sym149: +sym150: +sym151: +sym152: +sym153: +sym154: +sym155: +sym156: +sym157: +sym158: +sym159: +sym160: +sym161: +sym162: +sym163: +sym164: +sym165: +sym166: +sym167: +sym168: +sym169: +sym170: +sym171: +sym172: +sym173: +sym174: +sym175: +sym176: +sym177: +sym178: +sym179: +sym180: +sym181: +sym182: +sym183: +sym184: +sym185: +sym186: +sym187: +sym188: +sym189: +sym190: +sym191: +sym192: +sym193: +sym194: + ret Index: lld/test/COFF/symtab.test =================================================================== --- /dev/null +++ lld/test/COFF/symtab.test @@ -0,0 +1,236 @@ +# RUN: yaml2obj < %s > %t.obj +# RUN: lld-link /debug:dwarf /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib +# RUN: llvm-readobj -symbols %t.exe | FileCheck %s +# RUN: lld-link /debug:dwarf /opt:noref /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib +# RUN: llvm-readobj -symbols %t.exe | FileCheck %s + +# RUN: lld-link /debug /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib +# RUN: llvm-readobj -symbols %t.exe | FileCheck -check-prefix=NO %s + +# CHECK: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .text +# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Section: .text (2) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: Static (0x3) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .text2 +# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Section: .text (2) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: Static (0x3) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .data +# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Section: .data (1) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: Static (0x3) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: MessageBoxA +# CHECK-NEXT: Value: 80 +# CHECK-NEXT: Section: .text (2) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: External (0x2) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: ExitProcess +# CHECK-NEXT: Value: 64 +# CHECK-NEXT: Section: .text (2) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: External (0x2) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: message +# CHECK-NEXT: Value: 6 +# CHECK-NEXT: Section: .text2 (3) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: Static (0x3) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: main +# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Section: .text (2) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: External (0x2) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: caption +# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Section: .text2 (3) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: Static (0x3) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: abs_symbol +# CHECK-NEXT: Value: 2662186735 +# CHECK-NEXT: Section: IMAGE_SYM_ABSOLUTE (-1) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: External (0x2) +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# NO: Symbols [ + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4096 + SectionData: B800000000000000005068000000000000000068000000000000000050E8000000000000000050E8000000000000000050E80000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: abs_symbol + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 7 + SymbolName: caption + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 12 + SymbolName: message + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 18 + SymbolName: MessageBoxA + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 24 + SymbolName: ExitProcess + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 30 + SymbolName: __ImageBase + Type: IMAGE_REL_AMD64_ADDR64 + - Name: .text2 + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4096 + SectionData: B800000000000000005068000000000000000068000000000000000050E8000000000000000050E8000000000000000050E80000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: abs_symbol + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 7 + SymbolName: caption + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 12 + SymbolName: message + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 18 + SymbolName: MessageBoxA + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 24 + SymbolName: ExitProcess + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 30 + SymbolName: __ImageBase + Type: IMAGE_REL_AMD64_ADDR64 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 48656C6C6F0048656C6C6F20576F726C6400 +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 28 + NumberOfRelocations: 6 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .text2 + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 28 + NumberOfRelocations: 6 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 18 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: MessageBoxA + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: ExitProcess + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: message + Value: 6 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: caption + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: abs_symbol + Value: 0xDEADBEEF + SectionNumber: -1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __ImageBase + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +...