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>
40 #include "dyld_priv.h"
42 #include "AllImages.h"
43 #include "MachOParser.h"
46 #include "Diagnostics.h"
47 #include "DyldSharedCache.h"
52 typedef dyld3::launch_cache::binary_format::Image BinaryImage
;
58 void parseDlHandle(void* h
, const mach_header
** mh
, bool* dontContinue
);
59 const mach_header
* loadImageAndDependents(Diagnostics
& diag
, const launch_cache::binary_format::Image
* imageToLoad
, bool bumpDlopenCount
);
62 // only in macOS and deprecated
63 #if __MAC_OS_X_VERSION_MIN_REQUIRED
65 // macOS needs to support an old API that only works with fileype==MH_BUNDLE.
66 // In this deprecated API (unlike dlopen), loading and linking are separate steps.
67 // NSCreateObjectFileImageFrom*() just maps in the bundle mach-o file.
68 // NSLinkModule() does the load of dependent modules and rebasing/binding.
69 // To unload one of these, you must call NSUnLinkModule() and NSDestroyObjectFileImage() in any order!
72 NSObjectFileImageReturnCode
NSCreateObjectFileImageFromFile(const char* path
, NSObjectFileImage
* ofi
)
74 log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path
, ofi
);
78 if ( ::stat(path
, &statbuf
) == -1 )
79 return NSObjectFileImageFailure
;
81 // create ofi that just contains path. NSLinkModule does all the work
82 __NSObjectFileImage
* result
= gAllImages
.addNSObjectFileImage();
83 result
->path
= strdup(path
);
84 result
->memSource
= nullptr;
85 result
->memLength
= 0;
86 result
->loadAddress
= nullptr;
87 result
->binImage
= nullptr;
90 log_apis("NSCreateObjectFileImageFromFile() => %p\n", result
);
92 return NSObjectFileImageSuccess
;
95 NSObjectFileImageReturnCode
NSCreateObjectFileImageFromMemory(const void* memImage
, size_t memImageSize
, NSObjectFileImage
*ofi
)
97 log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage
, memImageSize
, ofi
);
99 // sanity check the buffer is a mach-o file
100 __block Diagnostics diag
;
101 __block
const mach_header
* foundMH
= nullptr;
102 if ( MachOParser::isMachO(diag
, memImage
, memImageSize
) ) {
103 foundMH
= (mach_header
*)memImage
;
106 FatUtil::forEachSlice(diag
, memImage
, memImageSize
, ^(uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, const void* sliceStart
, size_t sliceSize
, bool& stop
) {
107 if ( MachOParser::isMachO(diag
, sliceStart
, sliceSize
) ) {
108 foundMH
= (mach_header
*)sliceStart
;
113 if ( foundMH
== nullptr ) {
114 log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
115 return NSObjectFileImageFailure
;
118 // this API can only be used with bundles
119 if ( foundMH
->filetype
!= MH_BUNDLE
) {
120 log_apis("NSCreateObjectFileImageFromMemory() not a bundle, filetype=%d\n", foundMH
->filetype
);
121 return NSObjectFileImageInappropriateFile
;
124 // allocate ofi that just lists the memory range
125 __NSObjectFileImage
* result
= gAllImages
.addNSObjectFileImage();
126 result
->path
= nullptr;
127 result
->memSource
= memImage
;
128 result
->memLength
= memImageSize
;
129 result
->loadAddress
= nullptr;
130 result
->binImage
= nullptr;
133 log_apis("NSCreateObjectFileImageFromMemory() => %p\n", result
);
135 return NSObjectFileImageSuccess
;
138 NSModule
NSLinkModule(NSObjectFileImage ofi
, const char* moduleName
, uint32_t options
)
140 log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi
, moduleName
, options
);
142 // ofi is invalid if not in list
143 if ( !gAllImages
.hasNSObjectFileImage(ofi
) ) {
144 log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
148 // if this is memory based image, write to temp file, then use file based loading
149 const BinaryImage
* imageToLoad
= nullptr;
150 if ( ofi
->memSource
!= nullptr ) {
151 // make temp file with content of memory buffer
152 bool successfullyWritten
= false;
153 ofi
->path
= ::tempnam(nullptr, "NSCreateObjectFileImageFromMemory-");
154 if ( ofi
->path
!= nullptr ) {
155 int fd
= ::open(ofi
->path
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
157 ssize_t writtenSize
= ::pwrite(fd
, ofi
->memSource
, ofi
->memLength
, 0);
158 if ( writtenSize
== ofi
->memLength
)
159 successfullyWritten
= true;
163 if ( !successfullyWritten
) {
164 if ( ofi
->path
!= nullptr ) {
165 free((void*)ofi
->path
);
168 log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
173 // check if image is in a known ImageGroup, but not loaded. if so, load using existing closure info
174 log_apis(" NSLinkModule: checking for pre-built closure for path: %s\n", ofi
->path
);
175 imageToLoad
= gAllImages
.findImageInKnownGroups(ofi
->path
);
176 // TODO: check symlinks, realpath
179 // if no existing closure, RPC to closured to create one
180 if ( imageToLoad
== nullptr ) {
181 const char* closuredErrorMessages
[3];
182 int closuredErrorMessagesCount
= 0;
183 if ( imageToLoad
== nullptr ) {
184 imageToLoad
= gAllImages
.messageClosured(ofi
->path
, "NSLinkModule", closuredErrorMessages
, closuredErrorMessagesCount
);
186 for (int i
=0; i
< closuredErrorMessagesCount
; ++i
) {
187 log_apis(" NSLinkModule: failed: %s\n", closuredErrorMessages
[i
]);
188 free((void*)closuredErrorMessages
[i
]);
192 // use Image info to load and fixup image and all its dependents
193 if ( imageToLoad
!= nullptr ) {
195 ofi
->loadAddress
= loadImageAndDependents(diag
, imageToLoad
, true);
196 if ( diag
.hasError() )
197 log_apis(" NSLinkModule: failed: %s\n", diag
.errorMessage());
200 // if memory based load, delete temp file
201 if ( ofi
->memSource
!= nullptr ) {
202 log_apis(" NSLinkModule: delete temp file: %s\n", ofi
->path
);
206 log_apis("NSLinkModule() => %p\n", ofi
->loadAddress
);
207 return (NSModule
)ofi
->loadAddress
;
210 // NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
211 bool NSUnLinkModule(NSModule
module, uint32_t options
)
213 log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options
);
216 const mach_header
* mh
= (mach_header
*)module;
217 launch_cache::Image image
= gAllImages
.findByLoadAddress(mh
);
218 if ( image
.valid() ) {
219 // removes image if reference count went to zero
220 gAllImages
.decRefCount(mh
);
224 log_apis("NSUnLinkModule() => %d\n", result
);
229 // NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
230 bool NSDestroyObjectFileImage(NSObjectFileImage ofi
)
232 log_apis("NSDestroyObjectFileImage(%p)\n", ofi
);
234 // ofi is invalid if not in list
235 if ( !gAllImages
.hasNSObjectFileImage(ofi
) )
239 const void* memSource
= ofi
->memSource
;
240 size_t memLength
= ofi
->memLength
;
241 const char* path
= ofi
->path
;
244 gAllImages
.removeNSObjectFileImage(ofi
);
246 // if object was created from a memory, release that memory
247 // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands ownership of the memory to dyld
248 if ( memSource
!= nullptr ) {
249 // we don't know if memory came from malloc or vm_allocate, so ask malloc
250 if ( malloc_size(memSource
) != 0 )
251 free((void*)(memSource
));
253 vm_deallocate(mach_task_self(), (vm_address_t
)memSource
, memLength
);
260 uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage
)
262 halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete");
265 const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage
, uint32_t ordinal
)
267 halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete");
270 uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage
)
272 halt("NSSymbolReferenceCountInObjectFileImage() is obsolete");
275 const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage
, uint32_t ordinal
, bool *tentative_definition
)
277 halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
280 bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage ofi
, const char* symbolName
)
282 log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", ofi
, symbolName
);
284 // ofi is invalid if not in list
285 if ( !gAllImages
.hasNSObjectFileImage(ofi
) )
289 MachOParser
parser(ofi
->loadAddress
);
290 return parser
.hasExportedSymbol(symbolName
, ^(uint32_t , const char*, void*, const mach_header
**, void**) {
295 void* NSGetSectionDataInObjectFileImage(NSObjectFileImage ofi
, const char* segmentName
, const char* sectionName
, size_t* size
)
297 // ofi is invalid if not in list
298 if ( !gAllImages
.hasNSObjectFileImage(ofi
) )
301 __block
void* result
= nullptr;
302 MachOParser
parser(ofi
->loadAddress
);
303 parser
.forEachSection(^(const char* aSegName
, const char* aSectName
, uint32_t flags
, const void* content
, size_t aSize
, bool illegalSectionSize
, bool& stop
) {
304 if ( (strcmp(sectionName
, aSectName
) == 0) && (strcmp(segmentName
, aSegName
) == 0) ) {
305 result
= (void*)content
;
306 if ( size
!= nullptr )
314 const char* NSNameOfModule(NSModule m
)
316 log_apis("NSNameOfModule(%p)\n", m
);
318 const mach_header
* foundInLoadAddress
;
319 launch_cache::Image image
= gAllImages
.findByOwnedAddress(m
, &foundInLoadAddress
);
320 if ( image
.valid() ) {
321 return gAllImages
.imagePath(image
.binaryData());
326 const char* NSLibraryNameForModule(NSModule m
)
328 log_apis("NSLibraryNameForModule(%p)\n", m
);
330 const mach_header
* foundInLoadAddress
;
331 launch_cache::Image image
= gAllImages
.findByOwnedAddress(m
, &foundInLoadAddress
);
332 if ( image
.valid() ) {
333 return gAllImages
.imagePath(image
.binaryData());
339 static bool flatFindSymbol(const char* symbolName
, void** symbolAddress
, const mach_header
** foundInImageAtLoadAddress
)
341 for (uint32_t index
=0; index
< gAllImages
.count(); ++index
) {
342 const mach_header
* loadAddress
;
343 launch_cache::Image image
= gAllImages
.findByLoadOrder(index
, &loadAddress
);
344 if ( image
.valid() ) {
345 MachOParser
parser(loadAddress
);
346 if ( parser
.hasExportedSymbol(symbolName
, ^(uint32_t , const char* , void* , const mach_header
** , void**) { return false; }, symbolAddress
) ) {
347 *foundInImageAtLoadAddress
= loadAddress
;
355 bool NSIsSymbolNameDefined(const char* symbolName
)
357 log_apis("NSIsSymbolNameDefined(%s)\n", symbolName
);
359 const mach_header
* foundInImageAtLoadAddress
;
361 return flatFindSymbol(symbolName
, &address
, &foundInImageAtLoadAddress
);
364 bool NSIsSymbolNameDefinedWithHint(const char* symbolName
, const char* libraryNameHint
)
366 log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName
, libraryNameHint
);
368 const mach_header
* foundInImageAtLoadAddress
;
370 return flatFindSymbol(symbolName
, &address
, &foundInImageAtLoadAddress
);
373 bool NSIsSymbolNameDefinedInImage(const struct mach_header
* mh
, const char* symbolName
)
375 log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh
, symbolName
);
377 MachOParser::DependentFinder reExportFollower
= ^(uint32_t depIndex
, const char* depLoadPath
, void* extra
, const mach_header
** foundMH
, void** foundExtra
) {
378 *foundMH
= gAllImages
.alreadyLoaded(depLoadPath
, false);
379 return (*foundMH
!= nullptr);
382 MachOParser
parser(mh
);
384 return parser
.hasExportedSymbol(symbolName
, reExportFollower
, &result
);
387 NSSymbol
NSLookupAndBindSymbol(const char* symbolName
)
389 log_apis("NSLookupAndBindSymbol(%s)\n", symbolName
);
391 const mach_header
* foundInImageAtLoadAddress
;
393 if ( flatFindSymbol(symbolName
, &symbolAddress
, &foundInImageAtLoadAddress
) ) {
394 return (NSSymbol
)symbolAddress
;
399 NSSymbol
NSLookupAndBindSymbolWithHint(const char* symbolName
, const char* libraryNameHint
)
401 log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName
, libraryNameHint
);
403 const mach_header
* foundInImageAtLoadAddress
;
405 if ( flatFindSymbol(symbolName
, &symbolAddress
, &foundInImageAtLoadAddress
) ) {
406 return (NSSymbol
)symbolAddress
;
411 NSSymbol
NSLookupSymbolInModule(NSModule
module, const char* symbolName
)
413 log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName
);
415 MachOParser::DependentFinder reExportFollower
= ^(uint32_t depIndex
, const char* depLoadPath
, void* extra
, const mach_header
** foundMH
, void** foundExtra
) {
416 *foundMH
= gAllImages
.alreadyLoaded(depLoadPath
, false);
417 return (*foundMH
!= nullptr);
420 const mach_header
* mh
= (const mach_header
*)module;
422 if ( gAllImages
.findIndexForLoadAddress(mh
, loadIndex
) ) {
423 MachOParser
parser(mh
);
425 if ( parser
.hasExportedSymbol(symbolName
, reExportFollower
, &symAddress
) ) {
426 return (NSSymbol
)symAddress
;
432 NSSymbol
NSLookupSymbolInImage(const struct mach_header
* mh
, const char* symbolName
, uint32_t options
)
434 log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh
, symbolName
, options
);
436 MachOParser::DependentFinder reExportFollower
= ^(uint32_t depIndex
, const char* depLoadPath
, void* extra
, const mach_header
** foundMH
, void** foundExtra
) {
437 *foundMH
= gAllImages
.alreadyLoaded(depLoadPath
, false);
438 return (*foundMH
!= nullptr);
441 MachOParser
parser(mh
);
443 if ( parser
.hasExportedSymbol(symbolName
, reExportFollower
, &result
) ) {
444 log_apis(" NSLookupSymbolInImage() => %p\n", result
);
445 return (NSSymbol
)result
;
448 if ( options
& NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
) {
449 log_apis(" NSLookupSymbolInImage() => NULL\n");
455 const char* NSNameOfSymbol(NSSymbol symbol
)
457 halt("NSNameOfSymbol() is obsolete");
460 void* NSAddressOfSymbol(NSSymbol symbol
)
462 log_apis("NSAddressOfSymbol(%p)\n", symbol
);
464 // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table
465 return (void*)symbol
;
468 NSModule
NSModuleForSymbol(NSSymbol symbol
)
470 log_apis("NSModuleForSymbol(%p)\n", symbol
);
472 const mach_header
* foundInLoadAddress
;
473 launch_cache::Image image
= gAllImages
.findByOwnedAddress(symbol
, &foundInLoadAddress
);
474 if ( image
.valid() ) {
475 return (NSModule
)foundInLoadAddress
;
480 void NSLinkEditError(NSLinkEditErrors
*c
, int *errorNumber
, const char** fileName
, const char** errorString
)
482 log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c
, errorNumber
, fileName
, errorString
);
483 *c
= NSLinkEditOtherError
;
489 bool NSAddLibrary(const char* pathName
)
491 log_apis("NSAddLibrary(%s)\n", pathName
);
493 return ( dlopen(pathName
, 0) != nullptr);
496 bool NSAddLibraryWithSearching(const char* pathName
)
498 log_apis("NSAddLibraryWithSearching(%s)\n", pathName
);
500 return ( dlopen(pathName
, 0) != nullptr);
503 const mach_header
* NSAddImage(const char* imageName
, uint32_t options
)
505 log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName
, options
);
507 // Note: this is a quick and dirty implementation that just uses dlopen() and ignores some option flags
508 uint32_t dloptions
= 0;
509 if ( (options
& NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
) != 0 )
510 dloptions
|= RTLD_NOLOAD
;
512 void* h
= dlopen(imageName
, dloptions
);
513 if ( h
!= nullptr ) {
514 const mach_header
* mh
;
516 parseDlHandle(h
, &mh
, &dontContinue
);
520 if ( (options
& (NSADDIMAGE_OPTION_RETURN_ON_ERROR
|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
)) == 0 ) {
521 halt("NSAddImage() image not found");
526 void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers
*handlers
)
528 halt("NSInstallLinkEditErrorHandlers() is obsolete");
531 bool _dyld_present(void)
533 log_apis("_dyld_present()\n");
538 bool _dyld_launched_prebound(void)
540 halt("_dyld_launched_prebound() is obsolete");
543 bool _dyld_all_twolevel_modules_prebound(void)
545 halt("_dyld_all_twolevel_modules_prebound() is obsolete");
548 bool _dyld_bind_fully_image_containing_address(const void* address
)
550 log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address
);
552 // in dyld3, everything is always fully bound
556 bool _dyld_image_containing_address(const void* address
)
558 log_apis("_dyld_image_containing_address(%p)\n", address
);
560 return (dyld_image_header_containing_address(address
) != nullptr);
563 void _dyld_lookup_and_bind(const char* symbolName
, void **address
, NSModule
* module)
565 log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName
, address
, module);
567 const mach_header
* foundInImageAtLoadAddress
;
568 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
569 *module = (NSModule
)foundInImageAtLoadAddress
;
577 void _dyld_lookup_and_bind_with_hint(const char* symbolName
, const char* libraryNameHint
, void** address
, NSModule
* module)
579 log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName
, libraryNameHint
, address
, module);
581 const mach_header
* foundInImageAtLoadAddress
;
582 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
583 *module = (NSModule
)foundInImageAtLoadAddress
;
592 void _dyld_lookup_and_bind_fully(const char* symbolName
, void** address
, NSModule
* module)
594 log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName
, address
, module);
596 const mach_header
* foundInImageAtLoadAddress
;
597 if ( flatFindSymbol(symbolName
, address
, &foundInImageAtLoadAddress
) ) {
598 *module = (NSModule
)foundInImageAtLoadAddress
;
606 const struct mach_header
* _dyld_get_image_header_containing_address(const void* address
)
608 log_apis("_dyld_get_image_header_containing_address(%p)\n", address
);
610 return dyld_image_header_containing_address(address
);