ELF: Create unique SHF_GNU_RETAIN sections for llvm.used global objects

If a global object is listed in `@llvm.used`, place it in a unique section with
the `SHF_GNU_RETAIN` flag. The section is a GC root under `ld --gc-sections`
with LLD>=13 or GNU ld>=2.36.

For front ends which do not expect to see multiple sections of the same name,
consider emitting `@llvm.compiler.used` instead of `@llvm.used`.

SHF_GNU_RETAIN is restricted to ELFOSABI_GNU and ELFOSABI_FREEBSD in
binutils. We don't do the restriction - see the rationale in D95749.

The integrated assembler has supported SHF_GNU_RETAIN since D95730.
GNU as>=2.36 supports section flag 'R'.
We don't need to worry about GNU ld support because older GNU ld just ignores
the unknown SHF_GNU_RETAIN.

With this change, `__attribute__((retain))` functions/variables emitted
by clang will get the SHF_GNU_RETAIN flag.

Differential Revision: https://reviews.llvm.org/D97448
This commit is contained in:
Fangrui Song 2021-02-26 16:38:44 -08:00
parent 8afdacba9d
commit 47c5576d7d
6 changed files with 110 additions and 11 deletions

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H
#define LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/BinaryFormat/XCOFF.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
@ -32,6 +33,7 @@ class TargetMachine;
class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
bool UseInitArray = false;
mutable unsigned NextUniqueID = 1; // ID 0 is reserved for execute-only sections
SmallPtrSet<GlobalObject *, 2> Used;
protected:
MCSymbolRefExpr::VariantKind PLTRelativeVariantKind =
@ -43,6 +45,8 @@ public:
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
void getModuleMetadata(Module &M) override;
/// Emit Obj-C garbage collection and linker options.
void emitModuleMetadata(MCStreamer &Streamer, Module &M) const override;

View File

