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