xref: /aosp_15_r20/external/pigweed/pw_elf/reader.cc (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 #include "pw_elf/reader.h"
16 
17 #include "pw_bytes/endian.h"
18 #include "pw_log/log.h"
19 #include "pw_status/try.h"
20 
21 namespace pw::elf {
22 namespace {
23 
24 using ElfIdent = std::array<unsigned char, EI_NIDENT>;
25 
ElfIdentGetEndian(const ElfIdent & e_ident)26 Result<pw::endian> ElfIdentGetEndian(const ElfIdent& e_ident) {
27   switch (e_ident[EI_DATA]) {
28     case ELFDATA2LSB:
29       // Encoding ELFDATA2LSB specifies 2's complement values, with the least
30       // significant byte occupying the lowest address.
31       return pw::endian::little;
32     case ELFDATA2MSB:
33       // Encoding ELFDATA2MSB specifies 2's complement values, with the most
34       // significant byte occupying the lowest address.
35       return pw::endian::big;
36     default:
37       return Status::OutOfRange();
38   }
39 }
40 
41 enum class ElfClass {
42   kElf32,
43   kElf64,
44 };
45 
ElfIdentGetElfClass(const ElfIdent & e_ident)46 Result<ElfClass> ElfIdentGetElfClass(const ElfIdent& e_ident) {
47   switch (e_ident[EI_CLASS]) {
48     case ELFCLASS32:
49       return ElfClass::kElf32;
50     case ELFCLASS64:
51       return ElfClass::kElf64;
52     default:
53       return Status::OutOfRange();
54   }
55 }
56 
MakeReaderImpl(ElfClass elf_class,stream::SeekableReader & stream)57 Result<internal::ElfReaderImpls> MakeReaderImpl(
58     ElfClass elf_class, stream::SeekableReader& stream) {
59   switch (elf_class) {
60     case ElfClass::kElf32:
61       return internal::ElfReaderImpl32::FromStream(stream);
62 #if UINTPTR_MAX == UINT64_MAX
63     case ElfClass::kElf64:
64       return internal::ElfReaderImpl64::FromStream(stream);
65 #endif
66     default:
67       return Status::Unimplemented();
68   }
69 }
70 
71 }  // namespace
72 
FromStream(stream::SeekableReader & stream)73 Result<ElfReader> ElfReader::FromStream(stream::SeekableReader& stream) {
74   PW_TRY(stream.Seek(0));
75 
76   // Read the e_ident field of the ELF header.
77   PW_TRY_ASSIGN(ElfIdent e_ident, internal::ReadObject<ElfIdent>(stream));
78   PW_TRY(stream.Seek(0));
79 
80   // Validate e_ident.
81   if (!(e_ident[EI_MAG0] == ELFMAG0 && e_ident[EI_MAG1] == ELFMAG1 &&
82         e_ident[EI_MAG2] == ELFMAG2 && e_ident[EI_MAG3] == ELFMAG3)) {
83     PW_LOG_ERROR("Invalid ELF magic bytes");
84     return Status::DataLoss();
85   }
86 
87   auto endian = ElfIdentGetEndian(e_ident);
88   if (!endian.ok()) {
89     return Status::DataLoss();
90   }
91   if (endian.value() != pw::endian::native) {
92     // Only native endian is supported.
93     PW_LOG_ERROR("Non-native ELF endian not supported");
94     return Status::Unimplemented();
95   }
96 
97   auto elf_class = ElfIdentGetElfClass(e_ident);
98   if (!elf_class.ok()) {
99     return Status::DataLoss();
100   }
101 
102   PW_TRY_ASSIGN(auto impl, MakeReaderImpl(elf_class.value(), stream));
103   return ElfReader(std::move(impl));
104 }
105 
ReadSection(std::string_view name)106 Result<std::vector<std::byte>> ElfReader::ReadSection(std::string_view name) {
107   StatusWithSize size = SeekToSection(name);
108   if (!size.ok()) {
109     return size.status();
110   }
111 
112   std::vector<std::byte> data;
113   data.resize(size.size());
114   PW_TRY(stream().ReadExact(data));
115 
116   return data;
117 }
118 
119 namespace internal {
120 
121 // TODO(jrreinhart): Move to pw_stream
ReadNullTermString(stream::Reader & stream)122 Result<std::string> ReadNullTermString(stream::Reader& stream) {
123   std::string result;
124   while (true) {
125     PW_TRY_ASSIGN(char c, ReadObject<char>(stream));
126     if (c == '\0') {
127       break;
128     }
129     result += c;
130   }
131   return result;
132 }
133 
134 }  // namespace internal
135 }  // namespace pw::elf
136