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>
41 #if __has_feature(ptrauth_calls)
47 #include "AllImages.h"
50 #include "Diagnostics.h"
51 #include "DyldSharedCache.h"
58 void parseDlHandle(void* h
, const MachOLoaded
** mh
, bool* dontContinue
);
61 // only in macOS and deprecated
64 // macOS needs to support an old API that only works with fileype==MH_BUNDLE.
65 // In this deprecated API (unlike dlopen), loading and linking are separate steps.
66 // NSCreateObjectFileImageFrom*() just maps in the bundle mach-o file.
67 // NSLinkModule() does the load of dependent modules and rebasing/binding.
68 // To unload one of these, you must call NSUnLinkModule() and NSDestroyObjectFileImage() in any order!
71 NSObjectFileImageReturnCode
NSCreateObjectFileImageFromFile(const char* path
, NSObjectFileImage
* ofi
)
73 log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path
, ofi
);
77 if ( dyld3::stat(path
, &statbuf
) == -1 )
78 return NSObjectFileImageFailure
;
80 // create ofi that just contains path. NSLinkModule does all the work
82 result
.path
= strdup(path
);
83 result
.memSource
= nullptr;
85 result
.loadAddress
= nullptr;
87 *ofi
= gAllImages
.addNSObjectFileImage(result
);
89 log_apis("NSCreateObjectFileImageFromFile() => %p\n", *ofi
);
91 return NSObjectFileImageSuccess
;
94 NSObjectFileImageReturnCode
NSCreateObjectFileImageFromMemory(const void* memImage
, size_t memImageSize
, NSObjectFileImage
*ofi
)
96 log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage
, memImageSize
, ofi
);
98 // sanity check the buffer is a mach-o file
99 __block Diagnostics diag
;
101 // check if it is current arch mach-o or fat with slice for current arch
103 const MachOFile
* mf
= (MachOFile
*)memImage
;
104 if ( mf
->hasMachOMagic() && mf
->isMachO(diag
, memImageSize
) ) {
105 usable
= (gAllImages
.archs().grade(mf
->cputype
, mf
->cpusubtype
, false) != 0);
107 else if ( const FatFile
* ff
= FatFile::isFatFile(memImage
) ) {
108 uint64_t sliceOffset
;
111 if ( ff
->isFatFileWithSlice(diag
, memImageSize
, gAllImages
.archs(), false, sliceOffset
, sliceLen
, missingSlice
) ) {
112 mf
= (MachOFile
*)((long)memImage
+sliceOffset
);
113 if ( mf
->isMachO(diag
, sliceLen
) ) {
119 if ( !mf
->builtForPlatform(Platform::macOS
) )
123 log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
124 return NSObjectFileImageFailure
;
127 // this API can only be used with bundles
128 if ( !mf
->isBundle() ) {
129 log_apis("NSCreateObjectFileImageFromMemory() not a bundle\n");
130 return NSObjectFileImageInappropriateFile
;
133 // allocate ofi that just lists the memory range
135 result
.path
= nullptr;
136 result
.memSource
= memImage
;
137 result
.memLength
= memImageSize
;
138 result
.loadAddress
= nullptr;
140 *ofi
= gAllImages
.addNSObjectFileImage(result
);
142 log_apis("NSCreateObjectFileImageFromMemory() => %p\n", *ofi
);
144 return NSObjectFileImageSuccess
;
147 NSModule
NSLinkModule(NSObjectFileImage ofi
, const char* moduleName
, uint32_t options
)
149 DYLD_LOAD_LOCK_THIS_BLOCK
150 log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi
, moduleName
, options
);
152 __block
const char* path
= nullptr;
153 bool foundImage
= gAllImages
.forNSObjectFileImage(ofi
, ^(OFIInfo
&image
) {
154 // if this is memory based image, write to temp file, then use file based loading
155 if ( image
.memSource
!= nullptr ) {
156 // make temp file with content of memory buffer
157 image
.path
= nullptr;
158 char tempFileName
[PATH_MAX
];
159 const char* tmpDir
= getenv("TMPDIR");
160 if ( (tmpDir
!= nullptr) && (strlen(tmpDir
) > 2) ) {
161 strlcpy(tempFileName
, tmpDir
, PATH_MAX
);
162 if ( tmpDir
[strlen(tmpDir
)-1] != '/' )
163 strlcat(tempFileName
, "/", PATH_MAX
);
166 strlcpy(tempFileName
,"/tmp/", PATH_MAX
);
167 strlcat(tempFileName
, "NSCreateObjectFileImageFromMemory-XXXXXXXX", PATH_MAX
);
168 int fd
= ::mkstemp(tempFileName
);
170 ssize_t writtenSize
= ::pwrite(fd
, image
.memSource
, image
.memLength
, 0);
171 if ( writtenSize
== image
.memLength
) {
172 image
.path
= strdup(tempFileName
);
175 log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
184 // ofi is invalid if not in list
185 log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
192 // dlopen the binary outside of the read lock as we don't want to risk deadlock
194 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
195 const MachOLoaded
* loadAddress
= gAllImages
.dlopen(diag
, path
, false, false, false, false, true, callerAddress
);
196 if ( diag
.hasError() ) {
197 log_apis(" NSLinkModule: failed: %s\n", diag
.errorMessage());
201 // Now update the load address of this object
202 gAllImages
.forNSObjectFileImage(ofi
, ^(OFIInfo
&image
) {
203 image
.loadAddress
= loadAddress
;
205 // if memory based load, delete temp file
206 if ( image
.memSource
!= nullptr ) {
207 log_apis(" NSLinkModule: delete temp file: %s\n", image
.path
);
208 ::unlink(image
.path
);
212 log_apis("NSLinkModule() => %p\n", loadAddress
);
213 return (NSModule
)loadAddress
;
216 // NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
217 bool NSUnLinkModule(NSModule
module, uint32_t options
)
219 DYLD_LOAD_LOCK_THIS_BLOCK
220 log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options
);
222 __block
const mach_header
* mh
= nullptr;
223 gAllImages
.infoForImageMappedAt(module, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
224 mh
= foundImage
.loadedAddress();
228 gAllImages
.decRefCount(mh
); // removes image if reference count went to zero
230 log_apis("NSUnLinkModule() => %d\n", mh
!= nullptr);
232 return mh
!= nullptr;
235 // NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
236 bool NSDestroyObjectFileImage(NSObjectFileImage imageHandle
)
238 log_apis("NSDestroyObjectFileImage(%p)\n", imageHandle
);
240 __block
const void* memSource
= nullptr;
241 __block
size_t memLength
= 0;
242 __block
const char* path
= nullptr;
243 bool foundImage
= gAllImages
.forNSObjectFileImage(imageHandle
, ^(OFIInfo
&image
) {
245 memSource
= image
.memSource
;
246 memLength
= image
.memLength
;
254 gAllImages
.removeNSObjectFileImage(imageHandle
);
256 // if object was created from a memory, release that memory
257 // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands ownership of the memory to dyld
258 if ( memSource
!= nullptr ) {
259 // we don't know if memory came from malloc or vm_allocate, so ask malloc
260 if ( malloc_size(memSource
) != 0 )
261 free((void*)(memSource
));
263 vm_deallocate(mach_task_self(), (vm_address_t
)memSource
, memLength
);
270 uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage
)
272 halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete");
275 const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage
, uint32_t ordinal
)
277 halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete");
280 uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage
)
282 halt("NSSymbolReferenceCountInObjectFileImage() is obsolete");
285 const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage
, uint32_t ordinal
, bool *tentative_definition
)
287 halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
290 bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage imageHandle
, const char* symbolName
)
292 log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", imageHandle
, symbolName
);
294 __block
bool hasSymbol
= false;
295 bool foundImage
= gAllImages
.forNSObjectFileImage(imageHandle
, ^(OFIInfo
&image
) {
297 bool resultPointsToInstructions
= false;
298 hasSymbol
= image
.loadAddress
->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
);
301 // ofi is invalid if not in list
308 void* NSGetSectionDataInObjectFileImage(NSObjectFileImage imageHandle
, const char* segmentName
, const char* sectionName
, size_t* size
)
310 __block
const void* result
= nullptr;
311 bool foundImage
= gAllImages
.forNSObjectFileImage(imageHandle
, ^(OFIInfo
&image
) {
313 result
= image
.loadAddress
->findSectionContent(segmentName
, sectionName
, sz
);
317 // ofi is invalid if not in list
321 return (void*)result
;
324 const char* NSNameOfModule(NSModule m
)
326 log_apis("NSNameOfModule(%p)\n", m
);
328 __block
const char* result
= nullptr;
329 gAllImages
.infoForImageMappedAt(m
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
330 result
= gAllImages
.imagePath(foundImage
.image());
336 const char* NSLibraryNameForModule(NSModule m
)
338 log_apis("NSLibraryNameForModule(%p)\n", m
);
340 __block
const char* result
= nullptr;
341 gAllImages
.infoForImageMappedAt(m
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
342 result
= gAllImages
.imagePath(foundImage
.image());
348 static bool flatFindSymbol(const char* symbolName
, void** symbolAddress
, const mach_header
** foundInImageAtLoadAddress
)
350 // <rdar://problem/59265987> allow flat lookup to find "_memcpy" even though it is not implemented as that name in any dylib
351 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
352 return gAllImages
.findDependent(mh
, depIndex
);
355 __block
bool result
= false;
356 gAllImages
.forEachImage(^(const LoadedImage
& loadedImage
, bool& stop
) {
357 bool resultPointsToInstructions
= false;
358 if ( loadedImage
.loadedAddress()->hasExportedSymbol(symbolName
, finder
, symbolAddress
, &resultPointsToInstructions
) ) {
359 *foundInImageAtLoadAddress
= loadedImage
.loadedAddress();
367 bool NSIsSymbolNameDefined(const char* symbolName
)
369 log_apis("NSIsSymbolNameDefined(%s)\n", symbolName
);
371 const mach_header
* foundInImageAtLoadAddress
;
373 return flatFindSymbol(symbolName
, &address
, &foundInImageAtLoadAddress
);
376 bool NSIsSymbolNameDefinedWithHint(const char* symbolName
, const char* libraryNameHint
)
378 log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName
, libraryNameHint
);
380 const mach_header
* foundInImageAtLoadAddress
;
382 return flatFindSymbol(symbolName
, &address
, &foundInImageAtLoadAddress
);
385 bool NSIsSymbolNameDefinedInImage(const struct mach_header
* mh
, const char* symbolName
)
387 log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh
, symbolName
);
390 bool resultPointsToInstructions
= false;
391 return ((MachOLoaded
*)mh
)->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
);
394 NSSymbol
NSLookupAndBindSymbol(const char* symbolName
)
396 log_apis("NSLookupAndBindSymbol(%s)\n", symbolName
);
398 const mach_header
* foundInImageAtLoadAddress
;
400 if ( flatFindSymbol(symbolName
, &symbolAddress
, &foundInImageAtLoadAddress
) ) {
401 return (NSSymbol
)symbolAddress
;
406 NSSymbol
NSLookupAndBindSymbolWithHint(const char* symbolName
, const char* libraryNameHint
)
408 log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName
, libraryNameHint
);
410 const mach_header
* foundInImageAtLoadAddress
;
412 if ( flatFindSymbol(symbolName
, &symbolAddress
, &foundInImageAtLoadAddress
) ) {
413 return (NSSymbol
)symbolAddress
;
418 NSSymbol
NSLookupSymbolInModule(NSModule
module, const char* symbolName
)
420 log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName
);
422 const MachOLoaded
* mh
= (const MachOLoaded
*)module;
424 bool resultPointsToInstructions
= false;
425 if ( mh
->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
) ) {
426 return (NSSymbol
)addr
;
431 NSSymbol
NSLookupSymbolInImage(const mach_header
* mh
, const char* symbolName
, uint32_t options
)
433 log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh
, symbolName
, options
);
436 bool resultPointsToInstructions
= false;
437 if ( ((MachOLoaded
*)mh
)->hasExportedSymbol(symbolName
, nullptr, &addr
, &resultPointsToInstructions
) ) {
438 log_apis(" NSLookupSymbolInImage() => %p\n", addr
);
439 return (NSSymbol
)addr
;
441 if ( options
& NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
) {
442 log_apis(" NSLookupSymbolInImage() => NULL\n");
449 const char* NSNameOfSymbol(NSSymbol symbol
)
451 halt("NSNameOfSymbol() is obsolete");
454 void* NSAddressOfSymbol(NSSymbol symbol
)
456 log_apis("NSAddressOfSymbol(%p)\n", symbol
);
458 if ( symbol
== nullptr )
461 // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table
462 void *result
= (void*)symbol
;
464 #if __has_feature(ptrauth_calls)
465 __block
const MachOLoaded
*module = nullptr;
466 gAllImages
.infoForImageMappedAt(symbol
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
467 module = foundImage
.loadedAddress();
470 int64_t slide
= module->getSlide();
471 __block
bool resultPointsToInstructions
= false;
472 module->forEachSection(^(const MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
473 uint64_t sectStartAddr
= sectInfo
.sectAddr
+ slide
;
474 uint64_t sectEndAddr
= sectStartAddr
+ sectInfo
.sectSize
;
475 if ( ((uint64_t)result
>= sectStartAddr
) && ((uint64_t)result
< sectEndAddr
) ) {
476 resultPointsToInstructions
= (sectInfo
.sectFlags
& S_ATTR_PURE_INSTRUCTIONS
) || (sectInfo
.sectFlags
& S_ATTR_SOME_INSTRUCTIONS
);
481 if (resultPointsToInstructions
) {
482 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
489 NSModule
NSModuleForSymbol(NSSymbol symbol
)
491 log_apis("NSModuleForSymbol(%p)\n", symbol
);
493 __block NSModule result
= nullptr;
494 gAllImages
.infoForImageMappedAt(symbol
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
495 result
= (NSModule
)foundImage
.loadedAddress();
501 void NSLinkEditError(NSLinkEditErrors
*c
, int *errorNumber
, const char** fileName
, const char** errorString
)
503 log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c
, errorNumber
, fileName
, errorString
);
504 *c
= NSLinkEditOtherError
;
510 bool NSAddLibrary(const char* pathName
)
512 log_apis("NSAddLibrary(%s)\n", pathName
);
514 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
515 return ( dlopen_internal(pathName
, 0, callerAddress
) != nullptr);
518 bool NSAddLibraryWithSearching(const char* pathName
)
520 log_apis("NSAddLibraryWithSearching(%s)\n", pathName
);
522 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
523 return ( dlopen_internal(pathName
, 0, callerAddress
) != nullptr);
526 const mach_header
* NSAddImage(const char* imageName
, uint32_t options
)
528 log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName
, options
);
530 // Note: this is a quick and dirty implementation that just uses dlopen() and ignores some option flags
531 uint32_t dloptions
= 0;
532 if ( (options
& NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
) != 0 )
533 dloptions
|= RTLD_NOLOAD
;
535 void* callerAddress
= __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
536 void* h
= dlopen_internal(imageName
, dloptions
, callerAddress
);
537 if ( h
!= nullptr ) {
538 const MachOLoaded
* mh
;
540 parseDlHandle(h
, &mh
, &dontContinue
);
544 if ( (options
& (NSADDIMAGE_OPTION_RETURN_ON_ERROR
|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
)) == 0 ) {
545 halt("NSAddImage() image not found");
550 void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers
*handlers
)
552 halt("NSInstallLinkEditErrorHandlers() is obsolete");
555 bool _dyld_present(void)
557 log_apis("_dyld_present()\n");
562 bool _dyld_launched_prebound(void)
564 halt("_dyld_launched_prebound() is obsolete");
567 bool _dyld_all_twolevel_modules_prebound(void)
569 halt("_dyld_all_twolevel_modules_prebound() is obsolete");
572 bool _dyld_bind_fully_image_containing_address(const void* address
)
574 log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address
);
576 // in dyld3, everything is always fully bound
580 bool _dyld_image_containing_address(const void* address
)
582 log_apis("_dyld_image_containing_address(%p)\n", address
);
584 return (dyld_image_header_containing_address(address
) != nullptr);
587 void _dyld_lookup_and_bind(const char* symbolName
, void **address
, NSModule
* module)
589 log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName
, address
, module);
591 const mach_header
* foundInImageAtLoadAddress
;
592 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
593 *module = (NSModule
)foundInImageAtLoadAddress
;
601 void _dyld_lookup_and_bind_with_hint(const char* symbolName
, const char* libraryNameHint
, void** address
, NSModule
* module)
603 log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName
, libraryNameHint
, address
, module);
605 const mach_header
* foundInImageAtLoadAddress
;
606 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
607 *module = (NSModule
)foundInImageAtLoadAddress
;
616 void _dyld_lookup_and_bind_fully(const char* symbolName
, void** address
, NSModule
* module)
618 log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName
, address
, module);
620 const mach_header
* foundInImageAtLoadAddress
;
621 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
622 *module = (NSModule
)foundInImageAtLoadAddress
;
630 const struct mach_header
* _dyld_get_image_header_containing_address(const void* address
)
632 log_apis("_dyld_get_image_header_containing_address(%p)\n", address
);
634 return dyld_image_header_containing_address(address
);