xref: /aosp_15_r20/external/cronet/base/debug/elf_reader_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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