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