Skip to content
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
6 changes: 6 additions & 0 deletions bolt/include/bolt/Core/BinaryContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ class BinaryContext {
/// Unique build ID if available for the binary.
std::optional<std::string> FileBuildID;

/// GNU property note indicating AArch64 BTI.
bool UsesBTI{false};

/// Set of all sections.
struct CompareSections {
bool operator()(const BinarySection *A, const BinarySection *B) const {
Expand Down Expand Up @@ -384,6 +387,9 @@ class BinaryContext {
}
void setFileBuildID(StringRef ID) { FileBuildID = std::string(ID); }

bool usesBTI() const { return UsesBTI; }
void setUsesBTI(bool Value) { UsesBTI = Value; }

bool hasSymbolsWithFileName() const { return HasSymbolsWithFileName; }
void setHasSymbolsWithFileName(bool Value) { HasSymbolsWithFileName = Value; }

Expand Down
2 changes: 2 additions & 0 deletions bolt/include/bolt/Rewrite/MetadataRewriters.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ std::unique_ptr<MetadataRewriter> createPseudoProbeRewriter(BinaryContext &);

std::unique_ptr<MetadataRewriter> createSDTRewriter(BinaryContext &);

std::unique_ptr<MetadataRewriter> createGNUPropertyRewriter(BinaryContext &);

} // namespace bolt
} // namespace llvm

Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_llvm_library(LLVMBOLTRewrite
PseudoProbeRewriter.cpp
RewriteInstance.cpp
SDTRewriter.cpp
GNUPropertyRewriter.cpp

NO_EXPORT
DISABLE_LLVM_LINK_LLVM_DYLIB
Expand Down
147 changes: 147 additions & 0 deletions bolt/lib/Rewrite/GNUPropertyRewriter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//===- bolt/Rewrite/GNUPropertyRewriter.cpp -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Read the .note.gnu.property section.
//
//===----------------------------------------------------------------------===//

#include "bolt/Rewrite/MetadataRewriter.h"
#include "bolt/Rewrite/MetadataRewriters.h"
#include "llvm/Support/Errc.h"

using namespace llvm;
using namespace bolt;

namespace {

class GNUPropertyRewriter final : public MetadataRewriter {

Expected<uint32_t> decodeGNUPropertyNote(StringRef Desc);

public:
GNUPropertyRewriter(StringRef Name, BinaryContext &BC)
: MetadataRewriter(Name, BC) {}

Error sectionInitializer() override;
};

Error GNUPropertyRewriter::sectionInitializer() {

ErrorOr<BinarySection &> Sec =
BC.getUniqueSectionByName(".note.gnu.property");
if (!Sec)
return Error::success();

// Accumulate feature bits
uint32_t FeaturesAcc = 0;

StringRef Buf = Sec->getContents();
DataExtractor DE(Buf, BC.AsmInfo->isLittleEndian(),
BC.AsmInfo->getCodePointerSize());
DataExtractor::Cursor Cursor(0);
while (Cursor && !DE.eof(Cursor)) {
const uint32_t NameSz = DE.getU32(Cursor);
const uint32_t DescSz = DE.getU32(Cursor);
const uint32_t Type = DE.getU32(Cursor);

StringRef Name =
NameSz ? Buf.slice(Cursor.tell(), Cursor.tell() + NameSz) : "<empty>";
Cursor.seek(alignTo(Cursor.tell() + NameSz, 4));

const uint64_t DescOffset = Cursor.tell();
StringRef Desc =
DescSz ? Buf.slice(DescOffset, DescOffset + DescSz) : "<empty>";
Cursor.seek(alignTo(DescOffset + DescSz, 4));
if (!Cursor)
return createStringError(
errc::executable_format_error,
"out of bounds while reading .note.gnu.property section: %s",
toString(Cursor.takeError()).c_str());

if (Type == ELF::NT_GNU_PROPERTY_TYPE_0 && Name.starts_with("GNU") &&
DescSz) {
auto Features = decodeGNUPropertyNote(Desc);
if (!Features)
return Features.takeError();
FeaturesAcc |= *Features;
}
}

if (BC.isAArch64()) {
BC.setUsesBTI(FeaturesAcc & llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
if (BC.usesBTI())
BC.outs() << "BOLT-WARNING: binary is using BTI. Optimized binary may be "
"corrupted\n";
}

return Error::success();
}

/// \p Desc contains an array of property descriptors. Each member has the
/// following structure:
/// typedef struct {
/// Elf_Word pr_type;
/// Elf_Word pr_datasz;
/// unsigned char pr_data[PR_DATASZ];
/// unsigned char pr_padding[PR_PADDING];
/// } Elf_Prop;
///
/// As there is no guarantee that the features are encoded in which element of
/// the array, we have to read all, and OR together the result.
Expected<uint32_t> GNUPropertyRewriter::decodeGNUPropertyNote(StringRef Desc) {
DataExtractor DE(Desc, BC.AsmInfo->isLittleEndian(),
BC.AsmInfo->getCodePointerSize());
DataExtractor::Cursor Cursor(0);
const uint32_t Align = DE.getAddressSize();

std::optional<uint32_t> Features = 0;
while (Cursor && !DE.eof(Cursor)) {
const uint32_t PrType = DE.getU32(Cursor);
const uint32_t PrDataSz = DE.getU32(Cursor);

const uint64_t PrDataStart = Cursor.tell();
const uint64_t PrDataEnd = PrDataStart + PrDataSz;
Cursor.seek(PrDataEnd);
if (!Cursor)
return createStringError(
errc::executable_format_error,
"out of bounds while reading .note.gnu.property section: %s",
toString(Cursor.takeError()).c_str());

if (PrType == llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND) {
if (PrDataSz != 4) {
return createStringError(
errc::executable_format_error,
"Property descriptor size has to be 4 bytes on AArch64\n");
}
DataExtractor::Cursor Tmp(PrDataStart);
// PrDataSz = 4 -> PrData is uint32_t
const uint32_t FeaturesItem = DE.getU32(Tmp);
if (!Tmp)
return createStringError(
errc::executable_format_error,
"failed to read property from .note.gnu.property section: %s",
toString(Tmp.takeError()).c_str());
Features = Features ? (*Features | FeaturesItem) : FeaturesItem;
}

Cursor.seek(alignTo(PrDataEnd, Align));
if (!Cursor)
return createStringError(errc::executable_format_error,
"out of bounds while reading property array in "
".note.gnu.property section: %s",
toString(Cursor.takeError()).c_str());
}
return Features.value_or(0u);
}
} // namespace

std::unique_ptr<MetadataRewriter>
llvm::bolt::createGNUPropertyRewriter(BinaryContext &BC) {
return std::make_unique<GNUPropertyRewriter>("gnu-property-rewriter", BC);
}
2 changes: 2 additions & 0 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3341,6 +3341,8 @@ void RewriteInstance::initializeMetadataManager() {
MetadataManager.registerRewriter(createPseudoProbeRewriter(*BC));

MetadataManager.registerRewriter(createSDTRewriter(*BC));

MetadataManager.registerRewriter(createGNUPropertyRewriter(*BC));
}

void RewriteInstance::processSectionMetadata() {
Expand Down
50 changes: 50 additions & 0 deletions bolt/test/AArch64/Inputs/property-note-bti.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_AARCH64
Entry: 0x400510
ProgramHeaders:
- Type: PT_NOTE
Flags: [ PF_R ]
FirstSec: .note.gnu.property
LastSec: .note.gnu.property
VAddr: 0x400338
Align: 0x8
- Type: PT_LOAD
Flags: [ PF_R ]
VAddr: 0x0
Align: 0x10000
FileSize: 0xf8
MemSize: 0xf8
Offset: 0x0
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x2a0000
AddressAlign: 0x4
Content: 400580d2c0035fd6
- Name: .note.gnu.property
Type: SHT_NOTE
Flags: [ SHF_ALLOC ]
Address: 0x400338
AddressAlign: 0x8
Notes:
- Name: GNU
Desc: 000000C0040000000300000000000000
Type: NT_GNU_PROPERTY_TYPE_0
- Type: SectionHeaderTable
Sections:
- Name: .note.gnu.property
- Name: .symtab
- Name: .strtab
- Name: .shstrtab
- Name: .text
Symbols:
- Name: .note.gnu.property
Type: STT_SECTION
Section: .note.gnu.property
Value: 0x400338
...
50 changes: 50 additions & 0 deletions bolt/test/AArch64/Inputs/property-note-nobti.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_AARCH64
Entry: 0x400510
ProgramHeaders:
- Type: PT_NOTE
Flags: [ PF_R ]
FirstSec: .note.gnu.property
LastSec: .note.gnu.property
VAddr: 0x400338
Align: 0x8
- Type: PT_LOAD
Flags: [ PF_R ]
VAddr: 0x0
Align: 0x10000
FileSize: 0xf8
MemSize: 0xf8
Offset: 0x0
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x2a0000
AddressAlign: 0x4
Content: 400580d2c0035fd6
- Name: .note.gnu.property
Type: SHT_NOTE
Flags: [ SHF_ALLOC ]
Address: 0x400338
AddressAlign: 0x8
Notes:
- Name: GNU
Desc: 000000C0040000000200000000000000
Type: NT_GNU_PROPERTY_TYPE_0
- Type: SectionHeaderTable
Sections:
- Name: .note.gnu.property
- Name: .symtab
- Name: .strtab
- Name: .shstrtab
- Name: .text
Symbols:
- Name: .note.gnu.property
Type: STT_SECTION
Section: .note.gnu.property
Value: 0x400338
...
10 changes: 10 additions & 0 deletions bolt/test/AArch64/bti-note.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This test checks that the GNUPropertyRewriter can decode the BTI feature flag.
// It decodes an executable with BTI, and checks for the warning.

RUN: yaml2obj %p/Inputs/property-note-bti.yaml &> %t.exe

RUN: llvm-readelf -n %t.exe | FileCheck %s
CHECK: BTI

RUN: llvm-bolt %t.exe -o %t.exe.bolt | FileCheck %s -check-prefix=CHECK-BOLT
CHECK-BOLT: BOLT-WARNING: binary is using BTI. Optimized binary may be corrupted
10 changes: 10 additions & 0 deletions bolt/test/AArch64/no-bti-note.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This test checks that the GNUPropertyRewriter can decode the BTI feature flag.
// It decodes an executable without BTI, and checks for the warning.

RUN: yaml2obj %p/Inputs/property-note-nobti.yaml &> %t.exe

RUN: llvm-readelf -n %t.exe | FileCheck %s
CHECK-NOT: BTI

RUN: llvm-bolt %t.exe -o %t.exe.bolt | FileCheck %s -check-prefix=CHECK-BOLT
CHECK-BOLT-NOT: BOLT-WARNING: binary is using BTI. Optimized binary may be corrupted
Loading