diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -330,8 +330,6 @@ if (config->relocatable) { if (config->shared) error("-r and -shared may not be used together"); - if (config->gcSections) - error("-r and --gc-sections may not be used together"); if (config->gdbIndex) error("-r and --gdb-index may not be used together"); if (config->icf != ICFLevel::None) diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -926,20 +926,6 @@ this->sections[sec.sh_info] = target; } - // This section contains relocation information. - // If -r is given, we do not interpret or apply relocation - // but just copy relocation sections to output. - if (config->relocatable) { - InputSection *relocSec = make(*this, sec, name); - // We want to add a dependency to target, similar like we do for - // -emit-relocs below. This is useful for the case when linker script - // contains the "/DISCARD/". It is perhaps uncommon to use a script with - // -r, but we faced it in the Linux kernel and have to handle such case - // and not to crash. - target->dependentSections.push_back(relocSec); - return relocSec; - } - if (target->firstRelocation) fatal(toString(this) + ": multiple relocation sections to one section are not supported"); @@ -957,17 +943,17 @@ } assert(isUInt<31>(target->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) { - InputSection *relocSec = make(*this, sec, name); - // We will not emit relocation section if target was discarded. - target->dependentSections.push_back(relocSec); - return relocSec; - } - return nullptr; + // Relocation sections are usually removed from the output, so return + // `nullptr` for the normal case. However, if -r or --emit-relocs is + // specified, we need to copy them to the output. (Some post link analysis + // tools specify --emit-relocs to obtain the information.) + if (!config->relocatable && !config->emitRelocs) + return nullptr; + InputSection *relocSec = make(*this, sec, name); + // If the relocated section is discarded (due to /DISCARD/ or + // --gc-sections), the relocation section should be discarded as well. + target->dependentSections.push_back(relocSec); + return relocSec; } } diff --git a/lld/test/ELF/driver.test b/lld/test/ELF/driver.test --- a/lld/test/ELF/driver.test +++ b/lld/test/ELF/driver.test @@ -34,10 +34,6 @@ # RUN: not ld.lld -r -shared %t -o /dev/null 2>&1 | FileCheck -check-prefix=ERR2 %s # ERR2: -r and -shared may not be used together -## Attempt to use -r and --gc-sections together -# RUN: not ld.lld -r --gc-sections %t -o /dev/null 2>&1 | FileCheck -check-prefix=ERR3 %s -# ERR3: -r and --gc-sections may not be used together - ## Attempt to use -r and --gdb-index together # RUN: not ld.lld -r --gdb-index %t -o /dev/null 2>&1 | FileCheck -check-prefix=ERR4 %s # ERR4: -r and --gdb-index may not be used together diff --git a/lld/test/ELF/relocatable-gc.s b/lld/test/ELF/relocatable-gc.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/relocatable-gc.s @@ -0,0 +1,80 @@ +# REQUIRES: x86 +## Test -r --gc-sections. + +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o + +## By default all regular sections are discarded. We currently don't track +## usage of group signature symbols and will retain them and their associated +## STT_SECTION symbols. +# RUN: ld.lld -r --gc-sections --print-gc-sections %t.o -o %t +# RUN: llvm-readelf -S -s %t | FileCheck %s + +# CHECK: [ 1] .group +# CHECK-NEXT: [ 2] .note.GNU-stack + +# CHECK: Symbol table '.symtab' contains 3 entries: +# CHECK-NEXT: Num: +# CHECK-NEXT: 0: +# CHECK-NEXT: 1: {{.*}} NOTYPE LOCAL DEFAULT 1 group +# CHECK-NEXT: 2: {{.*}} SECTION LOCAL DEFAULT 1 + +## -u keeps .text.bar alive. Other group members are kept alive as well. +# RUN: ld.lld -r --gc-sections -u bar %t.o -o - | llvm-readelf -Ss - | \ +# RUN: FileCheck %s --check-prefix=KEEP_GROUP +## -e, --init and --fini are similar. +# RUN: ld.lld -r --gc-sections -e bar %t.o -o - | llvm-readelf -Ss - | \ +# RUN: FileCheck %s --check-prefix=KEEP_GROUP +# RUN: ld.lld -r --gc-sections --init=bar %t.o -o - | llvm-readelf -Ss - | \ +# RUN: FileCheck %s --check-prefix=KEEP_GROUP +# RUN: ld.lld -r --gc-sections --fini=bar %t.o -o - | llvm-readelf -Ss - | \ +# RUN: FileCheck %s --check-prefix=KEEP_GROUP + +# KEEP_GROUP: [ 1] .group +# KEEP_GROUP-NEXT: [ 2] .text.bar +# KEEP_GROUP-NEXT: [ 3] .text.foo +# KEEP_GROUP-NEXT: [ 4] .note.GNU-stack + +# KEEP_GROUP: Symbol table '.symtab' contains 7 entries: +# KEEP_GROUP: 4: {{.*}} SECTION +# KEEP_GROUP-NEXT: 5: {{.*}} 2 bar +# KEEP_GROUP-NEXT: 6: {{.*}} 3 foo + +## If .text is retained, its referenced qux and .fred are retained as well. +## fred_und is used (by .fred) and thus emitted. +## Note, GNU ld does not retain qux. +# RUN: ld.lld -r --gc-sections -e _start %t.o -o %tstart.ro +# RUN: llvm-readelf -Ss %tstart.ro | FileCheck %s --check-prefix=KEEP_START + +# KEEP_START: [ 1] .text +# KEEP_START-NEXT: [ 2] .rela.text +# KEEP_START-NEXT: [ 3] qux +# KEEP_START-NEXT: [ 4] .group +# KEEP_START-NEXT: [ 5] .fred +# KEEP_START-NEXT: [ 6] .rela.fred +# KEEP_START-NEXT: [ 7] .note.GNU-stack + +# KEEP_START: Symbol table '.symtab' contains 10 entries: +# KEEP_START: 5: {{.*}} SECTION +# KEEP_START-NEXT: 6: {{.*}} UND __start_qux +# KEEP_START-NEXT: 7: {{.*}} 1 _start +# KEEP_START-NEXT: 8: {{.*}} 5 fred +# KEEP_START-NEXT: 9: {{.*}} UND fred_und + +.section qux,"a",@progbits + .byte 0 + +.text +.globl _start, bar, foo, fred +_start: + call fred + .quad __start_qux + +.section .text.bar,"axG",@progbits,group,comdat +bar: + .byte 1 +.section .text.foo,"axG",@progbits,group,comdat +foo: + .byte 2 +.section .fred,"ax",@progbits +fred: + call fred_und