2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
29 #include <sys/errno.h>
34 #include <TargetConditionals.h>
35 #include <malloc/malloc.h>
36 #include <mach-o/dyld_priv.h>
37 #include <mach-o/dyld_images.h>
43 #include "AllImages.h"
46 #include "Diagnostics.h"
47 #include "DyldSharedCache.h"
54 void parseDlHandle(void* h
, const MachOLoaded
** mh
, bool* dontContinue
);
57 // only in macOS and deprecated
60 // macOS needs to support an old API that only works with fileype==MH_BUNDLE.
61 // In this deprecated API (unlike dlopen), loading and linking are separate steps.
62 // NSCreateObjectFileImageFrom*() just maps in the bundle mach-o file.
63 // NSLinkModule() does the load of dependent modules and rebasing/binding.
64 // To unload one of these, you must call NSUnLinkModule() and NSDestroyObjectFileImage() in any order!
67 NSObjectFileImageReturnCode
NSCreateObjectFileImageFromFile(const char* path
, NSObjectFileImage
* ofi
)
69 log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path
, ofi
);
73 if ( dyld3::stat(path
, &statbuf
) == -1 )
74 return NSObjectFileImageFailure
;
76 // create ofi that just contains path. NSLinkModule does all the work
78 result
.path
= strdup(path
);
79 result
.memSource
= nullptr;
81 result
.loadAddress
= nullptr;
83 *ofi
= gAllImages
.addNSObjectFileImage(result
);
85 log_apis("NSCreateObjectFileImageFromFile() => %p\n", *ofi
);
87 return NSObjectFileImageSuccess
;
90 NSObjectFileImageReturnCode
NSCreateObjectFileImageFromMemory(const void* memImage
, size_t memImageSize
, NSObjectFileImage
*ofi
)
92 log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage
, memImageSize
, ofi
);
94 // sanity check the buffer is a mach-o file
95 __block Diagnostics diag
;
97 // check if it is current arch mach-o or fat with slice for current arch
99 const MachOFile
* mf
= (MachOFile
*)memImage
;
100 if ( mf
->hasMachOMagic() && mf
->isMachO(diag
, memImageSize
) ) {
101 usable
= (gAllImages
.archs().grade(mf
->cputype
, mf
->cpusubtype
, false) != 0);
103 else if ( const FatFile
* ff
= FatFile::isFatFile(memImage
) ) {
104 uint64_t sliceOffset
;
107 if ( ff
->isFatFileWithSlice(diag
, memImageSize
, gAllImages
.archs(), false, sliceOffset
, sliceLen
, missingSlice
) ) {
108 mf
= (MachOFile
*)((long)memImage
+sliceOffset
);
109 if ( mf
->isMachO(diag
, sliceLen
) ) {
115 if ( !mf
->builtForPlatform(Platform::macOS
) )
119 log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
120 return NSObjectFileImageFailure
;
123 // this API can only be used with bundles
124 if ( !mf
->isBundle() ) {
125 log_apis("NSCreateObjectFileImageFromMemory() not a bundle\n");
126 return NSObjectFileImageInappropriateFile
;
129 // allocate ofi that just lists the memory range
131 result
.path
= nullptr;
132 result
.memSource
= memImage
;
133 result
.memLength
= memImageSize
;
134 result
.loadAddress
= nullptr;
136 *ofi
= gAllImages
.addNSObjectFileImage(result
);
138 log_apis("NSCreateObjectFileImageFromMemory() => %p\n", *ofi
);
140 return NSObjectFileImageSuccess
;
143 NSModule
NSLinkModule(NSObjectFileImage ofi
, const char* moduleName
, uint32_t options
)
145 DYLD_LOAD_LOCK_THIS_BLOCK
146 log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi
, moduleName
, options
);
148 __block
const char* path
= nullptr;
149 bool foundImage
= gAllImages
.forNSObjectFileImage(ofi
, ^(OFIInfo
&image
) {
150 // if this is memory based image, write to temp file, then use file based loading
151 if ( image
.memSource
!= nullptr ) {
152 // make temp file with content of memory buffer
153 image
.path
= nullptr;
154 char tempFileName
[PATH_MAX
];
155 const char* tmpDir
= getenv("TMPDIR");
156 if ( (tmpDir
!= nullptr) && (strlen(tmpDir
) > 2) ) {
157 strlcpy(tempFileName
, tmpDir
, PATH_MAX
);
158 if ( tmpDir
[strlen(tmpDir
)-1] != '/' )
159 strlcat(tempFileName
, "/", PATH_MAX
);
162 strlcpy(tempFileName
,"/tmp/", PATH_MAX
);
163 strlcat(tempFileName
, "NSCreateObjectFileImageFromMemory-XXXXXXXX", PATH_MAX
);
164 int fd
= ::mkstemp(tempFileName
);
166 ssize_t writtenSize
= ::pwrite(fd
, image
.memSource
, image
.memLength
, 0);
167 if ( writtenSize
== image
.memLength
) {
168 image
.path
= strdup(tempFileName
);
171 log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
180 // ofi is invalid if not in list
181 log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
188 // dlopen the binary outside of the read lock as we don't want to risk deadlock
190 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
191 const MachOLoaded
* loadAddress
= gAllImages
.dlopen(diag
, path
, false, false, false, false, true, callerAddress
);
192 if ( diag
.hasError() ) {
193 log_apis(" NSLinkModule: failed: %s\n", diag
.errorMessage());
197 // Now update the load address of this object
198 gAllImages
.forNSObjectFileImage(ofi
, ^(OFIInfo
&image
) {
199 image
.loadAddress
= loadAddress
;
201 // if memory based load, delete temp file
202 if ( image
.memSource
!= nullptr ) {
203 log_apis(" NSLinkModule: delete temp file: %s\n", image
.path
);
204 ::unlink(image
.path
);
208 log_apis("NSLinkModule() => %p\n", loadAddress
);
209 return (NSModule
)loadAddress
;
212 // NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
213 bool NSUnLinkModule(NSModule
module, uint32_t options
)
215 DYLD_LOAD_LOCK_THIS_BLOCK
216 log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options
);
218 __block
const mach_header
* mh
= nullptr;
219 gAllImages
.infoForImageMappedAt(module, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
220 mh
= foundImage
.loadedAddress();
224 gAllImages
.decRefCount(mh
); // removes image if reference count went to zero
226 log_apis("NSUnLinkModule() => %d\n", mh
!= nullptr);
228 return mh
!= nullptr;
231 // NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
232 bool NSDestroyObjectFileImage(NSObjectFileImage imageHandle
)
234 log_apis("NSDestroyObjectFileImage(%p)\n", imageHandle
);
236 __block
const void* memSource
= nullptr;
237 __block
size_t memLength
= 0;
238 __block
const char* path
= nullptr;
239 bool foundImage
= gAllImages
.forNSObjectFileImage(imageHandle
, ^(OFIInfo
&image
) {
241 memSource
= image
.memSource
;
242 memLength
= image
.memLength
;
250 gAllImages
.removeNSObjectFileImage(imageHandle
);
252 // if object was created from a memory, release that memory
253 // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands ownership of the memory to dyld
254 if ( memSource
!= nullptr ) {
255 // we don't know if memory came from malloc or vm_allocate, so ask malloc
256 if ( malloc_size(memSource
) != 0 )
257 free((void*)(memSource
));
259 vm_deallocate(mach_task_self(), (vm_address_t
)memSource
, memLength
);
266 uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage
)
268 halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete");
271 const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage
, uint32_t ordinal
)
273 halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete");
276 uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage
)
278 halt("NSSymbolReferenceCountInObjectFileImage() is obsolete");
281 const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage
, uint32_t ordinal
, bool *tentative_definition
)
283 halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
286 bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage imageHandle
, const char* symbolName
)
288 log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", imageHandle
, symbolName
);
290 __block
bool hasSymbol
= false;
291 bool foundImage
= gAllImages
.forNSObjectFileImage(imageHandle
, ^(OFIInfo
&image
) {
293 bool resultPointsToInstructions
= false;
294 hasSymbol
= image
.loadAddress
->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
);
297 // ofi is invalid if not in list
304 void* NSGetSectionDataInObjectFileImage(NSObjectFileImage imageHandle
, const char* segmentName
, const char* sectionName
, size_t* size
)
306 __block
const void* result
= nullptr;
307 bool foundImage
= gAllImages
.forNSObjectFileImage(imageHandle
, ^(OFIInfo
&image
) {
309 result
= image
.loadAddress
->findSectionContent(segmentName
, sectionName
, sz
);
313 // ofi is invalid if not in list
317 return (void*)result
;
320 const char* NSNameOfModule(NSModule m
)
322 log_apis("NSNameOfModule(%p)\n", m
);
324 __block
const char* result
= nullptr;
325 gAllImages
.infoForImageMappedAt(m
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
326 result
= gAllImages
.imagePath(foundImage
.image());
332 const char* NSLibraryNameForModule(NSModule m
)
334 log_apis("NSLibraryNameForModule(%p)\n", m
);
336 __block
const char* result
= nullptr;
337 gAllImages
.infoForImageMappedAt(m
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
338 result
= gAllImages
.imagePath(foundImage
.image());
344 static bool flatFindSymbol(const char* symbolName
, void** symbolAddress
, const mach_header
** foundInImageAtLoadAddress
)
346 // <rdar://problem/59265987> allow flat lookup to find "_memcpy" even though it is not implemented as that name in any dylib
347 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
348 return gAllImages
.findDependent(mh
, depIndex
);
351 __block
bool result
= false;
352 gAllImages
.forEachImage(^(const LoadedImage
& loadedImage
, bool& stop
) {
353 bool resultPointsToInstructions
= false;
354 if ( loadedImage
.loadedAddress()->hasExportedSymbol(symbolName
, finder
, symbolAddress
, &resultPointsToInstructions
) ) {
355 *foundInImageAtLoadAddress
= loadedImage
.loadedAddress();
363 bool NSIsSymbolNameDefined(const char* symbolName
)
365 log_apis("NSIsSymbolNameDefined(%s)\n", symbolName
);
367 const mach_header
* foundInImageAtLoadAddress
;
369 return flatFindSymbol(symbolName
, &address
, &foundInImageAtLoadAddress
);
372 bool NSIsSymbolNameDefinedWithHint(const char* symbolName
, const char* libraryNameHint
)
374 log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName
, libraryNameHint
);
376 const mach_header
* foundInImageAtLoadAddress
;
378 return flatFindSymbol(symbolName
, &address
, &foundInImageAtLoadAddress
);
381 bool NSIsSymbolNameDefinedInImage(const struct mach_header
* mh
, const char* symbolName
)
383 log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh
, symbolName
);
386 bool resultPointsToInstructions
= false;
387 return ((MachOLoaded
*)mh
)->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
);
390 NSSymbol
NSLookupAndBindSymbol(const char* symbolName
)
392 log_apis("NSLookupAndBindSymbol(%s)\n", symbolName
);
394 const mach_header
* foundInImageAtLoadAddress
;
396 if ( flatFindSymbol(symbolName
, &symbolAddress
, &foundInImageAtLoadAddress
) ) {
397 return (NSSymbol
)symbolAddress
;
402 NSSymbol
NSLookupAndBindSymbolWithHint(const char* symbolName
, const char* libraryNameHint
)
404 log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName
, libraryNameHint
);
406 const mach_header
* foundInImageAtLoadAddress
;
408 if ( flatFindSymbol(symbolName
, &symbolAddress
, &foundInImageAtLoadAddress
) ) {
409 return (NSSymbol
)symbolAddress
;
414 NSSymbol
NSLookupSymbolInModule(NSModule
module, const char* symbolName
)
416 log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName
);
418 const MachOLoaded
* mh
= (const MachOLoaded
*)module;
420 bool resultPointsToInstructions
= false;
421 if ( mh
->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
) ) {
422 return (NSSymbol
)addr
;
427 NSSymbol
NSLookupSymbolInImage(const mach_header
* mh
, const char* symbolName
, uint32_t options
)
429 log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh
, symbolName
, options
);
432 bool resultPointsToInstructions
= false;
433 if ( ((MachOLoaded
*)mh
)->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
) ) {
434 log_apis(" NSLookupSymbolInImage() => %p\n", addr
);
435 return (NSSymbol
)addr
;
437 if ( options
& NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
) {
438 log_apis(" NSLookupSymbolInImage() => NULL\n");
445 const char* NSNameOfSymbol(NSSymbol symbol
)
447 halt("NSNameOfSymbol() is obsolete");
450 void* NSAddressOfSymbol(NSSymbol symbol
)
452 log_apis("NSAddressOfSymbol(%p)\n", symbol
);
454 // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table
455 return (void*)symbol
;
458 NSModule
NSModuleForSymbol(NSSymbol symbol
)
460 log_apis("NSModuleForSymbol(%p)\n", symbol
);
462 __block NSModule result
= nullptr;
463 gAllImages
.infoForImageMappedAt(symbol
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
464 result
= (NSModule
)foundImage
.loadedAddress();
470 void NSLinkEditError(NSLinkEditErrors
*c
, int *errorNumber
, const char** fileName
, const char** errorString
)
472 log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c
, errorNumber
, fileName
, errorString
);
473 *c
= NSLinkEditOtherError
;
479 bool NSAddLibrary(const char* pathName
)
481 log_apis("NSAddLibrary(%s)\n", pathName
);
483 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
484 return ( dlopen_internal(pathName
, 0, callerAddress
) != nullptr);
487 bool NSAddLibraryWithSearching(const char* pathName
)
489 log_apis("NSAddLibraryWithSearching(%s)\n", pathName
);
491 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
492 return ( dlopen_internal(pathName
, 0, callerAddress
) != nullptr);
495 const mach_header
* NSAddImage(const char* imageName
, uint32_t options
)
497 log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName
, options
);
499 // Note: this is a quick and dirty implementation that just uses dlopen() and ignores some option flags
500 uint32_t dloptions
= 0;
501 if ( (options
& NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
) != 0 )
502 dloptions
|= RTLD_NOLOAD
;
504 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
505 void* h
= dlopen_internal(imageName
, dloptions
, callerAddress
);
506 if ( h
!= nullptr ) {
507 const MachOLoaded
* mh
;
509 parseDlHandle(h
, &mh
, &dontContinue
);
513 if ( (options
& (NSADDIMAGE_OPTION_RETURN_ON_ERROR
|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
)) == 0 ) {
514 halt("NSAddImage() image not found");
519 void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers
*handlers
)
521 halt("NSInstallLinkEditErrorHandlers() is obsolete");
524 bool _dyld_present(void)
526 log_apis("_dyld_present()\n");
531 bool _dyld_launched_prebound(void)
533 halt("_dyld_launched_prebound() is obsolete");
536 bool _dyld_all_twolevel_modules_prebound(void)
538 halt("_dyld_all_twolevel_modules_prebound() is obsolete");
541 bool _dyld_bind_fully_image_containing_address(const void* address
)
543 log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address
);
545 // in dyld3, everything is always fully bound
549 bool _dyld_image_containing_address(const void* address
)
551 log_apis("_dyld_image_containing_address(%p)\n", address
);
553 return (dyld_image_header_containing_address(address
) != nullptr);
556 void _dyld_lookup_and_bind(const char* symbolName
, void **address
, NSModule
* module)
558 log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName
, address
, module);
560 const mach_header
* foundInImageAtLoadAddress
;
561 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
562 *module = (NSModule
)foundInImageAtLoadAddress
;
570 void _dyld_lookup_and_bind_with_hint(const char* symbolName
, const char* libraryNameHint
, void** address
, NSModule
* module)
572 log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName
, libraryNameHint
, address
, module);
574 const mach_header
* foundInImageAtLoadAddress
;
575 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
576 *module = (NSModule
)foundInImageAtLoadAddress
;
585 void _dyld_lookup_and_bind_fully(const char* symbolName
, void** address
, NSModule
* module)
587 log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName
, address
, module);
589 const mach_header
* foundInImageAtLoadAddress
;
590 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
591 *module = (NSModule
)foundInImageAtLoadAddress
;
599 const struct mach_header
* _dyld_get_image_header_containing_address(const void* address
)
601 log_apis("_dyld_get_image_header_containing_address(%p)\n", address
);
603 return dyld_image_header_containing_address(address
);