blob: f61c08ec46fe6bd786c714e4efcb21a3d1865763 [file] [log] [blame]
//===- 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);
}