`AddressSize` parameter is not used by `DataExtractor` and will be removed in the future. See #190519 for more context. I took the liberty of switching from using the `StringRef` constructor overload to `ArrayRef` where appropriate.
145 lines
4.8 KiB
C++
145 lines
4.8 KiB
C++
//===- 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());
|
|
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-INFO: binary is using BTI\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());
|
|
DataExtractor::Cursor Cursor(0);
|
|
const uint32_t Align = BC.AsmInfo->getCodePointerSize();
|
|
|
|
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);
|
|
}
|