Index: lld/trunk/COFF/CMakeLists.txt =================================================================== --- lld/trunk/COFF/CMakeLists.txt +++ lld/trunk/COFF/CMakeLists.txt @@ -15,6 +15,7 @@ ICF.cpp InputFiles.cpp Librarian.cpp + MapFile.cpp MarkLive.cpp ModuleDef.cpp PDB.cpp Index: lld/trunk/COFF/Chunks.h =================================================================== --- lld/trunk/COFF/Chunks.h +++ lld/trunk/COFF/Chunks.h @@ -187,10 +187,10 @@ const coff_section *Header; -private: - // A file this chunk was created from. + // The file that this chunk was created from. ObjectFile *File; +private: StringRef SectionName; std::vector AssocChildren; llvm::iterator_range Relocs; Index: lld/trunk/COFF/Config.h =================================================================== --- lld/trunk/COFF/Config.h +++ lld/trunk/COFF/Config.h @@ -135,6 +135,9 @@ // Used for /alternatename. std::map AlternateNames; + // Used for /lldmap. + std::string MapFile; + uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -658,6 +658,8 @@ Config->DumpPdb = Args.hasArg(OPT_dumppdb); Config->DebugPdb = Args.hasArg(OPT_debugpdb); + Config->MapFile = getMapFile(Args); + // Create a list of input files. Files can be given as arguments // for /defaultlib option. std::vector MBs; @@ -846,17 +848,6 @@ // Write the result. writeResult(&Symtab); - // Create a symbol map file containing symbol VAs and their names - // to help debugging. - std::string MapFile = getMapFile(Args); - if (!MapFile.empty()) { - std::error_code EC; - raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text); - if (EC) - fatal(EC, "could not create the symbol map " + MapFile); - Symtab.printMap(Out); - } - // Call exit to avoid calling destructors. exit(0); } Index: lld/trunk/COFF/MapFile.h =================================================================== --- lld/trunk/COFF/MapFile.h +++ lld/trunk/COFF/MapFile.h @@ -0,0 +1,22 @@ +//===- MapFile.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_MAPFILE_H +#define LLD_COFF_MAPFILE_H + +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { +class OutputSection; +void writeMapFile(llvm::ArrayRef OutputSections); +} +} + +#endif Index: lld/trunk/COFF/MapFile.cpp =================================================================== --- lld/trunk/COFF/MapFile.cpp +++ lld/trunk/COFF/MapFile.cpp @@ -0,0 +1,125 @@ +//===- MapFile.cpp --------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the /lldmap option. It shows lists in order and +// hierarchically the output sections, input sections, input files and +// symbol: +// +// Address Size Align Out In File Symbol +// ================================================================= +// 00201000 00000015 4 .text +// 00201000 0000000e 4 .text +// 00201000 0000000e 4 test.o +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "Error.h" +#include "Symbols.h" +#include "Writer.h" + +#include "llvm/Support/FileUtilities.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + OS << format_hex_no_prefix(Address, 8) << ' ' + << format_hex_no_prefix(Size, 8) << ' ' << format("%5x ", Align) + << left_justify(Name, 7); +} + +static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeOutSecLine(OS, Address, Size, Align, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeInSecLine(OS, Address, Size, Align, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeFileLine(OS, Address, Size, 0, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC, + StringRef &PrevName) { + StringRef Name = SC->getSectionName(); + if (Name != PrevName) { + writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name); + OS << '\n'; + PrevName = Name; + } + coff::ObjectFile *File = SC->File; + if (!File) + return; + writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), + toString(File)); + OS << '\n'; + ArrayRef Syms = File->getSymbols(); + for (SymbolBody *Sym : Syms) { + auto *DR = dyn_cast(Sym); + if (!DR || DR->getChunk() != SC || + DR->getCOFFSymbol().isSectionDefinition()) + continue; + writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym)); + OS << '\n'; + } +} + +static void writeMapFile2(int FD, + ArrayRef OutputSections) { + raw_fd_ostream OS(FD, true); + OS << left_justify("Address", 8) << ' ' << left_justify("Size", 8) + << ' ' << left_justify("Align", 5) << ' ' << left_justify("Out", 7) << ' ' + << left_justify("In", 7) << ' ' << left_justify("File", 7) << " Symbol\n"; + for (OutputSection *Sec : OutputSections) { + uint32_t VA = Sec->getRVA(); + writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize, + Sec->getName()); + OS << '\n'; + StringRef PrevName = ""; + for (Chunk *C : Sec->getChunks()) + if (const auto *SC = dyn_cast(C)) + writeSectionChunk(OS, SC, PrevName); + } +} + +void coff::writeMapFile(ArrayRef OutputSections) { + StringRef MapFile = Config->MapFile; + if (MapFile.empty()) + return; + + // Create new file in same directory but with random name. + SmallString<128> TempPath; + int FD; + std::error_code EC = + sys::fs::createUniqueFile(Twine(MapFile) + ".tmp%%%%%%%", FD, TempPath); + if (EC) + fatal(EC.message()); + FileRemover RAII(TempPath); + writeMapFile2(FD, OutputSections); + EC = sys::fs::rename(TempPath, MapFile); + if (EC) + fatal(EC.message()); +} Index: lld/trunk/COFF/SymbolTable.h =================================================================== --- lld/trunk/COFF/SymbolTable.h +++ lld/trunk/COFF/SymbolTable.h @@ -69,9 +69,6 @@ void mangleMaybe(SymbolBody *B); StringRef findMangle(StringRef Name); - // Print a layout map to OS. - void printMap(llvm::raw_ostream &OS); - // Build a set of COFF objects representing the combined contents of // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. Index: lld/trunk/COFF/SymbolTable.cpp =================================================================== --- lld/trunk/COFF/SymbolTable.cpp +++ lld/trunk/COFF/SymbolTable.cpp @@ -345,17 +345,6 @@ return addUndefined(Name, nullptr, false)->body(); } -void SymbolTable::printMap(llvm::raw_ostream &OS) { - for (ObjectFile *File : ObjectFiles) { - OS << toString(File) << ":\n"; - for (SymbolBody *Body : File->getSymbols()) - if (auto *R = dyn_cast(Body)) - if (R->getChunk()->isLive()) - OS << Twine::utohexstr(Config->ImageBase + R->getRVA()) - << " " << R->getName() << "\n"; - } -} - void SymbolTable::addCombinedLTOObjects() { if (BitcodeFiles.empty()) return; Index: lld/trunk/COFF/Writer.h =================================================================== --- lld/trunk/COFF/Writer.h +++ lld/trunk/COFF/Writer.h @@ -10,14 +10,65 @@ #ifndef LLD_COFF_WRITER_H #define LLD_COFF_WRITER_H +#include "Chunks.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include #include namespace lld { namespace coff { class SymbolTable; +static const int PageSize = 4096; + void writeResult(SymbolTable *T); +// OutputSection represents a section in an output file. It's a +// container of chunks. OutputSection and Chunk are 1:N relationship. +// Chunks cannot belong to more than one OutputSections. The writer +// creates multiple OutputSections and assign them unique, +// non-overlapping file offsets and RVAs. +class OutputSection { +public: + OutputSection(llvm::StringRef N) : Name(N), Header({}) {} + void setRVA(uint64_t); + void setFileOffset(uint64_t); + void addChunk(Chunk *C); + llvm::StringRef getName() { return Name; } + std::vector &getChunks() { return Chunks; } + void addPermissions(uint32_t C); + void setPermissions(uint32_t C); + uint32_t getPermissions() { return Header.Characteristics & PermMask; } + uint32_t getCharacteristics() { return Header.Characteristics; } + uint64_t getRVA() { return Header.VirtualAddress; } + uint64_t getFileOff() { return Header.PointerToRawData; } + void writeHeaderTo(uint8_t *Buf); + + // Returns the size of this section in an executable memory image. + // This may be smaller than the raw size (the raw size is multiple + // of disk sector size, so there may be padding at end), or may be + // larger (if that's the case, the loader reserves spaces after end + // of raw data). + uint64_t getVirtualSize() { return Header.VirtualSize; } + + // Returns the size of the section in the output file. + uint64_t getRawSize() { return Header.SizeOfRawData; } + + // Set offset into the string table storing this section name. + // 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: + llvm::StringRef Name; + llvm::object::coff_section Header; + uint32_t StringTableOff = 0; + std::vector Chunks; +}; + } } Index: lld/trunk/COFF/Writer.cpp =================================================================== --- lld/trunk/COFF/Writer.cpp +++ lld/trunk/COFF/Writer.cpp @@ -12,6 +12,7 @@ #include "DLL.h" #include "Error.h" #include "InputFiles.h" +#include "MapFile.h" #include "Memory.h" #include "PDB.h" #include "SymbolTable.h" @@ -39,7 +40,6 @@ using namespace lld; using namespace lld::coff; -static const int PageSize = 4096; static const int SectorSize = 512; static const int DOSStubSize = 64; static const int NumberfOfDataDirectory = 16; @@ -163,51 +163,6 @@ void writeResult(SymbolTable *T) { Writer(T).run(); } -// OutputSection represents a section in an output file. It's a -// container of chunks. OutputSection and Chunk are 1:N relationship. -// Chunks cannot belong to more than one OutputSections. The writer -// creates multiple OutputSections and assign them unique, -// non-overlapping file offsets and RVAs. -class OutputSection { -public: - OutputSection(StringRef N) : Name(N), Header({}) {} - void setRVA(uint64_t); - void setFileOffset(uint64_t); - void addChunk(Chunk *C); - StringRef getName() { return Name; } - std::vector &getChunks() { return Chunks; } - void addPermissions(uint32_t C); - void setPermissions(uint32_t C); - uint32_t getPermissions() { return Header.Characteristics & PermMask; } - uint32_t getCharacteristics() { return Header.Characteristics; } - uint64_t getRVA() { return Header.VirtualAddress; } - uint64_t getFileOff() { return Header.PointerToRawData; } - void writeHeaderTo(uint8_t *Buf); - - // Returns the size of this section in an executable memory image. - // This may be smaller than the raw size (the raw size is multiple - // of disk sector size, so there may be padding at end), or may be - // larger (if that's the case, the loader reserves spaces after end - // of raw data). - uint64_t getVirtualSize() { return Header.VirtualSize; } - - // Returns the size of the section in the output file. - uint64_t getRawSize() { return Header.SizeOfRawData; } - - // Set offset into the string table storing this section name. - // 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; - coff_section Header; - uint32_t StringTableOff = 0; - std::vector Chunks; -}; - void OutputSection::setRVA(uint64_t RVA) { Header.VirtualAddress = RVA; for (Chunk *C : Chunks) @@ -306,6 +261,8 @@ if (!Config->PDBPath.empty()) createPDB(Config->PDBPath, Symtab, SectionTable, BuildId->DI); + writeMapFile(OutputSections); + if (auto EC = Buffer->commit()) fatal(EC, "failed to write the output file"); } Index: lld/trunk/test/COFF/lldmap.test =================================================================== --- lld/trunk/test/COFF/lldmap.test +++ lld/trunk/test/COFF/lldmap.test @@ -4,7 +4,8 @@ # RUN: lld-link /out:%T/bar.exe /entry:main /lldmap %t.obj # RUN: FileCheck %s < %T/bar.map -# CHECK: .obj: -# CHECK-NEXT: 140001000 .text$mn -# CHECK-NEXT: 140001000 .data -# CHECK-NEXT: 140001000 main +# CHECK: Address Size Align Out In File Symbol +# CHECK-NEXT: 00001000 00000006 1000 .text +# CHECK-NEXT: 00001000 00000006 10 .text$mn +# CHECK-NEXT: 00001000 00000006 10 {{.*}}lldmap.test.tmp.obj +# CHECK-NEXT: 00001000 00000006 0 main Index: lld/trunk/test/COFF/lto-parallel.ll =================================================================== --- lld/trunk/test/COFF/lto-parallel.ll +++ lld/trunk/test/COFF/lto-parallel.ll @@ -5,15 +5,15 @@ target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" -; CHECK: : -; CHECK: foo +; CHECK: +; CHECK-NEXT: foo define void @foo() { call void @bar() ret void } -; CHECK: : -; CHECK: bar +; CHECK: +; CHECK-NEXT: bar define void @bar() { call void @foo() ret void Index: lld/trunk/test/COFF/weak-external.test =================================================================== --- lld/trunk/test/COFF/weak-external.test +++ lld/trunk/test/COFF/weak-external.test @@ -4,9 +4,8 @@ # RUN: lld-link /out:%t2.exe /entry:g /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj # RUN: FileCheck %s < %t2.map -# CHECK: : -# CHECK-NOT: : -# CHECK: {{ g$}} +# CHECK: +# CHECK-NEXT: 0 g --- !COFF header: Index: lld/trunk/test/COFF/weak-external3.test =================================================================== --- lld/trunk/test/COFF/weak-external3.test +++ lld/trunk/test/COFF/weak-external3.test @@ -5,13 +5,11 @@ # RUN: lld-link /out:%t2.exe /entry:f /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj # RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map -# CHECK1: : -# CHECK1-NOT: : -# CHECK1: {{ g$}} +# CHECK1: +# CHECK1-NEXT: 0 g -# CHECK2: weak-external3{{.*}}: -# CHECK2-NOT: : -# CHECK2: {{ f$}} +# CHECK2: weak-external3.test.tmp.obj +# CHECK2-NEXT: 0 f --- !COFF header: