blob: a13f602590096f0e23eb23ba24cc5d36095d0654 [file]
// 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