// 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; Result 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 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 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::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(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> ElfReader::ReadSection(std::string_view name) { StatusWithSize size = SeekToSection(name); if (!size.ok()) { return size.status(); } std::vector data; data.resize(size.size()); PW_TRY(stream().ReadExact(data)); return data; } namespace internal { // TODO(jrreinhart): Move to pw_stream Result ReadNullTermString(stream::Reader& stream) { std::string result; while (true) { PW_TRY_ASSIGN(char c, ReadObject(stream)); if (c == '\0') { break; } result += c; } return result; } } // namespace internal } // namespace pw::elf