| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_elf/reader.h" |
| |
| #include "pw_bytes/endian.h" |
| #include "pw_log/log.h" |
| #include "pw_status/try.h" |
| |
| namespace pw::elf { |
| namespace { |
| |
| using ElfIdent = std::array<unsigned char, EI_NIDENT>; |
| |
| Result<pw::endian> ElfIdentGetEndian(const ElfIdent& e_ident) { |
| switch (e_ident[EI_DATA]) { |
| case ELFDATA2LSB: |
| // Encoding ELFDATA2LSB specifies 2's complement values, with the least |
| // significant byte occupying the lowest address. |
| return pw::endian::little; |
| case ELFDATA2MSB: |
| // Encoding ELFDATA2MSB specifies 2's complement values, with the most |
| // significant byte occupying the lowest address. |
| return pw::endian::big; |
| default: |
| return Status::OutOfRange(); |
| } |
| } |
| |
| enum class ElfClass { |
| kElf32, |
| kElf64, |
| }; |
| |
| Result<ElfClass> ElfIdentGetElfClass(const ElfIdent& e_ident) { |
| switch (e_ident[EI_CLASS]) { |
| case ELFCLASS32: |
| return ElfClass::kElf32; |
| case ELFCLASS64: |
| return ElfClass::kElf64; |
| default: |
| return Status::OutOfRange(); |
| } |
| } |
| |
| Result<internal::ElfReaderImpls> MakeReaderImpl( |
| ElfClass elf_class, stream::SeekableReader& stream) { |
| switch (elf_class) { |
| case ElfClass::kElf32: |
| return internal::ElfReaderImpl32::FromStream(stream); |
| #if UINTPTR_MAX == UINT64_MAX |
| case ElfClass::kElf64: |
| return internal::ElfReaderImpl64::FromStream(stream); |
| #endif |
| default: |
| return Status::Unimplemented(); |
| } |
| } |
| |
| } // namespace |
| |
| Result<ElfReader> ElfReader::FromStream(stream::SeekableReader& stream) { |
| PW_TRY(stream.Seek(0)); |
| |
| // Read the e_ident field of the ELF header. |
| PW_TRY_ASSIGN(ElfIdent e_ident, internal::ReadObject<ElfIdent>(stream)); |
| PW_TRY(stream.Seek(0)); |
| |
| // Validate e_ident. |
| if (!(e_ident[EI_MAG0] == ELFMAG0 && e_ident[EI_MAG1] == ELFMAG1 && |
| e_ident[EI_MAG2] == ELFMAG2 && e_ident[EI_MAG3] == ELFMAG3)) { |
| PW_LOG_ERROR("Invalid ELF magic bytes"); |
| return Status::DataLoss(); |
| } |
| |
| auto endian = ElfIdentGetEndian(e_ident); |
| if (!endian.ok()) { |
| return Status::DataLoss(); |
| } |
| if (endian.value() != pw::endian::native) { |
| // Only native endian is supported. |
| PW_LOG_ERROR("Non-native ELF endian not supported"); |
| return Status::Unimplemented(); |
| } |
| |
| auto elf_class = ElfIdentGetElfClass(e_ident); |
| if (!elf_class.ok()) { |
| return Status::DataLoss(); |
| } |
| |
| PW_TRY_ASSIGN(auto impl, MakeReaderImpl(elf_class.value(), stream)); |
| return ElfReader(std::move(impl)); |
| } |
| |
| Result<std::vector<std::byte>> ElfReader::ReadSection(std::string_view name) { |
| StatusWithSize size = SeekToSection(name); |
| if (!size.ok()) { |
| return size.status(); |
| } |
| |
| std::vector<std::byte> data; |
| data.resize(size.size()); |
| PW_TRY(stream().ReadExact(data)); |
| |
| return data; |
| } |
| |
| namespace internal { |
| |
| // TODO(jrreinhart): Move to pw_stream |
| Result<std::string> ReadNullTermString(stream::Reader& stream) { |
| std::string result; |
| while (true) { |
| PW_TRY_ASSIGN(char c, ReadObject<char>(stream)); |
| if (c == '\0') { |
| break; |
| } |
| result += c; |
| } |
| return result; |
| } |
| |
| } // namespace internal |
| } // namespace pw::elf |