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/array.h"
18 #include "pw_bytes/endian.h"
19 #include "pw_bytes/suffix.h"
20 #include "pw_stream/memory_stream.h"
21 #include "pw_stream/std_file_stream.h"
22 #include "pw_unit_test/framework.h"
23
24 namespace pw::elf {
25 namespace {
26
27 constexpr Status kEndOfFileStatus = Status::OutOfRange();
28
TestInitialize(ConstByteSpan data)29 Status TestInitialize(ConstByteSpan data) {
30 stream::MemoryReader stream(data);
31 return ElfReader::FromStream(stream).status();
32 }
33
TEST(Reader,HandlesEmptyStream)34 TEST(Reader, HandlesEmptyStream) {
35 constexpr auto kData = bytes::Array<>();
36 EXPECT_EQ(TestInitialize(kData), kEndOfFileStatus);
37 }
38
TEST(Reader,HandlesInvalidMagic)39 TEST(Reader, HandlesInvalidMagic) {
40 constexpr std::array<std::byte, EI_NIDENT> kData = {
41 std::byte{0x7F},
42 std::byte{'B'},
43 std::byte{'A'},
44 std::byte{'D'},
45 };
46 EXPECT_EQ(TestInitialize(kData), Status::DataLoss());
47 }
48
TEST(Reader,HandlesTruncatedAfterMagic)49 TEST(Reader, HandlesTruncatedAfterMagic) {
50 constexpr auto kData = bytes::Array<0x7F, 'E', 'L', 'F'>();
51 EXPECT_EQ(TestInitialize(kData), kEndOfFileStatus);
52 }
53
TEST(Reader,HandlesInvalidClass)54 TEST(Reader, HandlesInvalidClass) {
55 constexpr std::array<std::byte, EI_NIDENT> kData = {
56 std::byte{0x7F},
57 std::byte{'E'},
58 std::byte{'L'},
59 std::byte{'F'},
60 std::byte{0x66}, // EI_CLASS
61 std::byte{pw::endian::native == pw::endian::little
62 ? ELFDATA2LSB
63 : ELFDATA2MSB}, // EI_DATA
64 };
65 EXPECT_EQ(TestInitialize(kData), Status::DataLoss());
66 }
67
TEST(Reader,HandlesUnsupportedEndian)68 TEST(Reader, HandlesUnsupportedEndian) {
69 constexpr std::array<std::byte, EI_NIDENT> kData = {
70 std::byte{0x7F},
71 std::byte{'E'},
72 std::byte{'L'},
73 std::byte{'F'},
74 std::byte{0x66}, // EI_CLASS
75 std::byte{pw::endian::native == pw::endian::little
76 ? ELFDATA2MSB
77 : ELFDATA2LSB}, // EI_DATA (opposite)
78 };
79 EXPECT_EQ(TestInitialize(kData), Status::Unimplemented());
80 }
81
TEST(Reader,SeekToSectionWorksOnRealFile)82 TEST(Reader, SeekToSectionWorksOnRealFile) {
83 pw::stream::StdFileReader stream(TEST_ELF_FILE_PATH);
84 PW_TEST_ASSERT_OK_AND_ASSIGN(auto reader,
85 pw::elf::ElfReader::FromStream(stream));
86
87 auto section_size = reader.SeekToSection(".test_section_1");
88 PW_TEST_ASSERT_OK(section_size);
89
90 std::vector<std::byte> section_data;
91 section_data.resize(section_size.size());
92 PW_TEST_ASSERT_OK(stream.ReadExact(section_data));
93
94 constexpr auto kExpectedData = bytes::String("You cannot pass\0");
95 EXPECT_EQ(section_data.size(), kExpectedData.size());
96 EXPECT_TRUE(std::equal(
97 section_data.begin(), section_data.end(), kExpectedData.begin()));
98 }
99
TEST(Reader,ReadSectionWorksOnRealFile)100 TEST(Reader, ReadSectionWorksOnRealFile) {
101 pw::stream::StdFileReader stream(TEST_ELF_FILE_PATH);
102 PW_TEST_ASSERT_OK_AND_ASSIGN(auto reader,
103 pw::elf::ElfReader::FromStream(stream));
104
105 PW_TEST_ASSERT_OK_AND_ASSIGN(auto section_data,
106 reader.ReadSection(".test_section_2"));
107
108 constexpr auto kExpectedData = bytes::Array<0xEF, 0xBE, 0xED, 0xFE>();
109 EXPECT_EQ(section_data.size(), kExpectedData.size());
110 EXPECT_TRUE(std::equal(
111 section_data.begin(), section_data.end(), kExpectedData.begin()));
112 }
113
114 } // namespace
115 } // namespace pw::elf
116