Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 .gnu.property.note 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 .gnu.property.note 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();
}

/// 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 .gnu.property.note 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,
"out of bounds while reading .gnu.property.note 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 .gnu.property.note 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.
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.
Loading