Skip to content

[AArch64] Add support for SHF_AARCH64_PURECODE ELF section flag (1/3) #125687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions llvm/include/llvm/BinaryFormat/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -1299,8 +1299,11 @@ enum : unsigned {
// Section data is string data by default.
SHF_MIPS_STRING = 0x80000000,

// Make code section unreadable when in execute-only mode
SHF_ARM_PURECODE = 0x20000000
// Section contains only program instructions and no program data.
SHF_ARM_PURECODE = 0x20000000,

// Section contains only program instructions and no program data.
SHF_AARCH64_PURECODE = 0x20000000
};

// Section Group Flags
Expand Down
16 changes: 10 additions & 6 deletions llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ static unsigned getELFSectionType(StringRef Name, SectionKind K) {
return ELF::SHT_PROGBITS;
}

static unsigned getELFSectionFlags(SectionKind K) {
static unsigned getELFSectionFlags(SectionKind K, const Triple &T) {
unsigned Flags = 0;

if (!K.isMetadata() && !K.isExclude())
Expand All @@ -559,8 +559,12 @@ static unsigned getELFSectionFlags(SectionKind K) {
if (K.isText())
Flags |= ELF::SHF_EXECINSTR;

if (K.isExecuteOnly())
Flags |= ELF::SHF_ARM_PURECODE;
if (K.isExecuteOnly()) {
if (T.isAArch64())
Flags |= ELF::SHF_AARCH64_PURECODE;
else if (T.isARM() || T.isThumb())
Flags |= ELF::SHF_ARM_PURECODE;
}

if (K.isWriteable())
Flags |= ELF::SHF_WRITE;
Expand Down Expand Up @@ -840,7 +844,7 @@ static MCSection *selectExplicitSectionGlobal(const GlobalObject *GO,
// Infer section flags from the section name if we can.
Kind = getELFKindForNamedSection(SectionName, Kind);

unsigned Flags = getELFSectionFlags(Kind);
unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
auto [Group, IsComdat, ExtraFlags] = getGlobalObjectInfo(GO, TM);
Flags |= ExtraFlags;

Expand Down Expand Up @@ -947,7 +951,7 @@ static MCSection *selectELFSectionForGlobal(

MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
unsigned Flags = getELFSectionFlags(Kind);
unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());

// If we have -ffunction-section or -fdata-section then we should emit the
// global value to a uniqued section specifically for it.
Expand All @@ -967,7 +971,7 @@ MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
MCSection *TargetLoweringObjectFileELF::getUniqueSectionForFunction(
const Function &F, const TargetMachine &TM) const {
SectionKind Kind = SectionKind::getText();
unsigned Flags = getELFSectionFlags(Kind);
unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
// If the function's section names is pre-determined via pragma or a
// section attribute, call selectExplicitSectionGlobal.
if (F.hasSection())
Expand Down
7 changes: 5 additions & 2 deletions llvm/lib/MC/MCParser/ELFAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,12 @@ static unsigned parseSectionFlags(const Triple &TT, StringRef flagsStr,
flags |= ELF::XCORE_SHF_DP_SECTION;
break;
case 'y':
if (!(TT.isARM() || TT.isThumb()))
if (TT.isARM() || TT.isThumb())
flags |= ELF::SHF_ARM_PURECODE;
else if (TT.isAArch64())
flags |= ELF::SHF_AARCH64_PURECODE;
else
return -1U;
flags |= ELF::SHF_ARM_PURECODE;
break;
case 's':
if (TT.getArch() != Triple::hexagon)
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/MC/MCSectionELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
} else if (T.isARM() || T.isThumb()) {
if (Flags & ELF::SHF_ARM_PURECODE)
OS << 'y';
} else if (T.isAArch64()) {
if (Flags & ELF::SHF_AARCH64_PURECODE)
OS << 'y';
} else if (Arch == Triple::hexagon) {
if (Flags & ELF::SHF_HEX_GPREL)
OS << 's';
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/ObjectYAML/ELFYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,9 @@ void ScalarBitSetTraits<ELFYAML::ELF_SHF>::bitset(IO &IO,
break;
}
switch (Object->getMachine()) {
case ELF::EM_AARCH64:
BCase(SHF_AARCH64_PURECODE);
break;
case ELF::EM_ARM:
BCase(SHF_ARM_PURECODE);
break;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/AArch64/AArch64Features.td
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,11 @@ def FeatureStrictAlign : SubtargetFeature<"strict-align",
"Disallow all unaligned memory "
"access">;

def FeatureExecuteOnly : SubtargetFeature<"execute-only",
"GenExecuteOnly", "true",
"Enable the generation of "
"execute only code.">;

foreach i = {1-7,9-15,18,20-28} in
def FeatureReserveX#i : SubtargetFeature<"reserve-x"#i, "ReserveXRegister["#i#"]", "true",
"Reserve X"#i#", making it unavailable "
Expand Down
26 changes: 26 additions & 0 deletions llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,29 @@ MCSymbol *AArch64_MachoTargetObjectFile::getAuthPtrSlotSymbol(
return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, MachOMMI, RawSym,
Key, Discriminator);
}

static bool isExecuteOnlyFunction(const GlobalObject *GO, SectionKind Kind,
const TargetMachine &TM) {
if (const Function *F = dyn_cast<Function>(GO))
if (TM.getSubtarget<AArch64Subtarget>(*F).genExecuteOnly() && Kind.isText())
return true;
return false;
}

MCSection *AArch64_ELFTargetObjectFile::getExplicitSectionGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
// Set execute-only access for the explicit section
if (isExecuteOnlyFunction(GO, Kind, TM))
Kind = SectionKind::getExecuteOnly();

return TargetLoweringObjectFileELF::getExplicitSectionGlobal(GO, Kind, TM);
}

MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
// Set execute-only access for the explicit section
if (isExecuteOnlyFunction(GO, Kind, TM))
Kind = SectionKind::getExecuteOnly();

return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
}
6 changes: 6 additions & 0 deletions llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
void emitPersonalityValueImpl(MCStreamer &Streamer, const DataLayout &DL,
const MCSymbol *Sym,
const MachineModuleInfo *MMI) const override;

MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
const TargetMachine &TM) const override;

MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
const TargetMachine &TM) const override;
};

/// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
Expand Down
18 changes: 17 additions & 1 deletion llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ bool AArch64TTIImpl::isMultiversionedFunction(const Function &F) const {
return F.hasFnAttribute("fmv-features");
}

const FeatureBitset AArch64TTIImpl::InlineInverseFeatures = {
AArch64::FeatureExecuteOnly,
};

bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
const Function *Callee) const {
SMEAttrs CallerAttrs(*Caller), CalleeAttrs(*Callee);
Expand All @@ -284,7 +288,19 @@ bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
return false;
}

