• 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/profiler/module_cache.h"
11 
12 #include <dlfcn.h>
13 #include <mach-o/getsect.h>
14 #include <string.h>
15 #include <uuid/uuid.h>
16 
17 #include "base/strings/string_number_conversions.h"
18 #include "build/build_config.h"
19 
20 namespace base {
21 
22 namespace {
23 
24 #if defined(ARCH_CPU_64_BITS)
25 using MachHeaderType = mach_header_64;
26 using SegmentCommandType = segment_command_64;
27 constexpr uint32_t kMachHeaderMagic = MH_MAGIC_64;
28 constexpr uint32_t kSegmentCommand = LC_SEGMENT_64;
29 #else
30 using MachHeaderType = mach_header;
31 using SegmentCommandType = segment_command;
32 constexpr uint32_t kMachHeaderMagic = MH_MAGIC;
33 constexpr uint32_t kSegmentCommand = LC_SEGMENT;
34 #endif
35 
36 // Returns the unique build ID and text segment size for a module loaded at
37 // |module_addr|. Returns the empty string and 0 if the function fails to get
38 // the build ID or size.
39 //
40 // Build IDs are created by the concatenation of the module's GUID (Windows) /
41 // UUID (Mac) and an "age" field that indicates how many times that GUID/UUID
42 // has been reused. In Windows binaries, the "age" field is present in the
43 // module header, but on the Mac, UUIDs are never reused and so the "age" value
44 // appended to the UUID is always 0.
GetUniqueIdAndTextSize(const void * module_addr,std::string * unique_id,size_t * text_size)45 void GetUniqueIdAndTextSize(const void* module_addr,
46                             std::string* unique_id,
47                             size_t* text_size) {
48   const MachHeaderType* mach_header =
49       reinterpret_cast<const MachHeaderType*>(module_addr);
50   DCHECK_EQ(mach_header->magic, kMachHeaderMagic);
51 
52   size_t offset = sizeof(MachHeaderType);
53   size_t offset_limit = sizeof(MachHeaderType) + mach_header->sizeofcmds;
54   bool found_uuid = false;
55   bool found_text_size = false;
56 
57   for (uint32_t i = 0; i < mach_header->ncmds; ++i) {
58     if (offset + sizeof(load_command) >= offset_limit) {
59       unique_id->clear();
60       *text_size = 0;
61       return;
62     }
63 
64     const load_command* load_cmd = reinterpret_cast<const load_command*>(
65         reinterpret_cast<const uint8_t*>(mach_header) + offset);
66 
67     if (offset + load_cmd->cmdsize > offset_limit) {
68       // This command runs off the end of the command list. This is malformed.
69       unique_id->clear();
70       *text_size = 0;
71       return;
72     }
73 
74     if (load_cmd->cmd == LC_UUID) {
75       if (load_cmd->cmdsize < sizeof(uuid_command)) {
76         // This "UUID command" is too small. This is malformed.
77         unique_id->clear();
78       } else {
79         const uuid_command* uuid_cmd =
80             reinterpret_cast<const uuid_command*>(load_cmd);
81         static_assert(sizeof(uuid_cmd->uuid) == sizeof(uuid_t),
82                       "UUID field of UUID command should be 16 bytes.");
83         // The ID comprises the UUID concatenated with the Mac's "age" value
84         // which is always 0.
85         unique_id->assign(HexEncode(&uuid_cmd->uuid, sizeof(uuid_cmd->uuid)) +
86                           "0");
87       }
88       if (found_text_size) {
89         return;
90       }
91       found_uuid = true;
92     } else if (load_cmd->cmd == kSegmentCommand) {
93       const SegmentCommandType* segment_cmd =
94           reinterpret_cast<const SegmentCommandType*>(load_cmd);
95       if (strncmp(segment_cmd->segname, SEG_TEXT,
96                   sizeof(segment_cmd->segname)) == 0) {
97         *text_size = segment_cmd->vmsize;
98         // Compare result with library function call, which is slower than this
99         // code.
100         unsigned long text_size_from_libmacho;
101         DCHECK(getsegmentdata(mach_header, SEG_TEXT, &text_size_from_libmacho));
102         DCHECK_EQ(*text_size, text_size_from_libmacho);
103       }
104       if (found_uuid) {
105         return;
106       }
107       found_text_size = true;
108     }
109     offset += load_cmd->cmdsize;
110   }
111 
112   if (!found_uuid) {
113     unique_id->clear();
114   }
115   if (!found_text_size) {
116     *text_size = 0;
117   }
118 }
119 
120 }  // namespace
121 
122 class MacModule : public ModuleCache::Module {
123  public:
MacModule(const Dl_info & dl_info)124   explicit MacModule(const Dl_info& dl_info)
125       : base_address_(reinterpret_cast<uintptr_t>(dl_info.dli_fbase)),
126         debug_basename_(FilePath(dl_info.dli_fname).BaseName()) {
127     GetUniqueIdAndTextSize(dl_info.dli_fbase, &id_, &size_);
128   }
129 
130   MacModule(const MacModule&) = delete;
131   MacModule& operator=(const MacModule&) = delete;
132 
133   // ModuleCache::Module
GetBaseAddress() const134   uintptr_t GetBaseAddress() const override { return base_address_; }
GetId() const135   std::string GetId() const override { return id_; }
GetDebugBasename() const136   FilePath GetDebugBasename() const override { return debug_basename_; }
GetSize() const137   size_t GetSize() const override { return size_; }
IsNative() const138   bool IsNative() const override { return true; }
139 
140  private:
141   uintptr_t base_address_;
142   std::string id_;
143   FilePath debug_basename_;
144   size_t size_;
145 };
146 
147 // static
CreateModuleForAddress(uintptr_t address)148 std::unique_ptr<const ModuleCache::Module> ModuleCache::CreateModuleForAddress(
149     uintptr_t address) {
150   Dl_info info;
151   if (!dladdr(reinterpret_cast<const void*>(address), &info)) {
152     return nullptr;
153   }
154   return std::make_unique<MacModule>(info);
155 }
156 
157 }  // namespace base
158