//===- 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 decodeGNUPropertyNote(StringRef Desc); public: GNUPropertyRewriter(StringRef Name, BinaryContext &BC) : MetadataRewriter(Name, BC) {} Error sectionInitializer() override; }; Error GNUPropertyRewriter::sectionInitializer() { ErrorOr 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) : ""; Cursor.seek(alignTo(Cursor.tell() + NameSz, 4)); const uint64_t DescOffset = Cursor.tell(); StringRef Desc = DescSz ? Buf.slice(DescOffset, DescOffset + DescSz) : ""; 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 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 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 llvm::bolt::createGNUPropertyRewriter(BinaryContext &BC) { return std::make_unique("gnu-property-rewriter", BC); }