shared-libraries-macos.cc (6723B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "mozilla/SharedLibraries.h" 7 8 #include "platform.h" 9 10 #include <AvailabilityMacros.h> 11 12 #include <dlfcn.h> 13 #include <mach-o/arch.h> 14 #include <mach-o/dyld_images.h> 15 #include <mach-o/dyld.h> 16 #include <mach-o/loader.h> 17 #include <mach/mach_init.h> 18 #include <mach/mach_traps.h> 19 #include <mach/task_info.h> 20 #include <mach/task.h> 21 #include <sstream> 22 #include <string.h> 23 #include <vector> 24 25 // Architecture specific abstraction. 26 #if defined(GP_ARCH_x86) 27 typedef mach_header platform_mach_header; 28 typedef segment_command mach_segment_command_type; 29 # define MACHO_MAGIC_NUMBER MH_MAGIC 30 # define CMD_SEGMENT LC_SEGMENT 31 # define seg_size uint32_t 32 #else 33 typedef mach_header_64 platform_mach_header; 34 typedef segment_command_64 mach_segment_command_type; 35 # define MACHO_MAGIC_NUMBER MH_MAGIC_64 36 # define CMD_SEGMENT LC_SEGMENT_64 37 # define seg_size uint64_t 38 #endif 39 40 struct NativeSharedLibrary { 41 const platform_mach_header* header; 42 std::string path; 43 }; 44 static std::vector<NativeSharedLibrary>* sSharedLibrariesList = nullptr; 45 46 class MOZ_RAII SharedLibrariesLock { 47 public: 48 SharedLibrariesLock() { sSharedLibrariesMutex.Lock(); } 49 50 ~SharedLibrariesLock() { sSharedLibrariesMutex.Unlock(); } 51 52 SharedLibrariesLock(const SharedLibrariesLock&) = delete; 53 void operator=(const SharedLibrariesLock&) = delete; 54 55 private: 56 static mozilla::baseprofiler::detail::BaseProfilerMutex sSharedLibrariesMutex; 57 }; 58 59 MOZ_RUNINIT mozilla::baseprofiler::detail::BaseProfilerMutex 60 SharedLibrariesLock::sSharedLibrariesMutex; 61 62 static void SharedLibraryAddImage(const struct mach_header* mh, 63 intptr_t vmaddr_slide) { 64 // NOTE: Presumably for backwards-compatibility reasons, this function accepts 65 // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast 66 // it to the right type here. 67 auto header = reinterpret_cast<const platform_mach_header*>(mh); 68 69 Dl_info info; 70 if (!dladdr(header, &info)) { 71 return; 72 } 73 74 SharedLibrariesLock lock; 75 if (!sSharedLibrariesList) { 76 return; 77 } 78 79 NativeSharedLibrary lib = {header, info.dli_fname}; 80 sSharedLibrariesList->push_back(lib); 81 } 82 83 static void SharedLibraryRemoveImage(const struct mach_header* mh, 84 intptr_t vmaddr_slide) { 85 // NOTE: Presumably for backwards-compatibility reasons, this function accepts 86 // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast 87 // it to the right type here. 88 auto header = reinterpret_cast<const platform_mach_header*>(mh); 89 90 SharedLibrariesLock lock; 91 if (!sSharedLibrariesList) { 92 return; 93 } 94 95 uint32_t count = sSharedLibrariesList->size(); 96 for (uint32_t i = 0; i < count; ++i) { 97 if ((*sSharedLibrariesList)[i].header == header) { 98 sSharedLibrariesList->erase(sSharedLibrariesList->begin() + i); 99 return; 100 } 101 } 102 } 103 104 void SharedLibraryInfo::Initialize() { 105 // NOTE: We intentionally leak this memory here. We're allocating dynamically 106 // in order to avoid static initializers. 107 sSharedLibrariesList = new std::vector<NativeSharedLibrary>(); 108 109 _dyld_register_func_for_add_image(SharedLibraryAddImage); 110 _dyld_register_func_for_remove_image(SharedLibraryRemoveImage); 111 } 112 113 static void addSharedLibrary(const platform_mach_header* header, 114 const char* path, SharedLibraryInfo& info) { 115 const struct load_command* cmd = 116 reinterpret_cast<const struct load_command*>(header + 1); 117 118 seg_size size = 0; 119 unsigned long long start = reinterpret_cast<unsigned long long>(header); 120 // Find the cmd segment in the macho image. It will contain the offset we care 121 // about. 122 const uint8_t* uuid_bytes = nullptr; 123 for (unsigned int i = 0; 124 cmd && (i < header->ncmds) && (uuid_bytes == nullptr || size == 0); 125 ++i) { 126 if (cmd->cmd == CMD_SEGMENT) { 127 const mach_segment_command_type* seg = 128 reinterpret_cast<const mach_segment_command_type*>(cmd); 129 130 if (!strcmp(seg->segname, "__TEXT")) { 131 size = seg->vmsize; 132 } 133 } else if (cmd->cmd == LC_UUID) { 134 const uuid_command* ucmd = reinterpret_cast<const uuid_command*>(cmd); 135 uuid_bytes = ucmd->uuid; 136 } 137 138 cmd = reinterpret_cast<const struct load_command*>( 139 reinterpret_cast<const char*>(cmd) + cmd->cmdsize); 140 } 141 142 std::string uuid; 143 std::string breakpadId; 144 if (uuid_bytes != nullptr) { 145 static constexpr char digits[16] = {'0', '1', '2', '3', '4', '5', '6', '7', 146 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 147 for (int i = 0; i < 16; ++i) { 148 uint8_t byte = uuid_bytes[i]; 149 uuid += digits[byte >> 4]; 150 uuid += digits[byte & 0xFu]; 151 } 152 153 // Breakpad id is the same as the uuid but with the additional trailing 0 154 // for the breakpad id age. 155 breakpadId = uuid; 156 // breakpad id age. 157 breakpadId += '0'; 158 } 159 160 std::string pathStr = path; 161 162 size_t pos = pathStr.rfind('/'); 163 std::string nameStr = 164 (pos != std::string::npos) ? pathStr.substr(pos + 1) : pathStr; 165 166 const NXArchInfo* archInfo = 167 NXGetArchInfoFromCpuType(header->cputype, header->cpusubtype); 168 169 info.AddSharedLibrary(SharedLibrary( 170 start, start + size, 0, breakpadId, uuid, nameStr, pathStr, nameStr, 171 pathStr, std::string{}, archInfo ? archInfo->name : "")); 172 } 173 174 // Translate the statically stored sSharedLibrariesList information into a 175 // SharedLibraryInfo object. 176 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() { 177 SharedLibrariesLock lock; 178 SharedLibraryInfo sharedLibraryInfo; 179 180 for (auto& info : *sSharedLibrariesList) { 181 addSharedLibrary(info.header, info.path.c_str(), sharedLibraryInfo); 182 } 183 184 // Add the entry for dyld itself. 185 // We only support macOS 10.12+, which corresponds to dyld version 15+. 186 // dyld version 15 added the dyldPath property. 187 task_dyld_info_data_t task_dyld_info; 188 mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; 189 if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, 190 &count) != KERN_SUCCESS) { 191 return sharedLibraryInfo; 192 } 193 194 struct dyld_all_image_infos* aii = 195 (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr; 196 if (aii->version >= 15) { 197 const platform_mach_header* header = 198 reinterpret_cast<const platform_mach_header*>( 199 aii->dyldImageLoadAddress); 200 addSharedLibrary(header, aii->dyldPath, sharedLibraryInfo); 201 } 202 203 return sharedLibraryInfo; 204 }