Index: lld/ELF/CMakeLists.txt =================================================================== --- lld/ELF/CMakeLists.txt +++ lld/ELF/CMakeLists.txt @@ -17,6 +17,7 @@ InputSection.cpp LTO.cpp LinkerScript.cpp + MapFile.cpp MarkLive.cpp Mips.cpp OutputSections.cpp Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -80,6 +80,7 @@ llvm::StringRef Init; llvm::StringRef LTOAAPipeline; llvm::StringRef LTONewPmPasses; + llvm::StringRef MapFile; llvm::StringRef OutputFile; llvm::StringRef SoName; llvm::StringRef Sysroot; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -512,6 +512,7 @@ Config->Init = getString(Args, OPT_init, "_init"); Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline); Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes); + Config->MapFile = getString(Args, OPT_Map); Config->OutputFile = getString(Args, OPT_o); Config->SoName = getString(Args, OPT_soname); Config->Sysroot = getString(Args, OPT_sysroot); Index: lld/ELF/MapFile.h =================================================================== --- /dev/null +++ lld/ELF/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_ELF_MAPFILE_H +#define LLD_ELF_MAPFILE_H + +#include "OutputSections.h" + +namespace lld { +namespace elf { +template +void writeMapFile(llvm::ArrayRef OutputSections); +} +} + +#endif Index: lld/ELF/MapFile.cpp =================================================================== --- /dev/null +++ lld/ELF/MapFile.cpp @@ -0,0 +1,132 @@ +//===- 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 -Map 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 "InputFiles.h" +#include "Strings.h" + +#include "llvm/Support/FileUtilities.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::elf; + +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); +} + +template +static void writeMapFile2(int FD, + ArrayRef OutputSections) { + typedef typename ELFT::uint uintX_t; + raw_fd_ostream OS(FD, true); + OS << "Address Size Align Out In File Symbol\n" + << "=================================================================\n"; + for (OutputSectionBase *Sec : OutputSections) { + uintX_t VA = Sec->Addr; + writeOutSecLine(OS, VA, Sec->Size, Sec->Addralign, Sec->getName()); + OS << '\n'; + StringRef PrevName = ""; + Sec->forEachInputSection([&](InputSectionData *S) { + const auto *IS = dyn_cast>(S); + if (!IS) + return; + StringRef Name = IS->Name; + if (Name != PrevName) { + writeInSecLine(OS, VA + IS->OutSecOff, IS->getSize(), IS->Alignment, + Name); + OS << '\n'; + PrevName = Name; + } + elf::ObjectFile *File = IS->getFile(); + if (!File) + return; + writeFileLine(OS, VA + IS->OutSecOff, IS->getSize(), IS->Alignment, + toString(File)); + OS << '\n'; + ArrayRef Syms = File->getSymbols(); + for (SymbolBody *Sym : Syms) { + auto *DR = dyn_cast>(Sym); + if (!DR) + continue; + if (DR->Section != IS) + continue; + if (DR->isSection()) + continue; + writeSymbolLine(OS, Sym->getVA(), Sym->getSize(), + toString(*Sym)); + OS << '\n'; + } + }); + } +} + +template +void elf::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()); +} + +template void elf::writeMapFile(ArrayRef); +template void elf::writeMapFile(ArrayRef); +template void elf::writeMapFile(ArrayRef); +template void elf::writeMapFile(ArrayRef); Index: lld/ELF/OutputSections.h =================================================================== --- lld/ELF/OutputSections.h +++ lld/ELF/OutputSections.h @@ -81,6 +81,7 @@ OutputSectionBase *FirstInPtLoad = nullptr; virtual void finalize() {} + virtual void forEachInputSection(std::function F) {} virtual void assignOffsets() {} virtual void writeTo(uint8_t *Buf) {} virtual ~OutputSectionBase() = default; @@ -116,6 +117,7 @@ void sortCtorsDtors(); void writeTo(uint8_t *Buf) override; void finalize() override; + void forEachInputSection(std::function F) override; void assignOffsets() override; Kind getKind() const override { return Regular; } static bool classof(const OutputSectionBase *B) { @@ -168,6 +170,7 @@ void writeTo(uint8_t *Buf) override; void finalize() override; bool empty() const { return Sections.empty(); } + void forEachInputSection(std::function F) override; void addSection(InputSectionData *S) override; Kind getKind() const override { return EHFrame; } Index: lld/ELF/OutputSections.cpp =================================================================== --- lld/ELF/OutputSections.cpp +++ lld/ELF/OutputSections.cpp @@ -139,6 +139,13 @@ this->Entsize = S->Entsize; } +template +void OutputSection::forEachInputSection( + std::function F) { + for (InputSection *S : Sections) + F(S); +} + // This function is called after we sort input sections // and scan relocations to setup sections' offsets. template void OutputSection::assignOffsets() { @@ -265,6 +272,13 @@ EhOutputSection::EhOutputSection() : OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {} +template +void EhOutputSection::forEachInputSection( + std::function F) { + for (EhInputSection *S : Sections) + F(S); +} + // Search for an existing CIE record or create a new one. // CIE records from input object files are uniquified by their contents // and where their relocations point to. Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -10,6 +10,7 @@ #include "Writer.h" #include "Config.h" #include "LinkerScript.h" +#include "MapFile.h" #include "Memory.h" #include "OutputSections.h" #include "Relocations.h" @@ -231,6 +232,7 @@ if (ErrorCount) return; + writeMapFile(OutputSections); if (auto EC = Buffer->commit()) error(EC, "failed to write to the output file"); Index: lld/test/ELF/Inputs/map-file2.s =================================================================== --- /dev/null +++ lld/test/ELF/Inputs/map-file2.s @@ -0,0 +1,8 @@ +foo: +nop +.global bar +bar: +nop +.section .text.zed,"ax",@progbits +.global zed +zed: Index: lld/test/ELF/Inputs/map-file3.s =================================================================== --- /dev/null +++ lld/test/ELF/Inputs/map-file3.s @@ -0,0 +1,2 @@ +.global bah +bah: Index: lld/test/ELF/Inputs/map-file4.s =================================================================== --- /dev/null +++ lld/test/ELF/Inputs/map-file4.s @@ -0,0 +1,3 @@ +.global baz +baz: + retq Index: lld/test/ELF/map-file.s =================================================================== --- /dev/null +++ lld/test/ELF/map-file.s @@ -0,0 +1,55 @@ +// REQUIRES: x86 + +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file2.s -o %t2.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file3.s -o %t3.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file4.s -o %t4.o +// RUN: rm -f %t4.a +// RUN: llvm-ar rc %t4.a %t4.o +// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=%t.map +// RUN: FileCheck %s < %t.map + +.global _start +_start: + call baz +.global _Z1fi +_Z1fi: +.cfi_startproc +.cfi_endproc +nop +.weak bar +bar: +.long bar - . +.long zed - . +local: +.comm common,4,4 + +// CHECK: Address Size Align Out In File Symbol +// CHECK-NEXT: ================================================================= +// CHECK-NEXT: 00200158 00000030 8 .eh_frame +// CHECK-NEXT: 00201000 00000015 4 .text +// CHECK-NEXT: 00201000 0000000e 4 .text +// CHECK-NEXT: 00201000 0000000e 4 {{.*}}/map-file.s.tmp1.o +// CHECK-NEXT: 0020100e 00000000 0 local +// CHECK-NEXT: 00201005 00000000 0 f(int) +// CHECK-NEXT: 00201000 00000000 0 _start +// CHECK-NEXT: 00201010 00000002 4 {{.*}}/map-file.s.tmp2.o +// CHECK-NEXT: 00201010 00000000 0 foo +// CHECK-NEXT: 00201011 00000000 0 bar +// CHECK-NEXT: 00201012 00000000 1 .text.zed +// CHECK-NEXT: 00201012 00000000 1 {{.*}}/map-file.s.tmp2.o +// CHECK-NEXT: 00201012 00000000 0 zed +// CHECK-NEXT: 00201014 00000000 4 .text +// CHECK-NEXT: 00201014 00000000 4 {{.*}}/map-file.s.tmp3.o +// CHECK-NEXT: 00201014 00000000 0 bah +// CHECK-NEXT: 00201014 00000001 4 {{.*}}/map-file.s.tmp4.a(map-file.s.tmp4.o) +// CHECK-NEXT: 00201014 00000000 0 baz +// CHECK-NEXT: 00202000 00000004 4 .bss +// CHECK-NEXT: 00202000 00000004 4 COMMON +// CHECK-NEXT: 00000000 00000008 1 .comment +// CHECK-NEXT: 00000000 000000f0 8 .symtab +// CHECK-NEXT: 00000000 000000f0 8 .symtab +// CHECK-NEXT: 00000000 00000039 1 .shstrtab +// CHECK-NEXT: 00000000 00000039 1 .shstrtab +// CHECK-NEXT: 00000000 0000002f 1 .strtab +// CHECK-NEXT: 00000000 0000002f 1 .strtab