diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -89,6 +89,7 @@ llvm::StringRef DwoDir; llvm::StringRef Entry; llvm::StringRef Emulation; + llvm::StringRef GnuMap; llvm::StringRef Fini; llvm::StringRef Init; llvm::StringRef LTOAAPipeline; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -826,8 +826,9 @@ Config->ForceBTI = Args.hasArg(OPT_force_bti); Config->RequireCET = Args.hasArg(OPT_require_cet); Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); - Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); + Config->GnuMap = Args.getLastArgValue(OPT_gnu_map); + Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->ICF = getICF(Args); Config->IgnoreDataAddressEquality = Args.hasArg(OPT_ignore_data_address_equality); diff --git a/lld/ELF/MapFile.h b/lld/ELF/MapFile.h --- a/lld/ELF/MapFile.h +++ b/lld/ELF/MapFile.h @@ -12,6 +12,7 @@ namespace lld { namespace elf { void writeMapFile(); +void writeGnuStyleMapFile(); void writeCrossReferenceTable(); } // namespace elf } // namespace lld diff --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp --- a/lld/ELF/MapFile.cpp +++ b/lld/ELF/MapFile.cpp @@ -213,6 +213,75 @@ } } +// Print out a map file in the same format as GNU gold. This function +// is called when you pass `-gnu-map`. +// +// Technically, `-gnu-map` is a duplicate feature as `-Map`. lld's map +// file format is (in our opinion) better than GNU's, as our map file +// is arguably easier to read and also contains LMAs that the GNU +// format lacks. +// +// That being said, `-Map` output is oftentimes processed by post-link +// tools, and sometimes updating the tools to be able to understand +// the lld-style map file is hard. Therefore, we support both formats. +void elf::writeGnuStyleMapFile() { + if (Config->GnuMap.empty()) + return; + + // Open a map file for writing. + std::error_code EC; + raw_fd_ostream OS(Config->GnuMap, EC, sys::fs::F_None); + if (EC) { + error("cannot open " + Config->GnuMap + ": " + EC.message()); + return; + } + + // Collect symbol info that we want to print out. + std::vector Syms = getSymbols(); + SymbolMapTy SectionSyms = getSectionSyms(Syms); + + // Print out the header line. + OS << "Memory map\n"; + + for (BaseCommand *Base : Script->SectionCommands) { + OutputSection *OSec = dyn_cast(Base); + if (!OSec) + continue; + + OS << "\n"; + if (OSec->Name.size() <= 15) + OS << left_justify(OSec->Name, 15); + else + OS << OSec->Name << "\n "; + + OS << format(" 0x%016llx", OSec->Addr) << " " + << left_justify(("0x" + Twine::utohexstr(OSec->Size)).str(), 11) << "\n"; + + // Dump symbols for each input section. + for (BaseCommand *Base : OSec->SectionCommands) { + auto *Desc = dyn_cast(Base); + if (!Desc) + continue; + + for (InputSection *IS : Desc->Sections) { + if (IS->Name.size() <= 14) + OS << " " << left_justify(IS->Name, 14); + else + OS << " " << IS->Name << "\n "; + + OS << format(" 0x%016llx", IS->getVA(0)) << " " + << left_justify(("0x" + Twine::utohexstr(IS->getSize())).str(), 11) + << " " << toString(IS) << "\n"; + + for (Symbol *Sym : SectionSyms[IS]) { + OS << " " << format("0x%016llx", Sym->getVA()) + << " " << toString(*Sym) << "\n"; + } + } + } + } +} + static void print(StringRef A, StringRef B) { outs() << left_justify(A, 49) << " " << B << "\n"; } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -189,6 +189,9 @@ "Generate .gdb_index section", "Do not generate .gdb_index section (default)">; +defm gnu_map: Eq<"gnu-map", + "Print a link map to the specified file in the same format as GNU gold">; + defm gnu_unique: B<"gnu-unique", "Enable STB_GNU_UNIQUE symbol binding (default)", "Disable STB_GNU_UNIQUE symbol binding">; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -632,6 +632,7 @@ // Handle -Map and -cref options. writeMapFile(); + writeGnuStyleMapFile(); writeCrossReferenceTable(); if (errorCount()) return; diff --git a/lld/test/ELF/gnu-map.s b/lld/test/ELF/gnu-map.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/gnu-map.s @@ -0,0 +1,108 @@ +// 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: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file5.s -o %t5.o +// RUN: ld.lld -shared %t5.o -o %t5.so -soname dso +// RUN: rm -f %t4.a +// RUN: llvm-ar rc %t4.a %t4.o +// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t -gnu-map=- | \ +// RUN: FileCheck --match-full-lines %s + +.global _start +_start: +.cfi_startproc +.cfi_endproc + .quad sharedFoo + .quad sharedBar + .byte 0xe8 + .long sharedFunc1 - . + .byte 0xe8 + .long sharedFunc2 - . + .byte 0xe8 + .long baz - . +.global _Z1fi +_Z1fi: +.cfi_startproc +nop +.cfi_endproc +.weak bar +bar: +.long bar - . +.long zed - . +local: +.comm common,4,16 +.global abs +abs = 0xAB5 +labs = 0x1AB5 + +// CHECK: Memory map +// CHECK-EMPTY: +// CHECK-NEXT: .dynsym 0x0000000000200200 0x78 +// CHECK-NEXT: .dynsym 0x0000000000200200 0x78 :(.dynsym) +// CHECK-EMPTY: +// CHECK-NEXT: .gnu.hash 0x0000000000200278 0x2c +// CHECK-NEXT: .gnu.hash 0x0000000000200278 0x2c :(.gnu.hash) +// CHECK-EMPTY: +// CHECK-NEXT: .hash 0x00000000002002a4 0x30 +// CHECK-NEXT: .hash 0x00000000002002a4 0x30 :(.hash) +// CHECK-EMPTY: +// CHECK-NEXT: .dynstr 0x00000000002002d4 0x31 +// CHECK-NEXT: .dynstr 0x00000000002002d4 0x31 :(.dynstr) +// CHECK-EMPTY: +// CHECK-NEXT: .rela.dyn 0x0000000000200308 0x30 +// CHECK-NEXT: .rela.dyn 0x0000000000200308 0x30 :(.rela.dyn) +// CHECK-EMPTY: +// CHECK-NEXT: .rela.plt 0x0000000000200338 0x30 +// CHECK-NEXT: .rela.plt 0x0000000000200338 0x30 :(.rela.plt) +// CHECK-EMPTY: +// CHECK-NEXT: .eh_frame 0x0000000000200368 0x64 +// CHECK-NEXT: .eh_frame 0x0000000000200368 0x64 :(.eh_frame) +// CHECK-EMPTY: +// CHECK-NEXT: .text 0x0000000000201000 0x2d +// CHECK-NEXT: .text 0x0000000000201000 0x28 /home/ruiu/llvm/b/tools/lld/test/ELF/Output/gnu-map.s.tmp1.o:(.text) +// CHECK-NEXT: 0x0000000000201000 _start +// CHECK-NEXT: 0x000000000020101f f(int) +// CHECK-NEXT: 0x0000000000201028 local +// CHECK-NEXT: .text 0x0000000000201028 0x2 /home/ruiu/llvm/b/tools/lld/test/ELF/Output/gnu-map.s.tmp2.o:(.text) +// CHECK-NEXT: 0x0000000000201028 foo +// CHECK-NEXT: 0x0000000000201029 bar +// CHECK-NEXT: .text.zed 0x000000000020102a 0x0 /home/ruiu/llvm/b/tools/lld/test/ELF/Output/gnu-map.s.tmp2.o:(.text.zed) +// CHECK-NEXT: 0x000000000020102a zed +// CHECK-NEXT: .text 0x000000000020102c 0x0 /home/ruiu/llvm/b/tools/lld/test/ELF/Output/gnu-map.s.tmp3.o:(.text) +// CHECK-NEXT: 0x000000000020102c bah +// CHECK-NEXT: .text 0x000000000020102c 0x1 /home/ruiu/llvm/b/tools/lld/test/ELF/Output/gnu-map.s.tmp4.a(gnu-map.s.tmp4.o):(.text) +// CHECK-NEXT: 0x000000000020102c baz +// CHECK-EMPTY: +// CHECK-NEXT: .plt 0x0000000000201030 0x30 +// CHECK-NEXT: .plt 0x0000000000201030 0x30 :(.plt) +// CHECK-NEXT: 0x0000000000201040 sharedFunc1 +// CHECK-NEXT: 0x0000000000201050 sharedFunc2 +// CHECK-EMPTY: +// CHECK-NEXT: .dynamic 0x0000000000202000 0x100 +// CHECK-NEXT: .dynamic 0x0000000000202000 0x100 :(.dynamic) +// CHECK-EMPTY: +// CHECK-NEXT: .got.plt 0x0000000000203000 0x28 +// CHECK-NEXT: .got.plt 0x0000000000203000 0x28 :(.got.plt) +// CHECK-EMPTY: +// CHECK-NEXT: .bss 0x0000000000203030 0x10 +// CHECK-NEXT: COMMON 0x0000000000203030 0x4 /home/ruiu/llvm/b/tools/lld/test/ELF/Output/gnu-map.s.tmp1.o:(COMMON) +// CHECK-NEXT: 0x0000000000203030 common +// CHECK-NEXT: .bss 0x0000000000203034 0x4 :(.bss) +// CHECK-NEXT: 0x0000000000203034 sharedFoo +// CHECK-NEXT: .bss 0x0000000000203038 0x8 :(.bss) +// CHECK-NEXT: 0x0000000000203038 sharedBar +// CHECK-EMPTY: +// CHECK-NEXT: .comment 0x0000000000000000 0x8 +// CHECK-NEXT: .comment 0x0000000000000000 0x8 :(.comment) +// CHECK-EMPTY: +// CHECK-NEXT: .symtab 0x0000000000000000 0x198 +// CHECK-NEXT: .symtab 0x0000000000000000 0x198 :(.symtab) +// CHECK-EMPTY: +// CHECK-NEXT: .shstrtab 0x0000000000000000 0x84 +// CHECK-NEXT: .shstrtab 0x0000000000000000 0x84 :(.shstrtab) +// CHECK-EMPTY: +// CHECK-NEXT: .strtab 0x0000000000000000 0x6d +// CHECK-NEXT: .strtab 0x0000000000000000 0x6d :(.strtab)