xref: /aosp_15_r20/external/pigweed/pw_elf/public/pw_elf/internal/reader_impl.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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