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