Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/ELF/Config.h @@ -102,6 +102,7 @@ bool Demangle = true; bool DisableVerify; bool EhFrameHdr; + bool EmitRelocs; bool EnableNewDtags; bool ExportDynamic; bool FatalWarnings; @@ -157,6 +158,7 @@ unsigned LTOO; unsigned Optimize; unsigned ThinLTOJobs; + bool copyRelocs() { return Relocatable || EmitRelocs; }; }; // The only instance of Configuration struct. Index: lld/trunk/ELF/Driver.cpp =================================================================== --- lld/trunk/ELF/Driver.cpp +++ lld/trunk/ELF/Driver.cpp @@ -519,6 +519,7 @@ Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); + Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr); Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); Config->ExportDynamic = Index: lld/trunk/ELF/InputFiles.cpp =================================================================== --- lld/trunk/ELF/InputFiles.cpp +++ lld/trunk/ELF/InputFiles.cpp @@ -399,6 +399,13 @@ } assert(isUInt<31>(NumRelocations)); Target->NumRelocations = NumRelocations; + + // Relocation sections processed by the linker are usually removed + // from the output, so returning `nullptr` for the normal case. + // However, if -emit-relocs is given, we need to leave them in the output. + // (Some post link analysis tools need this information.) + if (Config->EmitRelocs) + return make>(this, &Sec, Name); return nullptr; } } Index: lld/trunk/ELF/InputSection.cpp =================================================================== --- lld/trunk/ELF/InputSection.cpp +++ lld/trunk/ELF/InputSection.cpp @@ -214,9 +214,9 @@ return Sections[this->Info]; } -// This is used for -r. We can't use memcpy to copy relocations because we need -// to update symbol table offset and section index for each relocation. So we -// copy relocations one by one. +// This is used for -r and --emit-relocs. We can't use memcpy to copy +// relocations because we need to update symbol table offset and section index +// for each relocation. So we copy relocations one by one. template template void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { @@ -235,7 +235,11 @@ if (Config->Rela) P->r_addend = getAddend(Rel); - P->r_offset = RelocatedSection->getOffset(Rel.r_offset); + + // Output section VA is zero for -r, so r_offset is an offset within the + // section, but for --emit-relocs it is an virtual address. + P->r_offset = RelocatedSection->OutSec->Addr + + RelocatedSection->getOffset(Rel.r_offset); P->setSymbolAndType(In::SymTab->getSymbolIndex(&Body), Type, Config->Mips64EL); } @@ -514,7 +518,8 @@ return; } - // If -r is given, then an InputSection may be a relocation section. + // If -r or --emit-relocs is given, then an InputSection + // may be a relocation section. if (this->Type == SHT_RELA) { copyRelocations(Buf + OutSecOff, this->template getDataAs()); return; Index: lld/trunk/ELF/Options.td =================================================================== --- lld/trunk/ELF/Options.td +++ lld/trunk/ELF/Options.td @@ -70,6 +70,8 @@ def eh_frame_hdr: F<"eh-frame-hdr">, HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">; +def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; + def enable_new_dtags: F<"enable-new-dtags">, HelpText<"Enable new dynamic tags">; @@ -283,6 +285,7 @@ def alias_discard_all_x: Flag<["-"], "x">, Alias; def alias_discard_locals_X: Flag<["-"], "X">, Alias; def alias_dynamic_list: J<"dynamic-list=">, Alias; +def alias_emit_relocs: Flag<["-"], "q">, Alias; def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias; def alias_entry_entry: J<"entry=">, Alias; def alias_error_limit: J<"error-limit=">, Alias; Index: lld/trunk/ELF/OutputSections.cpp =================================================================== --- lld/trunk/ELF/OutputSections.cpp +++ lld/trunk/ELF/OutputSections.cpp @@ -116,7 +116,7 @@ } uint32_t Type = this->Type; - if (!Config->Relocatable || (Type != SHT_RELA && Type != SHT_REL)) + if (!Config->copyRelocs() || (Type != SHT_RELA && Type != SHT_REL)) return; this->Link = In::SymTab->OutSec->SectionIndex; Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -449,9 +449,9 @@ if (B.isFile()) return false; - // We keep sections in symtab for relocatable output. + // We keep sections in symtab for relocatable output and --emit-reloc. if (B.isSection()) - return Config->Relocatable; + return Config->copyRelocs(); // If sym references a section in a discarded group, don't keep it. if (Sec == &InputSection::Discarded) Index: lld/trunk/test/ELF/emit-relocs.s =================================================================== --- lld/trunk/test/ELF/emit-relocs.s +++ lld/trunk/test/ELF/emit-relocs.s @@ -0,0 +1,108 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: ld.lld --emit-relocs %t1.o -o %t +# RUN: llvm-readobj -t -r %t1 | FileCheck %s + +## Check single dash form. +# RUN: ld.lld -emit-relocs %t1.o -o %t1 +# RUN: llvm-readobj -t -r %t1 | FileCheck %s + +## Check alias. +# RUN: ld.lld -q %t1.o -o %t2 +# RUN: llvm-readobj -t -r %t2 | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: Section ({{.*}}) .rela.text { +# CHECK-NEXT: 0x201002 R_X86_64_32 .text 0x1 +# CHECK-NEXT: 0x201007 R_X86_64_PLT32 fn 0xFFFFFFFFFFFFFFFC +# CHECK-NEXT: 0x20100E R_X86_64_32 .text 0x1 +# CHECK-NEXT: 0x201013 R_X86_64_PLT32 fn2 0xFFFFFFFFFFFFFFFC +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: bar +# CHECK-NEXT: Value: 0x201001 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Value: 0x20100D +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x201000 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Section +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x20100C +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Section +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: fn +# CHECK-NEXT: Value: 0x201000 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: fn2 +# CHECK-NEXT: Value: 0x20100C +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: ] + +.section .text,"ax",@progbits,unique,0 +.globl fn +.type fn,@function +fn: + nop + +bar: + movl $bar, %edx + callq fn@PLT + nop + +.section .text,"ax",@progbits,unique,1 +.globl fn2 +.type fn2,@function +fn2: + nop + +foo: + movl $foo, %edx + callq fn2@PLT + nop