diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -21,7 +21,6 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SetVector.h" #include "llvm/MC/StringTableBuilder.h" -#include "llvm/Object/MachO.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" @@ -477,15 +476,24 @@ // The code signature comes at the very end of the linked output file. class CodeSignatureSection final : public LinkEditSection { public: + static constexpr uint8_t blockSizeShift = 12; + static constexpr size_t blockSize = (1 << blockSizeShift); // 4 KiB + static constexpr size_t hashSize = 256 / 8; + static constexpr size_t blobHeadersSize = llvm::alignTo<8>( + sizeof(llvm::MachO::CS_SuperBlob) + sizeof(llvm::MachO::CS_BlobIndex)); + static constexpr uint32_t fixedHeadersSize = + blobHeadersSize + sizeof(llvm::MachO::CS_CodeDirectory); + + uint32_t fileNamePad = 0; + uint32_t allHeadersSize = 0; + StringRef fileName; + CodeSignatureSection(); uint64_t getRawSize() const override; bool isNeeded() const override { return true; } void writeTo(uint8_t *buf) const override; + uint32_t getBlockCount() const; void writeHashes(uint8_t *buf) const; - void finalize() override; - -private: - std::unique_ptr sectionBuilder; }; class BitcodeBundleSection final : public SyntheticSection { diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -24,6 +24,11 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" +#include "llvm/Support/SHA256.h" + +#if defined(__APPLE__) +#include +#endif #ifdef LLVM_HAVE_LIBXAR #include @@ -1146,30 +1151,97 @@ } } +static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0, ""); +static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0, ""); + CodeSignatureSection::CodeSignatureSection() : LinkEditSection(segment_names::linkEdit, section_names::codeSignature) { - align = object::CodeSignatureSection::Align; // required by libstuff + align = 16; // required by libstuff + // FIXME: Consider using finalOutput instead of outputFile. + fileName = config->outputFile; + size_t slashIndex = fileName.rfind("/"); + if (slashIndex != std::string::npos) + fileName = fileName.drop_front(slashIndex + 1); + allHeadersSize = alignTo<16>(fixedHeadersSize + fileName.size() + 1); + fileNamePad = allHeadersSize - fixedHeadersSize - fileName.size(); +} + +uint32_t CodeSignatureSection::getBlockCount() const { + return (fileOff + blockSize - 1) / blockSize; } uint64_t CodeSignatureSection::getRawSize() const { - return static_cast(sectionBuilder->getRawSize()); + return allHeadersSize + getBlockCount() * hashSize; } void CodeSignatureSection::writeHashes(uint8_t *buf) const { - sectionBuilder->write(buf); + uint8_t *code = buf; + uint8_t *codeEnd = buf + fileOff; + uint8_t *hashes = codeEnd + allHeadersSize; + while (code < codeEnd) { + StringRef block(reinterpret_cast(code), + std::min(codeEnd - code, static_cast(blockSize))); + SHA256 hasher; + hasher.update(block); + StringRef hash = hasher.final(); + assert(hash.size() == hashSize); + memcpy(hashes, hash.data(), hashSize); + code += blockSize; + hashes += hashSize; + } +#if defined(__APPLE__) + // This is macOS-specific work-around and makes no sense for any + // other host OS. See https://openradar.appspot.com/FB8914231 + // + // The macOS kernel maintains a signature-verification cache to + // quickly validate applications at time of execve(2). The trouble + // is that for the kernel creates the cache entry at the time of the + // mmap(2) call, before we have a chance to write either the code to + // sign or the signature header+hashes. The fix is to invalidate + // all cached data associated with the output file, thus discarding + // the bogus prematurely-cached signature. + msync(buf, fileOff + getSize(), MS_INVALIDATE); +#endif } void CodeSignatureSection::writeTo(uint8_t *buf) const { - // The entire code section including header is written - // in CodeSignatureSection::writeHashes above. -} - -void CodeSignatureSection::finalize() { + uint32_t signatureSize = static_cast(getSize()); + auto *superBlob = reinterpret_cast(buf); + write32be(&superBlob->magic, CSMAGIC_EMBEDDED_SIGNATURE); + write32be(&superBlob->length, signatureSize); + write32be(&superBlob->count, 1); + auto *blobIndex = reinterpret_cast(&superBlob[1]); + write32be(&blobIndex->type, CSSLOT_CODEDIRECTORY); + write32be(&blobIndex->offset, blobHeadersSize); + auto *codeDirectory = + reinterpret_cast(buf + blobHeadersSize); + write32be(&codeDirectory->magic, CSMAGIC_CODEDIRECTORY); + write32be(&codeDirectory->length, signatureSize - blobHeadersSize); + write32be(&codeDirectory->version, CS_SUPPORTSEXECSEG); + write32be(&codeDirectory->flags, CS_ADHOC | CS_LINKER_SIGNED); + write32be(&codeDirectory->hashOffset, + sizeof(CS_CodeDirectory) + fileName.size() + fileNamePad); + write32be(&codeDirectory->identOffset, sizeof(CS_CodeDirectory)); + codeDirectory->nSpecialSlots = 0; + write32be(&codeDirectory->nCodeSlots, getBlockCount()); + write32be(&codeDirectory->codeLimit, fileOff); + codeDirectory->hashSize = static_cast(hashSize); + codeDirectory->hashType = kSecCodeSignatureHashSHA256; + codeDirectory->platform = 0; + codeDirectory->pageSize = blockSizeShift; + codeDirectory->spare2 = 0; + codeDirectory->scatterOffset = 0; + codeDirectory->teamOffset = 0; + codeDirectory->spare3 = 0; + codeDirectory->codeLimit64 = 0; OutputSegment *textSeg = getOrCreateOutputSegment(segment_names::text); - // NOTE: ld64 seems to also use outputFile instead of finalOutput - sectionBuilder = std::make_unique( - fileOff, config->outputFile, config->outputType, textSeg->fileOff, - textSeg->fileSize); + write64be(&codeDirectory->execSegBase, textSeg->fileOff); + write64be(&codeDirectory->execSegLimit, textSeg->fileSize); + write64be(&codeDirectory->execSegFlags, + config->outputType == MH_EXECUTE ? CS_EXECSEG_MAIN_BINARY : 0); + auto *id = reinterpret_cast(&codeDirectory[1]); + memcpy(id, fileName.begin(), fileName.size()); + memset(id + fileName.size(), 0, fileNamePad); } BitcodeBundleSection::BitcodeBundleSection() diff --git a/llvm/include/llvm/Object/MachO.h b/llvm/include/llvm/Object/MachO.h --- a/llvm/include/llvm/Object/MachO.h +++ b/llvm/include/llvm/Object/MachO.h @@ -733,44 +733,6 @@ return OwningObject; } -class CodeSignatureSection { -public: - uint32_t getRawSize() const; - uint32_t getSize() const; - - static constexpr int Align = 16; - static constexpr uint8_t BlockSizeShift = 12; - static constexpr size_t BlockSize = (1 << BlockSizeShift); // 4 KiB - static constexpr size_t HashSize = 256 / 8; - static constexpr size_t BlobHeadersSize = - alignTo<8>(sizeof(MachO::CS_SuperBlob) + sizeof(MachO::CS_BlobIndex)); - static constexpr uint32_t FixedHeadersSize = - BlobHeadersSize + sizeof(MachO::CS_CodeDirectory); - - CodeSignatureSection(uint64_t FileOff, StringRef OutputFilePath, - MachO::HeaderFileType OutputFileType, - uint64_t TextSegmentFileOff, - uint64_t TextSegmentFileSize); - - void write(uint8_t *Buf) const; - -private: - uint32_t getAllHeadersSize() const; - uint32_t getBlockCount() const; - uint32_t getFileNamePad() const; - - StringRef stripOutputFilePath(const StringRef OutputFilePath); - - // FileOff is the offset relative to the start of the file - // used to access the start of code signature section - // in __LINKEDIT segment - uint64_t FileOff; - StringRef OutputFileName; - MachO::HeaderFileType OutputFileType; - uint64_t TextSegmentFileOff; - uint64_t TextSegmentFileSize; -}; - } // end namespace object } // end namespace llvm diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt --- a/llvm/lib/Object/CMakeLists.txt +++ b/llvm/lib/Object/CMakeLists.txt @@ -2,7 +2,6 @@ Archive.cpp ArchiveWriter.cpp Binary.cpp - CodeSignatureSection.cpp COFFImportFile.cpp COFFModuleDefinition.cpp COFFObjectFile.cpp diff --git a/llvm/lib/Object/CodeSignatureSection.cpp b/llvm/lib/Object/CodeSignatureSection.cpp deleted file mode 100644 --- a/llvm/lib/Object/CodeSignatureSection.cpp +++ /dev/null @@ -1,142 +0,0 @@ -//===- CodeSignatureSection.cpp - CodeSignatureSection class definition ---===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This file defines the CodeSignatureSection class -// -//===----------------------------------------------------------------------===// - -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Object/MachO.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/SHA256.h" -#include - -#if defined(__APPLE__) -#include -#endif - -using namespace llvm; -using namespace object; -using namespace support::endian; - -static_assert((CodeSignatureSection::BlobHeadersSize % 8) == 0, ""); -static_assert((CodeSignatureSection::FixedHeadersSize % 8) == 0, ""); - -CodeSignatureSection::CodeSignatureSection(uint64_t FileOff, - StringRef OutputFilePath, - MachO::HeaderFileType OutputFileType, - uint64_t TextSegmentFileOff, - uint64_t TextSegmentFileSize) - : FileOff{FileOff}, OutputFileName{stripOutputFilePath(OutputFilePath)}, - OutputFileType{OutputFileType}, TextSegmentFileOff{TextSegmentFileOff}, - TextSegmentFileSize{TextSegmentFileSize} {} - -StringRef -CodeSignatureSection::stripOutputFilePath(const StringRef OutputFilePath) { - const size_t LastSlashIndex = OutputFilePath.rfind("/"); - if (LastSlashIndex == std::string::npos) - return OutputFilePath; - - return OutputFilePath.drop_front(LastSlashIndex + 1); -} - -uint32_t CodeSignatureSection::getAllHeadersSize() const { - return alignTo(FixedHeadersSize + OutputFileName.size() + 1); -} - -uint32_t CodeSignatureSection::getBlockCount() const { - return (FileOff + BlockSize - 1) / BlockSize; -} - -uint32_t CodeSignatureSection::getFileNamePad() const { - return getAllHeadersSize() - FixedHeadersSize - OutputFileName.size(); -} - -uint32_t CodeSignatureSection::getRawSize() const { - return getAllHeadersSize() + getBlockCount() * HashSize; -} - -uint32_t CodeSignatureSection::getSize() const { - return alignTo(getRawSize()); -} - -void CodeSignatureSection::write(uint8_t *Buf) const { - const uint32_t AllHeadersSize = getAllHeadersSize(); - const uint32_t BlockCount = getBlockCount(); - const uint32_t FileNamePad = getFileNamePad(); - const uint32_t Size = getSize(); - - uint8_t *Code = Buf; - uint8_t *CodeEnd = Buf + FileOff; - uint8_t *Hashes = CodeEnd + AllHeadersSize; - - // Write code section header. - auto *SuperBlob = reinterpret_cast(CodeEnd); - write32be(&SuperBlob->magic, MachO::CSMAGIC_EMBEDDED_SIGNATURE); - write32be(&SuperBlob->length, Size); - write32be(&SuperBlob->count, 1); - auto *BlobIndex = reinterpret_cast(&SuperBlob[1]); - write32be(&BlobIndex->type, MachO::CSSLOT_CODEDIRECTORY); - write32be(&BlobIndex->offset, BlobHeadersSize); - auto *CodeDirectory = - reinterpret_cast(CodeEnd + BlobHeadersSize); - write32be(&CodeDirectory->magic, MachO::CSMAGIC_CODEDIRECTORY); - write32be(&CodeDirectory->length, Size - BlobHeadersSize); - write32be(&CodeDirectory->version, MachO::CS_SUPPORTSEXECSEG); - write32be(&CodeDirectory->flags, MachO::CS_ADHOC | MachO::CS_LINKER_SIGNED); - write32be(&CodeDirectory->hashOffset, sizeof(MachO::CS_CodeDirectory) + - OutputFileName.size() + - FileNamePad); - write32be(&CodeDirectory->identOffset, sizeof(MachO::CS_CodeDirectory)); - CodeDirectory->nSpecialSlots = 0; - write32be(&CodeDirectory->nCodeSlots, BlockCount); - write32be(&CodeDirectory->codeLimit, FileOff); - CodeDirectory->hashSize = static_cast(HashSize); - CodeDirectory->hashType = MachO::kSecCodeSignatureHashSHA256; - CodeDirectory->platform = 0; - CodeDirectory->pageSize = BlockSizeShift; - CodeDirectory->spare2 = 0; - CodeDirectory->scatterOffset = 0; - CodeDirectory->teamOffset = 0; - CodeDirectory->spare3 = 0; - CodeDirectory->codeLimit64 = 0; - write64be(&CodeDirectory->execSegBase, TextSegmentFileOff); - write64be(&CodeDirectory->execSegLimit, TextSegmentFileSize); - write64be(&CodeDirectory->execSegFlags, OutputFileType == MachO::MH_EXECUTE - ? MachO::CS_EXECSEG_MAIN_BINARY - : 0); - auto *Id = reinterpret_cast(&CodeDirectory[1]); - memcpy(Id, OutputFileName.begin(), OutputFileName.size()); - memset(Id + OutputFileName.size(), 0, FileNamePad); - - // Write code section signature. - while (Code < CodeEnd) { - StringRef Block(reinterpret_cast(Code), - std::min(CodeEnd - Code, static_cast(BlockSize))); - SHA256 Hasher; - Hasher.update(Block); - StringRef Hash = Hasher.final(); - assert(Hash.size() == HashSize); - memcpy(Hashes, Hash.data(), HashSize); - Code += BlockSize; - Hashes += HashSize; - } -#if defined(__APPLE__) - // This is macOS-specific work-around and makes no sense for any - // other host OS. See https://openradar.appspot.com/FB8914231 - // - // The macOS kernel maintains a signature-verification cache to - // quickly validate applications at time of execve(2). The trouble - // is that for the kernel creates the cache entry at the time of the - // mmap(2) call, before we have a chance to write either the code to - // sign or the signature header+hashes. The fix is to invalidate - // all cached data associated with the output file, thus discarding - // the bogus prematurely-cached signature. - msync(Buf, FileOff + Size, MS_INVALIDATE); -#endif -}