1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/debug/elf_reader.h"
11
12 #include <dlfcn.h>
13
14 #include <cstdint>
15 #include <optional>
16 #include <string_view>
17
18 #include "base/debug/test_elf_image_builder.h"
19 #include "base/files/memory_mapped_file.h"
20 #include "base/native_library.h"
21 #include "base/strings/string_util.h"
22 #include "build/build_config.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 extern char __executable_start;
26
27 namespace base {
28 namespace debug {
29
30 namespace {
31 constexpr uint8_t kBuildIdBytes[] = {0xab, 0xcd, 0x12, 0x34};
32 constexpr const char kBuildIdHexString[] = "ABCD1234";
33 constexpr const char kBuildIdHexStringLower[] = "ABCD1234";
34
ParamInfoToString(const::testing::TestParamInfo<base::TestElfImageBuilder::MappingType> & param_info)35 std::string ParamInfoToString(
36 const ::testing::TestParamInfo<base::TestElfImageBuilder::MappingType>&
37 param_info) {
38 switch (param_info.param) {
39 case TestElfImageBuilder::RELOCATABLE:
40 return "Relocatable";
41
42 case TestElfImageBuilder::RELOCATABLE_WITH_BIAS:
43 return "RelocatableWithBias";
44
45 case TestElfImageBuilder::NON_RELOCATABLE:
46 return "NonRelocatable";
47 }
48 }
49 } // namespace
50
51 using ElfReaderTest =
52 ::testing::TestWithParam<TestElfImageBuilder::MappingType>;
53
TEST_P(ElfReaderTest,ReadElfBuildIdUppercase)54 TEST_P(ElfReaderTest, ReadElfBuildIdUppercase) {
55 TestElfImage image =
56 TestElfImageBuilder(GetParam())
57 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
58 .AddNoteSegment(NT_GNU_BUILD_ID, "GNU", kBuildIdBytes)
59 .Build();
60
61 ElfBuildIdBuffer build_id;
62 size_t build_id_size = ReadElfBuildId(image.elf_start(), true, build_id);
63 EXPECT_EQ(8u, build_id_size);
64 EXPECT_EQ(kBuildIdHexString, std::string_view(&build_id[0], build_id_size));
65 }
66
TEST_P(ElfReaderTest,ReadElfBuildIdLowercase)67 TEST_P(ElfReaderTest, ReadElfBuildIdLowercase) {
68 TestElfImage image =
69 TestElfImageBuilder(GetParam())
70 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
71 .AddNoteSegment(NT_GNU_BUILD_ID, "GNU", kBuildIdBytes)
72 .Build();
73
74 ElfBuildIdBuffer build_id;
75 size_t build_id_size = ReadElfBuildId(image.elf_start(), false, build_id);
76 EXPECT_EQ(8u, build_id_size);
77 EXPECT_EQ(ToLowerASCII(kBuildIdHexStringLower),
78 std::string_view(&build_id[0], build_id_size));
79 }
80
TEST_P(ElfReaderTest,ReadElfBuildIdMultipleNotes)81 TEST_P(ElfReaderTest, ReadElfBuildIdMultipleNotes) {
82 constexpr uint8_t kOtherNoteBytes[] = {0xef, 0x56};
83
84 TestElfImage image =
85 TestElfImageBuilder(GetParam())
86 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
87 .AddNoteSegment(NT_GNU_BUILD_ID + 1, "ABC", kOtherNoteBytes)
88 .AddNoteSegment(NT_GNU_BUILD_ID, "GNU", kBuildIdBytes)
89 .Build();
90
91 ElfBuildIdBuffer build_id;
92 size_t build_id_size = ReadElfBuildId(image.elf_start(), true, build_id);
93 EXPECT_EQ(8u, build_id_size);
94 EXPECT_EQ(kBuildIdHexString, std::string_view(&build_id[0], build_id_size));
95 }
96
TEST_P(ElfReaderTest,ReadElfBuildIdWrongName)97 TEST_P(ElfReaderTest, ReadElfBuildIdWrongName) {
98 TestElfImage image =
99 TestElfImageBuilder(GetParam())
100 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
101 .AddNoteSegment(NT_GNU_BUILD_ID, "ABC", kBuildIdBytes)
102 .Build();
103
104 ElfBuildIdBuffer build_id;
105 size_t build_id_size = ReadElfBuildId(image.elf_start(), true, build_id);
106 EXPECT_EQ(0u, build_id_size);
107 }
108
TEST_P(ElfReaderTest,ReadElfBuildIdWrongType)109 TEST_P(ElfReaderTest, ReadElfBuildIdWrongType) {
110 TestElfImage image =
111 TestElfImageBuilder(GetParam())
112 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
113 .AddNoteSegment(NT_GNU_BUILD_ID + 1, "GNU", kBuildIdBytes)
114 .Build();
115
116 ElfBuildIdBuffer build_id;
117 size_t build_id_size = ReadElfBuildId(image.elf_start(), true, build_id);
118 EXPECT_EQ(0u, build_id_size);
119 }
120
TEST_P(ElfReaderTest,ReadElfBuildIdNoBuildId)121 TEST_P(ElfReaderTest, ReadElfBuildIdNoBuildId) {
122 TestElfImage image = TestElfImageBuilder(GetParam())
123 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
124 .Build();
125
126 ElfBuildIdBuffer build_id;
127 size_t build_id_size = ReadElfBuildId(image.elf_start(), true, build_id);
128 EXPECT_EQ(0u, build_id_size);
129 }
130
TEST_P(ElfReaderTest,ReadElfLibraryName)131 TEST_P(ElfReaderTest, ReadElfLibraryName) {
132 TestElfImage image = TestElfImageBuilder(GetParam())
133 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
134 .AddSoName("mysoname")
135 .Build();
136
137 std::optional<std::string_view> library_name =
138 ReadElfLibraryName(image.elf_start());
139 ASSERT_NE(std::nullopt, library_name);
140 EXPECT_EQ("mysoname", *library_name);
141 }
142
TEST_P(ElfReaderTest,ReadElfLibraryNameNoSoName)143 TEST_P(ElfReaderTest, ReadElfLibraryNameNoSoName) {
144 TestElfImage image = TestElfImageBuilder(GetParam())
145 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
146 .Build();
147
148 std::optional<std::string_view> library_name =
149 ReadElfLibraryName(image.elf_start());
150 EXPECT_EQ(std::nullopt, library_name);
151 }
152
TEST_P(ElfReaderTest,GetRelocationOffset)153 TEST_P(ElfReaderTest, GetRelocationOffset) {
154 TestElfImage image = TestElfImageBuilder(GetParam())
155 .AddLoadSegment(PF_R | PF_X, /* size = */ 2000)
156 .Build();
157
158 switch (GetParam()) {
159 case TestElfImageBuilder::RELOCATABLE:
160 EXPECT_EQ(reinterpret_cast<size_t>(image.elf_start()),
161 GetRelocationOffset(image.elf_start()));
162 break;
163
164 case TestElfImageBuilder::RELOCATABLE_WITH_BIAS:
165 EXPECT_EQ(reinterpret_cast<size_t>(image.elf_start()) -
166 TestElfImageBuilder::kLoadBias,
167 GetRelocationOffset(image.elf_start()));
168 break;
169
170 case TestElfImageBuilder::NON_RELOCATABLE:
171 EXPECT_EQ(0u, GetRelocationOffset(image.elf_start()));
172 break;
173 }
174 }
175
176 INSTANTIATE_TEST_SUITE_P(
177 MappingTypes,
178 ElfReaderTest,
179 ::testing::Values(TestElfImageBuilder::RELOCATABLE,
180 TestElfImageBuilder::RELOCATABLE_WITH_BIAS,
181 TestElfImageBuilder::NON_RELOCATABLE),
182 &ParamInfoToString);
183
TEST(ElfReaderTestWithCurrentElfImage,ReadElfBuildId)184 TEST(ElfReaderTestWithCurrentElfImage, ReadElfBuildId) {
185 ElfBuildIdBuffer build_id;
186 size_t build_id_size = ReadElfBuildId(&__executable_start, true, build_id);
187 ASSERT_NE(build_id_size, 0u);
188
189 #if defined(OFFICIAL_BUILD)
190 constexpr size_t kExpectedBuildIdStringLength = 40; // SHA1 hash in hex.
191 #else
192 constexpr size_t kExpectedBuildIdStringLength = 16; // 64-bit int in hex.
193 #endif
194
195 EXPECT_EQ(kExpectedBuildIdStringLength, build_id_size);
196 for (size_t i = 0; i < build_id_size; ++i) {
197 char c = build_id[i];
198 EXPECT_TRUE(IsHexDigit(c));
199 EXPECT_FALSE(IsAsciiLower(c));
200 }
201 }
202
TEST(ElfReaderTestWithCurrentImage,ReadElfBuildId)203 TEST(ElfReaderTestWithCurrentImage, ReadElfBuildId) {
204 #if BUILDFLAG(IS_ANDROID)
205 // On Android the library loader memory maps the full so file.
206 const char kLibraryName[] = "libbase_unittests__library";
207 const void* addr = &__executable_start;
208 #else
209 const char kLibraryName[] = MALLOC_WRAPPER_LIB;
210 // On Linux the executable does not contain soname and is not mapped till
211 // dynamic segment. So, use malloc wrapper so file on which the test already
212 // depends on.
213 // Find any symbol in the loaded file.
214 //
215 NativeLibraryLoadError error;
216 NativeLibrary library =
217 LoadNativeLibrary(base::FilePath(kLibraryName), &error);
218 void* init_addr =
219 GetFunctionPointerFromNativeLibrary(library, "MallocWrapper");
220
221 // Use this symbol to get full path to the loaded library.
222 Dl_info info;
223 int res = dladdr(init_addr, &info);
224 ASSERT_NE(0, res);
225 const void* addr = info.dli_fbase;
226 #endif
227
228 auto name = ReadElfLibraryName(addr);
229 ASSERT_TRUE(name);
230 EXPECT_NE(std::string::npos, name->find(kLibraryName))
231 << "Library name " << *name << " doesn't contain expected "
232 << kLibraryName;
233
234 #if !BUILDFLAG(IS_ANDROID)
235 UnloadNativeLibrary(library);
236 #endif
237 }
238
239 } // namespace debug
240 } // namespace base
241