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