• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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