1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include "pw_assert/assert.h" 18 #include "pw_elf/internal/elf.h" 19 #include "pw_elf/internal/stream_utils.h" 20 #include "pw_status/try.h" 21 #include "pw_stream/stream.h" 22 23 namespace pw::elf::internal { 24 25 template <class Elf_Ehdr, class Elf_Shdr> 26 class ElfReaderImpl { 27 public: FromStream(stream::SeekableReader & stream)28 static Result<ElfReaderImpl> FromStream(stream::SeekableReader& stream) { 29 ElfReaderImpl reader(stream); 30 PW_TRY(reader.Initialize()); 31 return reader; 32 } 33 stream()34 stream::SeekableReader& stream() const { return stream_; } 35 SeekToSection(std::string_view name)36 StatusWithSize SeekToSection(std::string_view name) { 37 for (unsigned int i = 0; i < sec_hdr_count(); ++i) { 38 // Read the section header 39 PW_TRY_WITH_SIZE(SeekToSectionHeader(i)); 40 auto section_hdr_ret = ReadObject<Elf_Shdr>(stream_); 41 PW_TRY_WITH_SIZE(section_hdr_ret.status()); 42 Elf_Shdr section_hdr = std::move(section_hdr_ret.value()); 43 44 // Read the section name string 45 PW_TRY_WITH_SIZE( 46 stream_.Seek(str_table_data_off() + section_hdr.sh_name)); 47 auto section_name = ReadNullTermString(stream_); 48 PW_TRY_WITH_SIZE(section_name.status()); 49 50 if (section_name.value() == name) { 51 PW_TRY_WITH_SIZE(stream_.Seek(section_hdr.sh_offset)); 52 return StatusWithSize(section_hdr.sh_size); 53 } 54 } 55 56 return StatusWithSize::NotFound(); 57 } 58 59 private: ElfReaderImpl(stream::SeekableReader & stream)60 ElfReaderImpl(stream::SeekableReader& stream) : stream_(stream) {} 61 Initialize()62 Status Initialize() { 63 PW_TRY(stream_.Seek(0)); 64 65 // Read ELF file header 66 PW_TRY_ASSIGN(file_header_, ReadObject<Elf_Ehdr>(stream_)); 67 68 // Note: e_ident already validated 69 70 // Validate section header size 71 if (file_header_->e_shentsize < sizeof(Elf_Shdr)) { 72 return Status::DataLoss(); 73 } 74 75 // Read string table section header 76 PW_TRY(SeekToSectionHeader(file_header_->e_shstrndx)); 77 PW_TRY_ASSIGN(str_table_sec_hdr_, ReadObject<Elf_Shdr>(stream_)); 78 79 return OkStatus(); 80 } 81 SeekToSectionHeader(unsigned int index)82 Status SeekToSectionHeader(unsigned int index) { 83 PW_ASSERT(file_header_); 84 return stream_.Seek(file_header_->e_shoff + 85 (index * file_header_->e_shentsize)); 86 } 87 str_table_data_off()88 ptrdiff_t str_table_data_off() const { 89 PW_ASSERT(str_table_sec_hdr_); 90 return str_table_sec_hdr_->sh_offset; 91 } 92 sec_hdr_count()93 unsigned int sec_hdr_count() const { 94 PW_ASSERT(file_header_); 95 return file_header_->e_shnum; 96 } 97 98 stream::SeekableReader& stream_; 99 std::optional<Elf_Ehdr> file_header_; 100 std::optional<Elf_Shdr> str_table_sec_hdr_; 101 }; 102 103 using ElfReaderImpl32 = ElfReaderImpl<Elf32_Ehdr, Elf32_Shdr>; 104 105 #if UINTPTR_MAX == UINT64_MAX 106 using ElfReaderImpl64 = ElfReaderImpl<Elf64_Ehdr, Elf64_Shdr>; 107 #endif 108 109 using ElfReaderImpls = std::variant<ElfReaderImpl32 110 #if UINTPTR_MAX == UINT64_MAX 111 , 112 ElfReaderImpl64 113 #endif 114 >; 115 } // namespace pw::elf::internal 116