forked from GPUOpen-Drivers/llpc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
When LLPC checks shader and pipeline caches, it uses the hash for the identifier to check if required ELF files exist in the cache file. Since the cache file keeps the mapping between the hashes and the ELF files, we do not keep the hash value in the ELF file. On the other hand, we need the cache creator proposed in GPUOpen-Drivers/xgl#64 to build the cache for relocatable shader ELFs, but the cache creator does not have any information about the hash. It is because the hash is generated by LLPC (or amdllpc) and the cache creator is a separate program. We want to let the LLPC compiler create a new note section for the cache hash. It can be used for the cache creator to create the cache file with the correct mapping between the hash and ELF. ELF layouts before/after adding new note entries for cache hash and LLPC version to the existing note section: ``` |-------------| |-------------| | ELF header | | ELF header | |-------------| |-------------| | Sections | | Sections | | ... | | ... | |-------------| |-------------| | Note section| ==> | Note section| | ... | | | |-------------|<--(Will be | + New note | | ... | Shifted) | entries | |-------------| | | | ... | | Section | | \->|-------------| | headers | V | ... | | ... | |-------------| | Section | | headers | | ... | ``` New note entries: 1. Note name with "llpc_cache_hash" and note description with the cache hash used for the cache lookup. 2. Note name with "llpc_version" and note description with the LLPC version - both major and minor. The LLPC version information will help us to understand the hash generation algorithm. We have to use a correct hash algorithm for the cache lookup. For example, if the hash is "4EDBED25 ADF15238 B8C92579 423DA423" and the LLPC version is 45.4 (the major version is 45=0x2D and the minor version is 4=0x04), two new note entries will be ``` Unknown(0) (name = llpc_cache_hash size = 16) 0:4EDBED25 ADF15238 B8C92579 423DA423 Unknown(0) (name = llpc_version size = 8) 0:0000002D 00000004 ```
- Loading branch information
Showing
12 changed files
with
517 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
*********************************************************************************************************************** | ||
* | ||
* Copyright (c) 2021 Google LLC. All Rights Reserved. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
* | ||
**********************************************************************************************************************/ | ||
/** | ||
*********************************************************************************************************************** | ||
* @file ElfNoteEntryInsertionUtil.h | ||
* @brief LLPC header file: declaration of lgc::AddNotesToELF interface | ||
* | ||
* @details The function AddNotesToELF adds given note entries to the given ELF. | ||
*********************************************************************************************************************** | ||
*/ | ||
|
||
#pragma once | ||
#include "llvm/ADT/ArrayRef.h" | ||
#include "llvm/ADT/SmallVector.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Support/CommandLine.h" | ||
|
||
namespace lgc { | ||
|
||
// Note entry that will be added to the note section. | ||
struct NoteEntry { | ||
llvm::StringRef name; | ||
llvm::ArrayRef<uint8_t> desc; | ||
}; | ||
|
||
// ===================================================================================================================== | ||
// Adds the given note entries to the given ELF if the given ELF has a note section. | ||
// Otherwise, it does nothing. | ||
// | ||
// ELF layouts before/after adding new "note" entries to the existing note section: | ||
// | ||
// |-------------| |-------------| | ||
// | ELF header | | ELF header | | ||
// |-------------| |-------------| | ||
// | Sections | | Sections | | ||
// | ... | | ... | | ||
// |-------------| |-------------| | ||
// | Note section| ==> | Note section| | ||
// | ... | | | | ||
// |-------------|<--(Will be | + New note | | ||
// | ... | Shifted) | entries | | ||
// |-------------| | | | ... | | ||
// | Section | | \->|-------------| | ||
// | headers | V | ... | | ||
// | ... | |-------------| | ||
// | Section | | ||
// | headers | | ||
// | ... | | ||
// | ||
void AddNotesToELF(llvm::SmallVectorImpl<char> &elf, llvm::ArrayRef<NoteEntry> notes, const char *noteSectionName); | ||
|
||
} // namespace lgc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
/* | ||
*********************************************************************************************************************** | ||
* | ||
* Copyright (c) 2021 Google LLC. All Rights Reserved. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
* | ||
**********************************************************************************************************************/ | ||
#include "lgc/ElfNoteEntryInsertionUtil.h" | ||
#include "llvm/ADT/SmallString.h" | ||
#include "llvm/ADT/StringExtras.h" | ||
#include "llvm/Object/ELF.h" | ||
#include "llvm/Support/Alignment.h" | ||
#include "llvm/Support/BinaryByteStream.h" | ||
#include "llvm/Support/BinaryStreamReader.h" | ||
#include "llvm/Support/BinaryStreamWriter.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
using namespace llvm; | ||
using namespace lgc; | ||
|
||
// The implementation of ELF rewriting is based on "Linux Programmer's Manual ELF(5)". | ||
// In particular, see "Notes (Nhdr)" for the document of the note section. | ||
|
||
namespace { | ||
|
||
// It is similar to struct NoteHeader defined in llpc/util/vkgcElfReader.h, but | ||
// it allows us to use Elf_Nhdr_Impl<object::ELF64LE>::Align and it does not | ||
// have the size limitation of note name. | ||
using NoteHeader = object::Elf_Nhdr_Impl<object::ELF64LE>; | ||
|
||
// An array of zeros. We use it for the note alignment. | ||
constexpr char ZerosForNoteAlign[NoteHeader::Align] = {'\0'}; | ||
|
||
// ===================================================================================================================== | ||
// Contents of a section to be shifted and its new offset. | ||
struct SectionShiftInfo { | ||
SmallString<64> section; | ||
ELF::Elf64_Off newOffset; | ||
}; | ||
|
||
// ===================================================================================================================== | ||
// Generates a StringRef from the given SmallVector. | ||
// | ||
// @param input : The SmallVector to be converted to StringRef. | ||
// @returns : The generated StringRef. | ||
template <typename T> StringRef stringRefFromSmallVector(const SmallVectorImpl<T> &input) { | ||
return StringRef(reinterpret_cast<const char *>(input.data()), sizeof(T) * input.size()); | ||
} | ||
|
||
// ===================================================================================================================== | ||
// Add an entry to the note section. | ||
// | ||
// Reference: Linux Programmer's Manual ELF(5) "Notes (Nhdr)". | ||
// | ||
// @param noteName : Name of the note entry. | ||
// @param noteDesc : Description of the note entry. | ||
// @param [out] noteEntryWriter : The .note section where the note entry will be added to. | ||
void addNoteEntry(StringRef noteName, ArrayRef<uint8_t> noteDesc, BinaryStreamWriter ¬eEntryWriter) { | ||
NoteHeader noteHeader = {}; | ||
noteHeader.n_namesz = noteName.size() + 1; | ||
noteHeader.n_descsz = noteDesc.size(); | ||
// TODO: Define a note type to specify cache hash and llpc version. It must | ||
// be defined in llvm/include/llvm/BinaryFormat/ELF.h. | ||
// Note types with values between 0 and 32 (inclusive) are reserved. | ||
noteHeader.n_type = 0; | ||
auto error = noteEntryWriter.writeObject(noteHeader); | ||
(void)error; | ||
assert(!error); | ||
|
||
// Write the note name terminated by zero and zeros for the alignment. | ||
error = noteEntryWriter.writeCString(noteName); | ||
assert(!error); | ||
error = noteEntryWriter.writeFixedString(StringRef( | ||
ZerosForNoteAlign, offsetToAlignment(noteEntryWriter.getLength(), Align::Constant<NoteHeader::Align>()))); | ||
assert(!error); | ||
|
||
// Write the note description and zeros for the alignment. | ||
error = noteEntryWriter.writeBytes(noteDesc); | ||
assert(!error); | ||
error = noteEntryWriter.writeFixedString(StringRef( | ||
ZerosForNoteAlign, offsetToAlignment(noteEntryWriter.getLength(), Align::Constant<NoteHeader::Align>()))); | ||
assert(!error); | ||
} | ||
|
||
// ===================================================================================================================== | ||
// Writes note entries to a byte-stream. | ||
// | ||
// @param notes : The array of note entries to be added to the note section. | ||
// @param newNoteEntryOffset : The offset in ELF where the note entries will be added. | ||
// @param [out] noteEntryStream : The byte-stream to be filled with the note entries. | ||
void writeNoteEntriesToByteStream(ArrayRef<NoteEntry> notes, const ELF::Elf64_Off newNoteEntryOffset, | ||
AppendingBinaryByteStream ¬eEntryStream) { | ||
BinaryStreamWriter noteEntryWriter(noteEntryStream); | ||
auto error = noteEntryWriter.writeFixedString( | ||
StringRef(ZerosForNoteAlign, offsetToAlignment(newNoteEntryOffset, Align::Constant<NoteHeader::Align>()))); | ||
(void)error; | ||
assert(!error); | ||
|
||
// Write the note entries. | ||
for (auto ¬e : notes) | ||
addNoteEntry(note.name, note.desc, noteEntryWriter); | ||
} | ||
|
||
// ===================================================================================================================== | ||
// Updates the offsets of sections to their new offsets to be shifted. After updating offsets | ||
// it returns the contents of sections and their new offsets. | ||
// | ||
// @param elf : The input ELF. | ||
// @param shiftStartingOffset : The first offset of ELF contents that will be shifted. | ||
// All sections after this offset will be shifted. | ||
// @param lengthToBeShifted : The length how much sections after shiftStartingOffset will be shift. | ||
// @param sectionHeaders : The array of section headers. | ||
// @param [out] sectionAndNewOffset : The array of the contents of sections to be shifted and their new | ||
// offset sorted by the new offset in the increasing order. | ||
void updateSectionOffsetsForShift(const SmallVectorImpl<char> &elf, const ELF::Elf64_Off shiftStartingOffset, | ||
ELF::Elf64_Off lengthToBeShifted, MutableArrayRef<ELF::Elf64_Shdr> sectionHeaders, | ||
SmallVectorImpl<SectionShiftInfo> §ionAndNewOffset) { | ||
// If a section is located after shiftStartingOffset, it must be shifted. | ||
for (auto §ionHeader : sectionHeaders) { | ||
if (sectionHeader.sh_offset < shiftStartingOffset) | ||
continue; | ||
const auto newOffset = alignTo(sectionHeader.sh_offset + lengthToBeShifted, Align(sectionHeader.sh_addralign)); | ||
sectionAndNewOffset.push_back( | ||
{SmallString<64>(StringRef(elf.data() + sectionHeader.sh_offset, sectionHeader.sh_size)), newOffset}); | ||
lengthToBeShifted = newOffset - sectionHeader.sh_offset; | ||
|
||
// Update the offset of section pointed by the section header to its new offset. | ||
sectionHeader.sh_offset = newOffset; | ||
} | ||
|
||
// Sort sectionAndNewOffset by the new offset of each section in the increasing order. | ||
sort(sectionAndNewOffset, | ||
[](const SectionShiftInfo &i0, const SectionShiftInfo &i1) { return i0.newOffset < i1.newOffset; }); | ||
} | ||
|
||
// ===================================================================================================================== | ||
// Inserts the new contents to the given ELF. | ||
// | ||
// @param [in/out] elf : The ELF to insert the new contents. | ||
// @param insertionOffset : The offset of the new contents to be inserted. | ||
// @param elfContentStream : The ELF contents to be inserted. | ||
// @param sectionAndNewOffset : The sections to be shifted and their new offsets. | ||
void insertContentsToELF(SmallVectorImpl<char> &elf, const ELF::Elf64_Off insertionOffset, | ||
AppendingBinaryByteStream &elfContentStream, | ||
const SmallVectorImpl<SectionShiftInfo> §ionAndNewOffset) { | ||
// Strip sections after the insertion offset of the new contents. | ||
elf.resize(insertionOffset); | ||
|
||
// Write the new contents. | ||
raw_svector_ostream elfStream(elf); | ||
ArrayRef<uint8_t> noteEntry; | ||
auto error = elfContentStream.readBytes(0, elfContentStream.getLength(), noteEntry); | ||
(void)error; | ||
assert(!error); | ||
elfStream << toStringRef(noteEntry); | ||
|
||
// Write the sections after the insertion offset. | ||
for (const auto §ionAndNewOffsetInfo : sectionAndNewOffset) { | ||
elfStream.write_zeros(sectionAndNewOffsetInfo.newOffset - elfStream.str().size()); | ||
elfStream << sectionAndNewOffsetInfo.section.str(); | ||
} | ||
} | ||
|
||
// ===================================================================================================================== | ||
// Write the section header table to the given offset. | ||
// | ||
// @param [in/out] elf : The ELF to write the section header table. | ||
// @param sectionHeaderTableOffset : The offset where it will write the section header table. | ||
// @param sectionHeaderTable : The section header table to be written to the ELF. | ||
void writeSectionHeaderTable(SmallVectorImpl<char> &elf, const ELF::Elf64_Off sectionHeaderTableOffset, | ||
const SmallVectorImpl<ELF::Elf64_Shdr> §ionHeaderTable) { | ||
if (sectionHeaderTable.size() == 0) | ||
return; | ||
|
||
raw_svector_ostream elfStream(elf); | ||
const unsigned minElfSizeForSectionHeaders = | ||
sectionHeaderTableOffset + sizeof(ELF::Elf64_Shdr) * sectionHeaderTable.size(); | ||
if (minElfSizeForSectionHeaders > elf.size()) | ||
elfStream.write_zeros(minElfSizeForSectionHeaders - elf.size()); | ||
auto sectionHeaderTableInString = stringRefFromSmallVector(sectionHeaderTable); | ||
elfStream.pwrite(sectionHeaderTableInString.data(), sectionHeaderTableInString.size(), sectionHeaderTableOffset); | ||
} | ||
|
||
} // anonymous namespace | ||
|
||
namespace lgc { | ||
|
||
// ===================================================================================================================== | ||
// Adds the given note entries to the note section with the given section name in the given ELF. | ||
// If the note section with the given name does not exist, it uses any other note section. | ||
// | ||
// @param [in/out] elf : ELF to be updated with the new note entries. | ||
// @param notes : An array of note entries to be inserted to the existing note section. | ||
// @param noteSectionName : The name of note section to where note entries will be inserted. | ||
void AddNotesToELF(SmallVectorImpl<char> &elf, llvm::ArrayRef<NoteEntry> notes, const char *noteSectionName) { | ||
// Get ELF header that contains information for section header table offset | ||
// and the number of section headers. | ||
// | ||
// Reference: http://www.skyfree.org/linux/references/ELF_Format.pdf | ||
ELF::Elf64_Ehdr *ehdr = reinterpret_cast<ELF::Elf64_Ehdr *>(elf.data()); | ||
|
||
// Get the section headers and the existing note section whose name is noteSectionName. | ||
MutableArrayRef<ELF::Elf64_Shdr> sectionHeaders(reinterpret_cast<ELF::Elf64_Shdr *>(elf.data() + ehdr->e_shoff), | ||
ehdr->e_shnum); | ||
auto existingNoteSection = | ||
find_if(sectionHeaders, [&elf, ehdr, §ionHeaders, noteSectionName](const ELF::Elf64_Shdr §ionHeader) { | ||
if (sectionHeader.sh_type != ELF::SHT_NOTE) | ||
return false; | ||
const char *stringTableForSectionNames = elf.data() + sectionHeaders[ehdr->e_shstrndx].sh_offset; | ||
return !strcmp(noteSectionName, &stringTableForSectionNames[sectionHeader.sh_name]); | ||
}); | ||
// If a note section with noteSectionName does not exist, use any other note section. | ||
if (existingNoteSection == sectionHeaders.end()) { | ||
existingNoteSection = find_if( | ||
sectionHeaders, [](const ELF::Elf64_Shdr §ionHeader) { return sectionHeader.sh_type == ELF::SHT_NOTE; }); | ||
} | ||
|
||
// We assume that the given ELF already contains a note section. Since AMD GPU | ||
// accepts only ELFs with AMD related metadata, the assumption will be satisfied. | ||
assert(existingNoteSection != sectionHeaders.end()); | ||
|
||
// Prepare the new note entries to be added to the existing note section. | ||
const ELF::Elf64_Off newNoteEntryOffset = existingNoteSection->sh_offset + existingNoteSection->sh_size; | ||
AppendingBinaryByteStream noteEntryStream(support::little); | ||
writeNoteEntriesToByteStream(notes, newNoteEntryOffset, noteEntryStream); | ||
|
||
// Get the last section located just before the section header table. | ||
auto sectionBeforeSectionHeaderTable = | ||
std::max_element(sectionHeaders.begin(), sectionHeaders.end(), | ||
[ehdr](const ELF::Elf64_Shdr &largest, const ELF::Elf64_Shdr ¤t) { | ||
if (current.sh_offset > ehdr->e_shoff) | ||
return false; | ||
return current.sh_offset > largest.sh_offset; | ||
}); | ||
|
||
// Update the offset information of sections after the offset of new note entries. | ||
// The new offset should be the offset where each section will be shifted to. | ||
SmallVector<SectionShiftInfo> sectionAndNewOffset; | ||
updateSectionOffsetsForShift(elf, newNoteEntryOffset, noteEntryStream.getLength(), sectionHeaders, | ||
sectionAndNewOffset); | ||
|
||
// Increase the size of the existing note section to include new note entries. | ||
existingNoteSection->sh_size += noteEntryStream.getLength(); | ||
|
||
// Prepare the section header table shift if we have to shift it. Note that inserting note entries requires | ||
// rewriting sections, which results in overwriting the section header table. Therefore, the contents | ||
// pointed by the MutableArrayRef sectionHeaders will not be the section header table. In that case, we | ||
// have to create a backup for the section header table. | ||
SmallVector<ELF::Elf64_Shdr> sectionHeaderTableBackup; | ||
if (ehdr->e_shoff > newNoteEntryOffset) { | ||
ehdr->e_shoff = sectionBeforeSectionHeaderTable->sh_offset + sectionBeforeSectionHeaderTable->sh_size; | ||
sectionHeaderTableBackup.append(sectionHeaders.begin(), sectionHeaders.end()); | ||
} | ||
|
||
// Insert the stream of note entries to the ELF. | ||
insertContentsToELF(elf, newNoteEntryOffset, noteEntryStream, sectionAndNewOffset); | ||
|
||
// Write section header table. | ||
writeSectionHeaderTable(elf, ehdr->e_shoff, sectionHeaderTableBackup); | ||
} | ||
|
||
} // namespace lgc |
Oops, something went wrong.