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