return BaseT::areInlineCompatible(Caller, Callee);
const TargetMachine &TM = getTLI()->getTargetMachine();
const FeatureBitset &CallerBits =
TM.getSubtargetImpl(*Caller)->getFeatureBits();
const FeatureBitset &CalleeBits =
TM.getSubtargetImpl(*Callee)->getFeatureBits();
// Adjust the feature bitsets by inverting some of the bits. This is needed
// for target features that represent restrictions rather than capabilities,
// for example a "+execute-only" callee can be inlined into a caller without
// "+execute-only", but not vice versa.
FeatureBitset EffectiveCallerBits = CallerBits ^ InlineInverseFeatures;
FeatureBitset EffectiveCalleeBits = CalleeBits ^ InlineInverseFeatures;

return (EffectiveCallerBits & EffectiveCalleeBits) == EffectiveCalleeBits;
}

bool AArch64TTIImpl::areTypesABICompatible(
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class AArch64TTIImpl : public BasicTTIImplBase<AArch64TTIImpl> {
const AArch64Subtarget *ST;
const AArch64TargetLowering *TLI;

static const FeatureBitset InlineInverseFeatures;

const AArch64Subtarget *getST() const { return ST; }
const AArch64TargetLowering *getTLI() const { return TLI; }

Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "llvm/MC/MCELFStreamer.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
Expand Down Expand Up @@ -504,6 +505,23 @@ void AArch64TargetELFStreamer::finish() {
}
}

// The mix of execute-only and non-execute-only at link time is
// non-execute-only. To avoid the empty implicitly created .text
// section from making the whole .text section non-execute-only, we
// mark it execute-only if it is empty and there is at least one
// execute-only section in the object.
if (any_of(Asm, [](const MCSection &Sec) {
return cast<MCSectionELF>(Sec).getFlags() & ELF::SHF_AARCH64_PURECODE;
})) {
auto *Text =
static_cast<MCSectionELF *>(Ctx.getObjectFileInfo()->getTextSection());
for (auto &F : *Text)
if (auto *DF = dyn_cast<MCDataFragment>(&F))
if (!DF->getContents().empty())
return;
Text->setFlags(Text->getFlags() | ELF::SHF_AARCH64_PURECODE);
}

MCSectionELF *MemtagSec = nullptr;
for (const MCSymbol &Symbol : Asm.symbols()) {
const auto &Sym = cast<MCSymbolELF>(Symbol);
Expand Down
39 changes: 39 additions & 0 deletions llvm/test/CodeGen/AArch64/execute-only-section.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
; RUN: llc -mtriple=aarch64 -mattr=+execute-only %s -o - | FileCheck %s

$test_comdat = comdat any

; CHECK: .section .text,"axy",@progbits,unique,0
; CHECK-NOT: .section
; CHECK-NOT: .text
; CHECK: .globl test_section_for_global
; CHECK: .type test_section_for_global,@function
define void @test_section_for_global() {
entry:
ret void
}

; CHECK: .section .text.test_comdat,"axGy",@progbits,test_comdat,comdat,unique,0
; CHECK-NOT: .section
; CHECK-NOT: .text
; CHECK: .weak test_comdat
; CHECK: .type test_comdat,@function
define linkonce_odr void @test_comdat() comdat {
entry:
ret void
}

; CHECK: .section .test,"axy",@progbits
; CHECK-NOT: .section
; CHECK-NOT: .text
; CHECK: .globl test_explicit_section_for_global
; CHECK: .type test_explicit_section_for_global,@function
define void @test_explicit_section_for_global() section ".test" {
entry:
ret void
}

; CHECK: .rodata,"a",@progbits
; CHECK-NOT: .section
; CHECK-NOT: .text
; CHECK: .globl test_rodata
@test_rodata = constant i32 0, align 4
27 changes: 27 additions & 0 deletions llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
// RUN: | llvm-readobj -S --symbols - | FileCheck %s

.text
ret

.section .text.foo,"axy"
ret

// CHECK: Section {
// CHECK: Name: .text
// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
// CHECK-NEXT: Flags [ (0x6)
// CHECK-NEXT: SHF_ALLOC (0x2)
// CHECK-NEXT: SHF_EXECINSTR (0x4)
// CHECK-NEXT: ]
// CHECK: }

// CHECK: Section {
// CHECK: Name: .text.foo
// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
// CHECK-NEXT: Flags [ (0x20000006)
// CHECK-NEXT: SHF_AARCH64_PURECODE (0x20000000)
// CHECK-NEXT: SHF_ALLOC (0x2)
// CHECK-NEXT: SHF_EXECINSTR (0x4)
// CHECK-NEXT: ]
// CHECK: }
55 changes: 55 additions & 0 deletions llvm/test/MC/ELF/AArch64/execute-only-section.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
// RUN: | llvm-readobj -S --symbols - | FileCheck %s --check-prefix=READOBJ
// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
// RUN: | llvm-readelf -S --symbols - | FileCheck %s --check-prefix=READELF

.section .text,"axy",@progbits,unique,0
.globl foo
.p2align 2
.type foo,@function
foo:
.cfi_startproc
ret
.Lfunc_end0:
.size foo, .Lfunc_end0-foo
.cfi_endproc

// READOBJ: Section {
// READOBJ: Name: .text
// READOBJ-NEXT: Type: SHT_PROGBITS (0x1)
// READOBJ-NEXT: Flags [ (0x20000006)
// READOBJ-NEXT: SHF_AARCH64_PURECODE (0x20000000)
// READOBJ-NEXT: SHF_ALLOC (0x2)
// READOBJ-NEXT: SHF_EXECINSTR (0x4)
// READOBJ-NEXT: ]
// READOBJ-NEXT: Address:
// READOBJ-NEXT: Offset:
// READOBJ-NEXT: Size: 0
// READOBJ: }

// READOBJ: Section {
// READOBJ: Name: .text
// READOBJ-NEXT: Type: SHT_PROGBITS (0x1)
// READOBJ-NEXT: Flags [ (0x20000006)
// READOBJ-NEXT: SHF_AARCH64_PURECODE (0x20000000)
// READOBJ-NEXT: SHF_ALLOC (0x2)
// READOBJ-NEXT: SHF_EXECINSTR (0x4)
// READOBJ-NEXT: ]
// READOBJ-NEXT: Address:
// READOBJ-NEXT: Offset:
// READOBJ-NEXT: Size: 4
// READOBJ: }

// READOBJ: Symbol {
// READOBJ: Name: foo
// READOBJ-NEXT: Value:
// READOBJ-NEXT: Size: 4
// READOBJ-NEXT: Binding: Global
// READOBJ-NEXT: Type: Function
// READOBJ-NEXT: Other:
// READOBJ-NEXT: Section: .text
// READOBJ: }

// READELF: Section Headers:
// READELF: .text PROGBITS {{[0-9a-f]+}} {{[0-9a-f]+}} 000000 {{[0-9a-f]+}} AXy {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
// READELF: .text PROGBITS {{[0-9a-f]+}} {{[0-9a-f]+}} 000004 {{[0-9a-f]+}} AXy {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
27 changes: 27 additions & 0 deletions llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
// RUN: | llvm-readobj -S --symbols - | FileCheck %s

.text
.ascii "test"

.section .text.foo,"axy"
ret

// CHECK: Section {
// CHECK: Name: .text
// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
// CHECK-NEXT: Flags [ (0x6)
// CHECK-NEXT: SHF_ALLOC (0x2)
// CHECK-NEXT: SHF_EXECINSTR (0x4)
// CHECK-NEXT: ]
// CHECK: }

// CHECK: Section {
// CHECK: Name: .text.foo
// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
// CHECK-NEXT: Flags [ (0x20000006)
// CHECK-NEXT: SHF_AARCH64_PURECODE (0x20000000)
// CHECK-NEXT: SHF_ALLOC (0x2)
// CHECK-NEXT: SHF_EXECINSTR (0x4)
// CHECK-NEXT: ]
// CHECK: }
5 changes: 3 additions & 2 deletions llvm/test/MC/ELF/section-flags-unknown.s
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
# CHECK: {{.*}}.s:[[# @LINE+1]]:27: error: unknown flag
.section SHF_HEX_GPREL,"s",@progbits

# CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
.section SHF_ARM_PURECODE,"y",@progbits
## Test SHF_ARM_PURECODE and SHF_AARCH64_PURECODE section flags
# CHECK: {{.*}}.s:[[# @LINE+1]]:22: error: unknown flag
.section purecode,"y",@progbits

# CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
.section SHF_X86_64_LARGE,"l",@progbits
Loading
Loading