| //===- 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); | |
| } |