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