@ -293,6 +293,14 @@ void TargetLoweringObjectFileELF::Initialize(MCContext &Ctx,
}
}
void TargetLoweringObjectFileELF::getModuleMetadata(Module &M) {
SmallVector<GlobalValue *, 4> Vec;
collectUsedGlobalVariables(M, Vec, false);
for (GlobalValue *GV : Vec)
if (auto *GO = dyn_cast<GlobalObject>(GV))
Used.insert(GO);
}
void TargetLoweringObjectFileELF::emitModuleMetadata(MCStreamer &Streamer,
Module &M) const {
auto &C = getContext();
@ -687,9 +695,15 @@ MCSection *TargetLoweringObjectFileELF::getExplicitSectionGlobal(
// MD_associated in a unique section.
unsigned UniqueID = MCContext::GenericSectionID;
const MCSymbolELF *LinkedToSym = getLinkedToSymbol(GO, TM);
if (GO->getMetadata(LLVMContext::MD_associated)) {
const bool Associated = GO->getMetadata(LLVMContext::MD_associated);
const bool Retain = Used.count(GO);
if (Associated || Retain) {
UniqueID = NextUniqueID++;
Flags |= ELF::SHF_LINK_ORDER;
if (Associated)
Flags |= ELF::SHF_LINK_ORDER;
if (Retain && (getContext().getAsmInfo()->useIntegratedAssembler() ||
getContext().getAsmInfo()->binutilsIsAtLeast(2, 36)))
Flags |= ELF::SHF_GNU_RETAIN;
} else {
if (getContext().getAsmInfo()->useIntegratedAssembler() ||
getContext().getAsmInfo()->binutilsIsAtLeast(2, 35)) {
@ -802,13 +816,18 @@ static MCSectionELF *selectELFSectionForGlobal(
static MCSection *selectELFSectionForGlobal(
MCContext &Ctx, const GlobalObject *GO, SectionKind Kind, Mangler &Mang,
const TargetMachine &TM, bool EmitUniqueSection, unsigned Flags,
unsigned *NextUniqueID) {
const TargetMachine &TM, bool Retain, bool EmitUniqueSection,
unsigned Flags, unsigned *NextUniqueID) {
const MCSymbolELF *LinkedToSym = getLinkedToSymbol(GO, TM);
if (LinkedToSym) {
EmitUniqueSection = true;
Flags |= ELF::SHF_LINK_ORDER;
}
if (Retain && (Ctx.getAsmInfo()->useIntegratedAssembler() ||
Ctx.getAsmInfo()->binutilsIsAtLeast(2, 36))) {
EmitUniqueSection = true;
Flags |= ELF::SHF_GNU_RETAIN;
}
MCSectionELF *Section = selectELFSectionForGlobal(
Ctx, GO, Kind, Mang, TM, EmitUniqueSection, Flags,
@ -832,16 +851,17 @@ MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
}
EmitUniqueSection |= GO->hasComdat();
return selectELFSectionForGlobal(getContext(), GO, Kind, getMangler(), TM,
EmitUniqueSection, Flags, &NextUniqueID);
Used.count(GO), EmitUniqueSection, Flags,
&NextUniqueID);
}
MCSection *TargetLoweringObjectFileELF::getUniqueSectionForFunction(
const Function &F, const TargetMachine &TM) const {
SectionKind Kind = SectionKind::getText();
unsigned Flags = getELFSectionFlags(Kind);
return selectELFSectionForGlobal(getContext(), &F, Kind, getMangler(), TM,
/* EmitUniqueSection = */ true, Flags,
&NextUniqueID);
return selectELFSectionForGlobal(
getContext(), &F, Kind, getMangler(), TM, Used.count(&F),
/*EmitUniqueSection=*/true, Flags, &NextUniqueID);
}
MCSection *TargetLoweringObjectFileELF::getSectionForJumpTable(

View File

@ -84,6 +84,7 @@ MCSection *RISCVELFTargetObjectFile::SelectSectionForGlobal(
}
void RISCVELFTargetObjectFile::getModuleMetadata(Module &M) {
TargetLoweringObjectFileELF::getModuleMetadata(M);
SmallVector<Module::ModuleFlagEntry, 8> ModuleFlags;
M.getModuleFlagsMetadata(ModuleFlags);

View File

@ -12,5 +12,5 @@ entry:
ret void
}
; CHECK: .section gsection,"aw",@progbits
; CHECK: .section hsection,"aw",@progbits
; CHECK: .section gsection,"awR",@progbits
; CHECK: .section hsection,"awR",@progbits

View File

@ -1,6 +1,6 @@
; RUN: llc -verify-machineinstrs -mtriple=powerpc-unknown-linux-gnu < %s | FileCheck %s
; CHECK: .section .bss,"aw",@nobits
; CHECK: .section .bss.X,"awR",@nobits
; CHECK: .weak X
; CHECK-LABEL: X:
; CHECK: .long 0

View File

@ -0,0 +1,74 @@
;; Place a global object in the llvm.used list in a unique section with the SHF_GNU_RETAIN flag.
; RUN: llc -mtriple=x86_64 < %s | FileCheck %s
; RUN: llc -mtriple=x86_64 -data-sections=1 < %s | FileCheck %s
; RUN: llc -mtriple=x86_64 -no-integrated-as -binutils-version=2.36 < %s | FileCheck %s
; RUN: llc -mtriple=x86_64 -no-integrated-as -binutils-version=2.35 < %s | FileCheck %s --check-prefix=OLDGAS
; RUN: llc -mtriple=x86_64 -data-sections=1 -unique-section-names=0 < %s | FileCheck %s --check-prefix=NOUNIQUE
@llvm.used = appending global [10 x i8*] [
i8* bitcast (void ()* @fa to i8*), i8* bitcast (void ()* @fb to i8*), i8* bitcast (void ()* @fc to i8*),
i8* bitcast (i32* @ga to i8*), i8* bitcast (i32* @gb to i8*), i8* bitcast (i32* @gc to i8*), i8* bitcast (i32* @gd to i8*), i8* bitcast (i32* @ge to i8*),
i8* bitcast (i32* @aa to i8*), i8* bitcast (i32* @ab to i8*) ], section "llvm.metadata"
; CHECK: .section .text.fa,"axR",@progbits{{$}}
; OLDGAS-NOT: .section .text
; NOUNIQUE: .section .text,"axR",@progbits,unique,1
define dso_local void @fa() {
entry:
ret void
}
; CHECK: .section .text.fb,"axR",@progbits{{$}}
; NOUNIQUE: .section .text,"axR",@progbits,unique,2
define internal void @fb() {
entry:
ret void
}
;; Explicit section.
; CHECK: .section ccc,"axR",@progbits,unique,1
; OLDGAS: .section ccc,"ax",@progbits,unique,1
; NOUNIQUE: .section ccc,"axR",@progbits,unique,3
define dso_local void @fc() section "ccc" {
entry:
ret void
}
; CHECK: .section .bss.ga,"awR",@nobits{{$}}
; OLDGAS: .bss{{$}}
; NOUNIQUE: .section .bss,"awR",@nobits,unique,4
@ga = global i32 0
; CHECK: .section .data.gb,"awR",@progbits{{$}}
; OLDGAS: .data{{$}}
; NOUNIQUE: .section .data,"awR",@progbits,unique,5
@gb = internal global i32 2
; CHECK: .section .rodata.gc,"aR",@progbits{{$}}
; OLDGAS: .section .rodata,"a",@progbits{{$}}
; NOUNIQUE: .section .rodata,"aR",@progbits,unique,6
@gc = constant i32 3
;; Explicit section.
; CHECK: .section ddd,"awR",@progbits,unique,2
; OLDGAS: .section ddd,"aw",@progbits,unique,2
; NOUNIQUE: .section ddd,"awR",@progbits,unique,7
@gd = global i32 1, section "ddd"
;; Used together with !associated.
; CHECK: .section .data.ge,"awoR",@progbits,gc
; OLDGAS: .section .data.ge,"awo",@progbits,gc
; NOUNIQUE: .section .data,"awoR",@progbits,gc,unique,8
@ge = global i32 1, !associated !0
;; Aliases in llvm.used are ignored.
; CHECK: .section fff,"aw",@progbits{{$}}
; OLDGAS: .section fff,"aw",@progbits{{$}}
; NOUNIQUE: .section fff,"aw",@progbits{{$}}
@gf = global i32 1, section "fff"
@aa = alias i32, i32* @gf
@ab = internal alias i32, i32* @gf
!0 = !{i32* @gc}