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/. */
6 #include "BaseProfilerSharedLibraries.h"
10 #include "mozilla/Unused.h"
11 #include <AvailabilityMacros.h>
14 #include <mach-o/arch.h>
15 #include <mach-o/dyld_images.h>
16 #include <mach-o/dyld.h>
17 #include <mach-o/loader.h>
18 #include <mach/mach_init.h>
19 #include <mach/mach_traps.h>
20 #include <mach/task_info.h>
21 #include <mach/task.h>
27 // Architecture specific abstraction.
28 #if defined(GP_ARCH_x86)
29 typedef mach_header platform_mach_header
;
30 typedef segment_command mach_segment_command_type
;
31 # define MACHO_MAGIC_NUMBER MH_MAGIC
32 # define CMD_SEGMENT LC_SEGMENT
33 # define seg_size uint32_t
35 typedef mach_header_64 platform_mach_header
;
36 typedef segment_command_64 mach_segment_command_type
;
37 # define MACHO_MAGIC_NUMBER MH_MAGIC_64
38 # define CMD_SEGMENT LC_SEGMENT_64
39 # define seg_size uint64_t
42 struct NativeSharedLibrary
{
43 const platform_mach_header
* header
;
46 static std::vector
<NativeSharedLibrary
>* sSharedLibrariesList
= nullptr;
48 class MOZ_RAII SharedLibrariesLock
{
50 SharedLibrariesLock() { sSharedLibrariesMutex
.Lock(); }
52 ~SharedLibrariesLock() { sSharedLibrariesMutex
.Unlock(); }
54 SharedLibrariesLock(const SharedLibrariesLock
&) = delete;
55 void operator=(const SharedLibrariesLock
&) = delete;
58 static mozilla::baseprofiler::detail::BaseProfilerMutex sSharedLibrariesMutex
;
61 mozilla::baseprofiler::detail::BaseProfilerMutex
62 SharedLibrariesLock::sSharedLibrariesMutex
;
64 static void SharedLibraryAddImage(const struct mach_header
* mh
,
65 intptr_t vmaddr_slide
) {
66 // NOTE: Presumably for backwards-compatibility reasons, this function accepts
67 // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast
68 // it to the right type here.
69 auto header
= reinterpret_cast<const platform_mach_header
*>(mh
);
72 if (!dladdr(header
, &info
)) {
76 SharedLibrariesLock lock
;
77 if (!sSharedLibrariesList
) {
81 NativeSharedLibrary lib
= {header
, info
.dli_fname
};
82 sSharedLibrariesList
->push_back(lib
);
85 static void SharedLibraryRemoveImage(const struct mach_header
* mh
,
86 intptr_t vmaddr_slide
) {
87 // NOTE: Presumably for backwards-compatibility reasons, this function accepts
88 // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast
89 // it to the right type here.
90 auto header
= reinterpret_cast<const platform_mach_header
*>(mh
);
92 SharedLibrariesLock lock
;
93 if (!sSharedLibrariesList
) {
97 uint32_t count
= sSharedLibrariesList
->size();
98 for (uint32_t i
= 0; i
< count
; ++i
) {
99 if ((*sSharedLibrariesList
)[i
].header
== header
) {
100 sSharedLibrariesList
->erase(sSharedLibrariesList
->begin() + i
);
106 void SharedLibraryInfo::Initialize() {
107 // NOTE: We intentionally leak this memory here. We're allocating dynamically
108 // in order to avoid static initializers.
109 sSharedLibrariesList
= new std::vector
<NativeSharedLibrary
>();
111 _dyld_register_func_for_add_image(SharedLibraryAddImage
);
112 _dyld_register_func_for_remove_image(SharedLibraryRemoveImage
);
115 static void addSharedLibrary(const platform_mach_header
* header
,
116 const char* path
, SharedLibraryInfo
& info
) {
117 const struct load_command
* cmd
=
118 reinterpret_cast<const struct load_command
*>(header
+ 1);
121 unsigned long long start
= reinterpret_cast<unsigned long long>(header
);
122 // Find the cmd segment in the macho image. It will contain the offset we care
124 const uint8_t* uuid_bytes
= nullptr;
125 for (unsigned int i
= 0;
126 cmd
&& (i
< header
->ncmds
) && (uuid_bytes
== nullptr || size
== 0);
128 if (cmd
->cmd
== CMD_SEGMENT
) {
129 const mach_segment_command_type
* seg
=
130 reinterpret_cast<const mach_segment_command_type
*>(cmd
);
132 if (!strcmp(seg
->segname
, "__TEXT")) {
135 } else if (cmd
->cmd
== LC_UUID
) {
136 const uuid_command
* ucmd
= reinterpret_cast<const uuid_command
*>(cmd
);
137 uuid_bytes
= ucmd
->uuid
;
140 cmd
= reinterpret_cast<const struct load_command
*>(
141 reinterpret_cast<const char*>(cmd
) + cmd
->cmdsize
);
145 std::string breakpadId
;
146 if (uuid_bytes
!= nullptr) {
147 static constexpr char digits
[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
148 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
149 for (int i
= 0; i
< 16; ++i
) {
150 uint8_t byte
= uuid_bytes
[i
];
151 uuid
+= digits
[byte
>> 4];
152 uuid
+= digits
[byte
& 0xFu
];
155 // Breakpad id is the same as the uuid but with the additional trailing 0
156 // for the breakpad id age.
162 std::string pathStr
= path
;
164 size_t pos
= pathStr
.rfind('\\');
165 std::string nameStr
=
166 (pos
!= std::string::npos
) ? pathStr
.substr(pos
+ 1) : pathStr
;
168 const NXArchInfo
* archInfo
=
169 NXGetArchInfoFromCpuType(header
->cputype
, header
->cpusubtype
);
171 info
.AddSharedLibrary(SharedLibrary(
172 start
, start
+ size
, 0, breakpadId
, uuid
, nameStr
, pathStr
, nameStr
,
173 pathStr
, std::string
{}, archInfo
? archInfo
->name
: ""));
176 // Translate the statically stored sSharedLibrariesList information into a
177 // SharedLibraryInfo object.
178 SharedLibraryInfo
SharedLibraryInfo::GetInfoForSelf() {
179 SharedLibrariesLock lock
;
180 SharedLibraryInfo sharedLibraryInfo
;
182 for (auto& info
: *sSharedLibrariesList
) {
183 addSharedLibrary(info
.header
, info
.path
.c_str(), sharedLibraryInfo
);
186 // Add the entry for dyld itself.
187 // We only support macOS 10.12+, which corresponds to dyld version 15+.
188 // dyld version 15 added the dyldPath property.
189 task_dyld_info_data_t task_dyld_info
;
190 mach_msg_type_number_t count
= TASK_DYLD_INFO_COUNT
;
191 if (task_info(mach_task_self(), TASK_DYLD_INFO
, (task_info_t
)&task_dyld_info
,
192 &count
) != KERN_SUCCESS
) {
193 return sharedLibraryInfo
;
196 struct dyld_all_image_infos
* aii
=
197 (struct dyld_all_image_infos
*)task_dyld_info
.all_image_info_addr
;
198 if (aii
->version
>= 15) {
199 const platform_mach_header
* header
=
200 reinterpret_cast<const platform_mach_header
*>(
201 aii
->dyldImageLoadAddress
);
202 addSharedLibrary(header
, aii
->dyldPath
, sharedLibraryInfo
);
205 return sharedLibraryInfo
;