Coverage Report

Created: 2025-06-13 06:31

/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