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@
24 #include "MachOAppCache.h"
28 #include <CoreFoundation/CFArray.h>
29 #include <CoreFoundation/CFPropertyList.h>
30 #include <CoreFoundation/CFString.h>
32 #ifndef LC_FILESET_ENTRY
33 #define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */
34 struct fileset_entry_command
{
35 uint32_t cmd
; /* LC_FILESET_ENTRY */
36 uint32_t cmdsize
; /* includes id string */
37 uint64_t vmaddr
; /* memory address of the dylib */
38 uint64_t fileoff
; /* file offset of the dylib */
39 union lc_str entry_id
; /* contained entry id */
40 uint32_t reserved
; /* entry_id is 32-bits long, so this is the reserved padding */
46 void MachOAppCache::forEachDylib(Diagnostics
& diag
, void (^callback
)(const MachOAnalyzer
* ma
, const char* name
, bool& stop
)) const {
47 const intptr_t slide
= getSlide();
48 forEachLoadCommand(diag
, ^(const load_command
*cmd
, bool &stop
) {
49 if (cmd
->cmd
== LC_FILESET_ENTRY
) {
50 const fileset_entry_command
* app_cache_cmd
= (const fileset_entry_command
*)cmd
;
51 const char* name
= (char*)app_cache_cmd
+ app_cache_cmd
->entry_id
.offset
;
52 callback((const MachOAnalyzer
*)(app_cache_cmd
->vmaddr
+ slide
), name
, stop
);
58 void MachOAppCache::forEachPrelinkInfoLibrary(Diagnostics
& diags
,
59 void (^callback
)(const char* bundleName
, const char* relativePath
,
60 const std::vector
<const char*>& deps
)) const {
62 __block
std::list
<std::string
> nonASCIIStrings
;
63 auto getString
= ^(Diagnostics
& diags
, CFStringRef symbolNameRef
) {
64 const char* symbolName
= CFStringGetCStringPtr(symbolNameRef
, kCFStringEncodingUTF8
);
65 if ( symbolName
!= nullptr )
68 CFIndex len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef
), kCFStringEncodingUTF8
);
70 if ( !CFStringGetCString(symbolNameRef
, buffer
, len
, kCFStringEncodingUTF8
) ) {
71 diags
.error("Could not convert string to ASCII");
72 return (const char*)nullptr;
75 nonASCIIStrings
.push_back(buffer
);
76 return nonASCIIStrings
.back().c_str();
79 const uint8_t* prelinkInfoBuffer
= nullptr;
80 uint64_t prelinkInfoBufferSize
= 0;
81 prelinkInfoBuffer
= (const uint8_t*)findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize
);
82 if ( prelinkInfoBuffer
== nullptr )
85 CFReadStreamRef readStreamRef
= CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault
, prelinkInfoBuffer
, prelinkInfoBufferSize
, kCFAllocatorNull
);
86 if ( !CFReadStreamOpen(readStreamRef
) ) {
87 fprintf(stderr
, "Could not open plist stream\n");
90 CFErrorRef errorRef
= nullptr;
91 CFPropertyListRef plistRef
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, readStreamRef
, prelinkInfoBufferSize
, kCFPropertyListImmutable
, nullptr, &errorRef
);
92 if ( errorRef
!= nullptr ) {
93 CFStringRef stringRef
= CFErrorCopyFailureReason(errorRef
);
94 fprintf(stderr
, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef
, kCFStringEncodingASCII
));
98 assert(CFGetTypeID(plistRef
) == CFDictionaryGetTypeID());
100 // Get the "_PrelinkInfoDictionary" array
101 CFArrayRef prelinkInfoDictionaryArrayRef
= (CFArrayRef
)CFDictionaryGetValue((CFDictionaryRef
)plistRef
, CFSTR("_PrelinkInfoDictionary"));
102 assert(CFGetTypeID(prelinkInfoDictionaryArrayRef
) == CFArrayGetTypeID());
104 for (CFIndex i
= 0; i
!= CFArrayGetCount(prelinkInfoDictionaryArrayRef
); ++i
) {
105 CFDictionaryRef kextInfoDictionary
= (CFDictionaryRef
)CFArrayGetValueAtIndex(prelinkInfoDictionaryArrayRef
, i
);
106 assert(CFGetTypeID(kextInfoDictionary
) == CFDictionaryGetTypeID());
108 CFStringRef bundleIdentifierStringRef
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)kextInfoDictionary
, CFSTR("CFBundleIdentifier"));
109 assert(CFGetTypeID(bundleIdentifierStringRef
) == CFStringGetTypeID());
111 const char* bundleID
= getString(diags
, bundleIdentifierStringRef
);
112 if ( bundleID
== nullptr )
115 const char* relativePath
= nullptr;
116 CFStringRef relativePathStringRef
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)kextInfoDictionary
, CFSTR("_PrelinkExecutableRelativePath"));
117 if ( relativePathStringRef
!= nullptr ) {
118 assert(CFGetTypeID(relativePathStringRef
) == CFStringGetTypeID());
119 relativePath
= getString(diags
, relativePathStringRef
);
120 if ( relativePath
== nullptr )
124 std::vector
<const char*> dependencies
;
126 CFDictionaryRef bundleLibrariesDictionaryRef
= (CFDictionaryRef
)CFDictionaryGetValue((CFDictionaryRef
)kextInfoDictionary
, CFSTR("OSBundleLibraries"));
127 if (bundleLibrariesDictionaryRef
!= nullptr) {
128 // Add the libraries to the dependencies
129 // If we didn't have bundle libraries then a placeholder was added
130 assert(CFGetTypeID(bundleLibrariesDictionaryRef
) == CFDictionaryGetTypeID());
132 struct ApplyContext
{
133 Diagnostics
* diagnostics
;
134 std::vector
<const char*>* dependencies
= nullptr;
135 const char* (^getString
)(Diagnostics
& diags
, CFStringRef symbolNameRef
) = nullptr;
138 CFDictionaryApplierFunction callback
= [](const void *key
, const void *value
, void *context
) {
139 CFStringRef keyStringRef
= (CFStringRef
)key
;
140 assert(CFGetTypeID(keyStringRef
) == CFStringGetTypeID());
142 ApplyContext
* applyContext
= (ApplyContext
*)context
;
143 const char* depString
= applyContext
->getString(*applyContext
->diagnostics
, keyStringRef
);
147 applyContext
->dependencies
->push_back(depString
);
150 ApplyContext applyContext
= { &diags
, &dependencies
, getString
};
151 CFDictionaryApplyFunction(bundleLibrariesDictionaryRef
, callback
, &applyContext
);
153 if ( diags
.hasError() )
156 callback(bundleID
, relativePath
, dependencies
);
160 CFRelease(readStreamRef
);