xref: /aosp_15_r20/external/pigweed/pw_elf/reader.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_elf/reader.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/endian.h"
18*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
20*61c4878aSAndroid Build Coastguard Worker 
21*61c4878aSAndroid Build Coastguard Worker namespace pw::elf {
22*61c4878aSAndroid Build Coastguard Worker namespace {
23*61c4878aSAndroid Build Coastguard Worker 
24*61c4878aSAndroid Build Coastguard Worker using ElfIdent = std::array<unsigned char, EI_NIDENT>;
25*61c4878aSAndroid Build Coastguard Worker 
ElfIdentGetEndian(const ElfIdent & e_ident)26*61c4878aSAndroid Build Coastguard Worker Result<pw::endian> ElfIdentGetEndian(const ElfIdent& e_ident) {
27*61c4878aSAndroid Build Coastguard Worker   switch (e_ident[EI_DATA]) {
28*61c4878aSAndroid Build Coastguard Worker     case ELFDATA2LSB:
29*61c4878aSAndroid Build Coastguard Worker       // Encoding ELFDATA2LSB specifies 2's complement values, with the least
30*61c4878aSAndroid Build Coastguard Worker       // significant byte occupying the lowest address.
31*61c4878aSAndroid Build Coastguard Worker       return pw::endian::little;
32*61c4878aSAndroid Build Coastguard Worker     case ELFDATA2MSB:
33*61c4878aSAndroid Build Coastguard Worker       // Encoding ELFDATA2MSB specifies 2's complement values, with the most
34*61c4878aSAndroid Build Coastguard Worker       // significant byte occupying the lowest address.
35*61c4878aSAndroid Build Coastguard Worker       return pw::endian::big;
36*61c4878aSAndroid Build Coastguard Worker     default:
37*61c4878aSAndroid Build Coastguard Worker       return Status::OutOfRange();
38*61c4878aSAndroid Build Coastguard Worker   }
39*61c4878aSAndroid Build Coastguard Worker }
40*61c4878aSAndroid Build Coastguard Worker 
41*61c4878aSAndroid Build Coastguard Worker enum class ElfClass {
42*61c4878aSAndroid Build Coastguard Worker   kElf32,
43*61c4878aSAndroid Build Coastguard Worker   kElf64,
44*61c4878aSAndroid Build Coastguard Worker };
45*61c4878aSAndroid Build Coastguard Worker 
ElfIdentGetElfClass(const ElfIdent & e_ident)46*61c4878aSAndroid Build Coastguard Worker Result<ElfClass> ElfIdentGetElfClass(const ElfIdent& e_ident) {
47*61c4878aSAndroid Build Coastguard Worker   switch (e_ident[EI_CLASS]) {
48*61c4878aSAndroid Build Coastguard Worker     case ELFCLASS32:
49*61c4878aSAndroid Build Coastguard Worker       return ElfClass::kElf32;
50*61c4878aSAndroid Build Coastguard Worker     case ELFCLASS64:
51*61c4878aSAndroid Build Coastguard Worker       return ElfClass::kElf64;
52*61c4878aSAndroid Build Coastguard Worker     default:
53*61c4878aSAndroid Build Coastguard Worker       return Status::OutOfRange();
54*61c4878aSAndroid Build Coastguard Worker   }
55*61c4878aSAndroid Build Coastguard Worker }
56*61c4878aSAndroid Build Coastguard Worker 
MakeReaderImpl(ElfClass elf_class,stream::SeekableReader & stream)57*61c4878aSAndroid Build Coastguard Worker Result<internal::ElfReaderImpls> MakeReaderImpl(
58*61c4878aSAndroid Build Coastguard Worker     ElfClass elf_class, stream::SeekableReader& stream) {
59*61c4878aSAndroid Build Coastguard Worker   switch (elf_class) {
60*61c4878aSAndroid Build Coastguard Worker     case ElfClass::kElf32:
61*61c4878aSAndroid Build Coastguard Worker       return internal::ElfReaderImpl32::FromStream(stream);
62*61c4878aSAndroid Build Coastguard Worker #if UINTPTR_MAX == UINT64_MAX
63*61c4878aSAndroid Build Coastguard Worker     case ElfClass::kElf64:
64*61c4878aSAndroid Build Coastguard Worker       return internal::ElfReaderImpl64::FromStream(stream);
65*61c4878aSAndroid Build Coastguard Worker #endif
66*61c4878aSAndroid Build Coastguard Worker     default:
67*61c4878aSAndroid Build Coastguard Worker       return Status::Unimplemented();
68*61c4878aSAndroid Build Coastguard Worker   }
69*61c4878aSAndroid Build Coastguard Worker }
70*61c4878aSAndroid Build Coastguard Worker 
71*61c4878aSAndroid Build Coastguard Worker }  // namespace
72*61c4878aSAndroid Build Coastguard Worker 
FromStream(stream::SeekableReader & stream)73*61c4878aSAndroid Build Coastguard Worker Result<ElfReader> ElfReader::FromStream(stream::SeekableReader& stream) {
74*61c4878aSAndroid Build Coastguard Worker   PW_TRY(stream.Seek(0));
75*61c4878aSAndroid Build Coastguard Worker 
76*61c4878aSAndroid Build Coastguard Worker   // Read the e_ident field of the ELF header.
77*61c4878aSAndroid Build Coastguard Worker   PW_TRY_ASSIGN(ElfIdent e_ident, internal::ReadObject<ElfIdent>(stream));
78*61c4878aSAndroid Build Coastguard Worker   PW_TRY(stream.Seek(0));
79*61c4878aSAndroid Build Coastguard Worker 
80*61c4878aSAndroid Build Coastguard Worker   // Validate e_ident.
81*61c4878aSAndroid Build Coastguard Worker   if (!(e_ident[EI_MAG0] == ELFMAG0 && e_ident[EI_MAG1] == ELFMAG1 &&
82*61c4878aSAndroid Build Coastguard Worker         e_ident[EI_MAG2] == ELFMAG2 && e_ident[EI_MAG3] == ELFMAG3)) {
83*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Invalid ELF magic bytes");
84*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
85*61c4878aSAndroid Build Coastguard Worker   }
86*61c4878aSAndroid Build Coastguard Worker 
87*61c4878aSAndroid Build Coastguard Worker   auto endian = ElfIdentGetEndian(e_ident);
88*61c4878aSAndroid Build Coastguard Worker   if (!endian.ok()) {
89*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
90*61c4878aSAndroid Build Coastguard Worker   }
91*61c4878aSAndroid Build Coastguard Worker   if (endian.value() != pw::endian::native) {
92*61c4878aSAndroid Build Coastguard Worker     // Only native endian is supported.
93*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Non-native ELF endian not supported");
94*61c4878aSAndroid Build Coastguard Worker     return Status::Unimplemented();
95*61c4878aSAndroid Build Coastguard Worker   }
96*61c4878aSAndroid Build Coastguard Worker 
97*61c4878aSAndroid Build Coastguard Worker   auto elf_class = ElfIdentGetElfClass(e_ident);
98*61c4878aSAndroid Build Coastguard Worker   if (!elf_class.ok()) {
99*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
100*61c4878aSAndroid Build Coastguard Worker   }
101*61c4878aSAndroid Build Coastguard Worker 
102*61c4878aSAndroid Build Coastguard Worker   PW_TRY_ASSIGN(auto impl, MakeReaderImpl(elf_class.value(), stream));
103*61c4878aSAndroid Build Coastguard Worker   return ElfReader(std::move(impl));
104*61c4878aSAndroid Build Coastguard Worker }
105*61c4878aSAndroid Build Coastguard Worker 
ReadSection(std::string_view name)106*61c4878aSAndroid Build Coastguard Worker Result<std::vector<std::byte>> ElfReader::ReadSection(std::string_view name) {
107*61c4878aSAndroid Build Coastguard Worker   StatusWithSize size = SeekToSection(name);
108*61c4878aSAndroid Build Coastguard Worker   if (!size.ok()) {
109*61c4878aSAndroid Build Coastguard Worker     return size.status();
110*61c4878aSAndroid Build Coastguard Worker   }
111*61c4878aSAndroid Build Coastguard Worker 
112*61c4878aSAndroid Build Coastguard Worker   std::vector<std::byte> data;
113*61c4878aSAndroid Build Coastguard Worker   data.resize(size.size());
114*61c4878aSAndroid Build Coastguard Worker   PW_TRY(stream().ReadExact(data));
115*61c4878aSAndroid Build Coastguard Worker 
116*61c4878aSAndroid Build Coastguard Worker   return data;
117*61c4878aSAndroid Build Coastguard Worker }
118*61c4878aSAndroid Build Coastguard Worker 
119*61c4878aSAndroid Build Coastguard Worker namespace internal {
120*61c4878aSAndroid Build Coastguard Worker 
121*61c4878aSAndroid Build Coastguard Worker // TODO(jrreinhart): Move to pw_stream
ReadNullTermString(stream::Reader & stream)122*61c4878aSAndroid Build Coastguard Worker Result<std::string> ReadNullTermString(stream::Reader& stream) {
123*61c4878aSAndroid Build Coastguard Worker   std::string result;
124*61c4878aSAndroid Build Coastguard Worker   while (true) {
125*61c4878aSAndroid Build Coastguard Worker     PW_TRY_ASSIGN(char c, ReadObject<char>(stream));
126*61c4878aSAndroid Build Coastguard Worker     if (c == '\0') {
127*61c4878aSAndroid Build Coastguard Worker       break;
128*61c4878aSAndroid Build Coastguard Worker     }
129*61c4878aSAndroid Build Coastguard Worker     result += c;
130*61c4878aSAndroid Build Coastguard Worker   }
131*61c4878aSAndroid Build Coastguard Worker   return result;
132*61c4878aSAndroid Build Coastguard Worker }
133*61c4878aSAndroid Build Coastguard Worker 
134*61c4878aSAndroid Build Coastguard Worker }  // namespace internal
135*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::elf
136