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