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/profiler/module_cache.h"
6
7 #include <dlfcn.h>
8 #include <elf.h>
9
10 #include <optional>
11 #include <string_view>
12
13 #include "base/debug/elf_reader.h"
14 #include "build/build_config.h"
15
16 #if BUILDFLAG(IS_ANDROID)
17 extern "C" {
18 // &__executable_start is the start address of the current module.
19 extern const char __executable_start;
20 // &__etext is the end addesss of the code segment in the current module.
21 extern const char _etext;
22 }
23 #endif
24
25 namespace base {
26
27 namespace {
28
29 // Returns the unique build ID for a module loaded at |module_addr|. Returns the
30 // empty string if the function fails to get the build ID.
31 //
32 // Build IDs follow a cross-platform format consisting of several fields
33 // concatenated together:
34 // - the module's unique ID, and
35 // - the age suffix for incremental builds.
36 //
37 // On POSIX, the unique ID is read from the ELF binary located at |module_addr|.
38 // The age field is always 0.
GetUniqueBuildId(const void * module_addr)39 std::string GetUniqueBuildId(const void* module_addr) {
40 debug::ElfBuildIdBuffer build_id;
41 size_t build_id_length = debug::ReadElfBuildId(module_addr, true, build_id);
42 if (!build_id_length)
43 return std::string();
44
45 // Append 0 for the age value.
46 return std::string(build_id, build_id_length) + "0";
47 }
48
49 // Returns the offset from |module_addr| to the first byte following the last
50 // executable segment from the ELF file mapped at |module_addr|.
51 // It's defined this way so that any executable address from this module is in
52 // range [addr, addr + GetLastExecutableOffset(addr)).
53 // If no executable segment is found, returns 0.
GetLastExecutableOffset(const void * module_addr)54 size_t GetLastExecutableOffset(const void* module_addr) {
55 const size_t relocation_offset = debug::GetRelocationOffset(module_addr);
56 size_t max_offset = 0;
57 for (const Phdr& header : debug::GetElfProgramHeaders(module_addr)) {
58 if (header.p_type != PT_LOAD || !(header.p_flags & PF_X))
59 continue;
60
61 max_offset = std::max(
62 max_offset, static_cast<size_t>(
63 header.p_vaddr + relocation_offset + header.p_memsz -
64 reinterpret_cast<uintptr_t>(module_addr)));
65 }
66
67 return max_offset;
68 }
69
GetDebugBasenameForModule(const void * base_address,std::string_view file)70 FilePath GetDebugBasenameForModule(const void* base_address,
71 std::string_view file) {
72 #if BUILDFLAG(IS_ANDROID)
73 // Preferentially identify the library using its soname on Android. Libraries
74 // mapped directly from apks have the apk filename in |dl_info.dli_fname|, and
75 // this doesn't distinguish the particular library.
76 std::optional<std::string_view> library_name =
77 debug::ReadElfLibraryName(base_address);
78 if (library_name)
79 return FilePath(*library_name);
80 #endif // BUILDFLAG(IS_ANDROID)
81
82 #if BUILDFLAG(IS_CHROMEOS)
83 // SetProcessTitleFromCommandLine() does not play well with dladdr(). In
84 // particular, after calling our setproctitle(), calling dladdr() with an
85 // address in the main binary will return the complete command line of the
86 // program, including all arguments, in dli_fname. If we get a complete
87 // command-line like "/opt/google/chrome/chrome --type=gpu-process
88 // --gpu-sandbox-failures-fatal=yes --enable-logging ...", strip off
89 // everything that looks like an argument. This is safe on ChromeOS, where we
90 // control the directory and file names and know that no chrome binary or
91 // system library will have a " --" in the path.
92 base::StringPiece::size_type pos = file.find(" --");
93 if (pos != base::StringPiece::npos) {
94 file = file.substr(0, pos);
95 }
96 #endif // BUILDFLAG(IS_CHROMEOS)
97
98 return FilePath(file).BaseName();
99 }
100
101 class PosixModule : public ModuleCache::Module {
102 public:
103 PosixModule(uintptr_t base_address,
104 const std::string& build_id,
105 const FilePath& debug_basename,
106 size_t size);
107
108 PosixModule(const PosixModule&) = delete;
109 PosixModule& operator=(const PosixModule&) = delete;
110
111 // ModuleCache::Module
GetBaseAddress() const112 uintptr_t GetBaseAddress() const override { return base_address_; }
GetId() const113 std::string GetId() const override { return id_; }
GetDebugBasename() const114 FilePath GetDebugBasename() const override { return debug_basename_; }
GetSize() const115 size_t GetSize() const override { return size_; }
IsNative() const116 bool IsNative() const override { return true; }
117
118 private:
119 uintptr_t base_address_;
120 std::string id_;
121 FilePath debug_basename_;
122 size_t size_;
123 };
124
PosixModule(uintptr_t base_address,const std::string & build_id,const FilePath & debug_basename,size_t size)125 PosixModule::PosixModule(uintptr_t base_address,
126 const std::string& build_id,
127 const FilePath& debug_basename,
128 size_t size)
129 : base_address_(base_address),
130 id_(build_id),
131 debug_basename_(debug_basename),
132 size_(size) {}
133
134 } // namespace
135
136 // static
CreateModuleForAddress(uintptr_t address)137 std::unique_ptr<const ModuleCache::Module> ModuleCache::CreateModuleForAddress(
138 uintptr_t address) {
139 Dl_info info;
140 if (!dladdr(reinterpret_cast<const void*>(address), &info)) {
141 #if BUILDFLAG(IS_ANDROID)
142 // dladdr doesn't know about the Chrome module in Android targets using the
143 // crazy linker. Explicitly check against the module's extents in that case.
144 // This is checked after dladdr because if dladdr CAN find the Chrome
145 // module, it will return a better fallback basename in `info.dli_fname`.
146 if (address >= reinterpret_cast<uintptr_t>(&__executable_start) &&
147 address < reinterpret_cast<uintptr_t>(&_etext)) {
148 const void* const base_address =
149 reinterpret_cast<const void*>(&__executable_start);
150 return std::make_unique<PosixModule>(
151 reinterpret_cast<uintptr_t>(&__executable_start),
152 GetUniqueBuildId(base_address),
153 // Extract the soname from the module. It is expected to exist, but if
154 // it doesn't use an empty string.
155 GetDebugBasenameForModule(base_address, /* file = */ ""),
156 GetLastExecutableOffset(base_address));
157 }
158 #endif
159 return nullptr;
160 }
161
162 return std::make_unique<PosixModule>(
163 reinterpret_cast<uintptr_t>(info.dli_fbase),
164 GetUniqueBuildId(info.dli_fbase),
165 GetDebugBasenameForModule(info.dli_fbase, info.dli_fname),
166 GetLastExecutableOffset(info.dli_fbase));
167 }
168
169 } // namespace base
170