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 "shared-libraries.h"
7 
8 #include "ClearOnShutdown.h"
9 #include "mozilla/StaticMutex.h"
10 #include "mozilla/Unused.h"
11 #include "nsNativeCharsetUtils.h"
12 #include <AvailabilityMacros.h>
13 
14 #include <dlfcn.h>
15 #include <mach-o/arch.h>
16 #include <mach-o/dyld_images.h>
17 #include <mach-o/dyld.h>
18 #include <mach-o/loader.h>
19 #include <mach/mach_init.h>
20 #include <mach/mach_traps.h>
21 #include <mach/task_info.h>
22 #include <mach/task.h>
23 #include <sstream>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <vector>
27 
28 // Architecture specific abstraction.
29 #if defined(GP_ARCH_x86)
30 typedef mach_header platform_mach_header;
31 typedef segment_command mach_segment_command_type;
32 #  define MACHO_MAGIC_NUMBER MH_MAGIC
33 #  define CMD_SEGMENT LC_SEGMENT
34 #  define seg_size uint32_t
35 #else
36 typedef mach_header_64 platform_mach_header;
37 typedef segment_command_64 mach_segment_command_type;
38 #  define MACHO_MAGIC_NUMBER MH_MAGIC_64
39 #  define CMD_SEGMENT LC_SEGMENT_64
40 #  define seg_size uint64_t
41 #endif
42 
43 struct NativeSharedLibrary {
44   const platform_mach_header* header;
45   std::string path;
46 };
47 static std::vector<NativeSharedLibrary>* sSharedLibrariesList = nullptr;
48 static mozilla::StaticMutex sSharedLibrariesMutex;
49 
SharedLibraryAddImage(const struct mach_header * mh,intptr_t vmaddr_slide)50 static void SharedLibraryAddImage(const struct mach_header* mh,
51                                   intptr_t vmaddr_slide) {
52   // NOTE: Presumably for backwards-compatibility reasons, this function accepts
53   // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast
54   // it to the right type here.
55   auto header = reinterpret_cast<const platform_mach_header*>(mh);
56 
57   Dl_info info;
58   if (!dladdr(header, &info)) {
59     return;
60   }
61 
62   mozilla::StaticMutexAutoLock lock(sSharedLibrariesMutex);
63   if (!sSharedLibrariesList) {
64     return;
65   }
66 
67   NativeSharedLibrary lib = {header, info.dli_fname};
68   sSharedLibrariesList->push_back(lib);
69 }
70 
SharedLibraryRemoveImage(const struct mach_header * mh,intptr_t vmaddr_slide)71 static void SharedLibraryRemoveImage(const struct mach_header* mh,
72                                      intptr_t vmaddr_slide) {
73   // NOTE: Presumably for backwards-compatibility reasons, this function accepts
74   // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast
75   // it to the right type here.
76   auto header = reinterpret_cast<const platform_mach_header*>(mh);
77 
78   mozilla::StaticMutexAutoLock lock(sSharedLibrariesMutex);
79   if (!sSharedLibrariesList) {
80     return;
81   }
82 
83   uint32_t count = sSharedLibrariesList->size();
84   for (uint32_t i = 0; i < count; ++i) {
85     if ((*sSharedLibrariesList)[i].header == header) {
86       sSharedLibrariesList->erase(sSharedLibrariesList->begin() + i);
87       return;
88     }
89   }
90 }
91 
Initialize()92 void SharedLibraryInfo::Initialize() {
93   // NOTE: We intentionally leak this memory here. We're allocating dynamically
94   // in order to avoid static initializers.
95   sSharedLibrariesList = new std::vector<NativeSharedLibrary>();
96 
97   _dyld_register_func_for_add_image(SharedLibraryAddImage);
98   _dyld_register_func_for_remove_image(SharedLibraryRemoveImage);
99 }
100 
addSharedLibrary(const platform_mach_header * header,const char * path,SharedLibraryInfo & info)101 static void addSharedLibrary(const platform_mach_header* header,
102                              const char* path, SharedLibraryInfo& info) {
103   const struct load_command* cmd =
104       reinterpret_cast<const struct load_command*>(header + 1);
105 
106   seg_size size = 0;
107   unsigned long long start = reinterpret_cast<unsigned long long>(header);
108   // Find the cmd segment in the macho image. It will contain the offset we care
109   // about.
110   const uint8_t* uuid_bytes = nullptr;
111   for (unsigned int i = 0;
112        cmd && (i < header->ncmds) && (uuid_bytes == nullptr || size == 0);
113        ++i) {
114     if (cmd->cmd == CMD_SEGMENT) {
115       const mach_segment_command_type* seg =
116           reinterpret_cast<const mach_segment_command_type*>(cmd);
117 
118       if (!strcmp(seg->segname, "__TEXT")) {
119         size = seg->vmsize;
120       }
121     } else if (cmd->cmd == LC_UUID) {
122       const uuid_command* ucmd = reinterpret_cast<const uuid_command*>(cmd);
123       uuid_bytes = ucmd->uuid;
124     }
125 
126     cmd = reinterpret_cast<const struct load_command*>(
127         reinterpret_cast<const char*>(cmd) + cmd->cmdsize);
128   }
129 
130   nsAutoCString uuid;
131   if (uuid_bytes != nullptr) {
132     uuid.AppendPrintf(
133         "%02X"
134         "%02X"
135         "%02X"
136         "%02X"
137         "%02X"
138         "%02X"
139         "%02X"
140         "%02X"
141         "%02X"
142         "%02X"
143         "%02X"
144         "%02X"
145         "%02X"
146         "%02X"
147         "%02X"
148         "%02X"
149         "0" /* breakpad id age */,
150         uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3],
151         uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7],
152         uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11],
153         uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
154   }
155 
156   nsAutoString pathStr;
157   mozilla::Unused << NS_WARN_IF(
158       NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(path), pathStr)));
159 
160   nsAutoString nameStr = pathStr;
161   int32_t pos = nameStr.RFindChar('/');
162   if (pos != kNotFound) {
163     nameStr.Cut(0, pos + 1);
164   }
165 
166   const NXArchInfo* archInfo =
167       NXGetArchInfoFromCpuType(header->cputype, header->cpusubtype);
168 
169   info.AddSharedLibrary(SharedLibrary(start, start + size, 0, uuid, nameStr,
170                                       pathStr, nameStr, pathStr, ""_ns,
171                                       archInfo ? archInfo->name : ""));
172 }
173 
174 // Translate the statically stored sSharedLibrariesList information into a
175 // SharedLibraryInfo object.
GetInfoForSelf()176 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
177   mozilla::StaticMutexAutoLock lock(sSharedLibrariesMutex);
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 }
205