]>
Commit | Line | Data |
---|---|---|
bc3b7c8c A |
1 | /* |
2 | * Copyright (c) 2017 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include "MachOAppCache.h" | |
25 | ||
26 | #include <list> | |
27 | ||
28 | #include <CoreFoundation/CFArray.h> | |
29 | #include <CoreFoundation/CFPropertyList.h> | |
30 | #include <CoreFoundation/CFString.h> | |
31 | ||
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 */ | |
41 | }; | |
42 | #endif | |
43 | ||
44 | namespace dyld3 { | |
45 | ||
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); | |
53 | return; | |
54 | } | |
55 | }); | |
56 | } | |
57 | ||
58 | void MachOAppCache::forEachPrelinkInfoLibrary(Diagnostics& diags, | |
59 | void (^callback)(const char* bundleName, const char* relativePath, | |
60 | const std::vector<const char*>& deps)) const { | |
61 | ||
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 ) | |
66 | return symbolName; | |
67 | ||
68 | CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8); | |
69 | char buffer[len + 1]; | |
70 | if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) { | |
71 | diags.error("Could not convert string to ASCII"); | |
72 | return (const char*)nullptr; | |
73 | } | |
74 | buffer[len] = '\0'; | |
75 | nonASCIIStrings.push_back(buffer); | |
76 | return nonASCIIStrings.back().c_str(); | |
77 | }; | |
78 | ||
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 ) | |
83 | return; | |
84 | ||
85 | CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull); | |
86 | if ( !CFReadStreamOpen(readStreamRef) ) { | |
87 | fprintf(stderr, "Could not open plist stream\n"); | |
88 | exit(1); | |
89 | } | |
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)); | |
95 | CFRelease(stringRef); | |
96 | exit(1); | |
97 | } | |
98 | assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID()); | |
99 | ||
100 | // Get the "_PrelinkInfoDictionary" array | |
101 | CFArrayRef prelinkInfoDictionaryArrayRef = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, CFSTR("_PrelinkInfoDictionary")); | |
102 | assert(CFGetTypeID(prelinkInfoDictionaryArrayRef) == CFArrayGetTypeID()); | |
103 | ||
104 | for (CFIndex i = 0; i != CFArrayGetCount(prelinkInfoDictionaryArrayRef); ++i) { | |
105 | CFDictionaryRef kextInfoDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(prelinkInfoDictionaryArrayRef, i); | |
106 | assert(CFGetTypeID(kextInfoDictionary) == CFDictionaryGetTypeID()); | |
107 | ||
108 | CFStringRef bundleIdentifierStringRef = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("CFBundleIdentifier")); | |
109 | assert(CFGetTypeID(bundleIdentifierStringRef) == CFStringGetTypeID()); | |
110 | ||
111 | const char* bundleID = getString(diags, bundleIdentifierStringRef); | |
112 | if ( bundleID == nullptr ) | |
113 | return; | |
114 | ||
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 ) | |
121 | return; | |
122 | } | |
123 | ||
124 | std::vector<const char*> dependencies; | |
125 | ||
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()); | |
131 | ||
132 | struct ApplyContext { | |
133 | Diagnostics* diagnostics; | |
134 | std::vector<const char*>* dependencies = nullptr; | |
135 | const char* (^getString)(Diagnostics& diags, CFStringRef symbolNameRef) = nullptr; | |
136 | }; | |
137 | ||
138 | CFDictionaryApplierFunction callback = [](const void *key, const void *value, void *context) { | |
139 | CFStringRef keyStringRef = (CFStringRef)key; | |
140 | assert(CFGetTypeID(keyStringRef) == CFStringGetTypeID()); | |
141 | ||
142 | ApplyContext* applyContext = (ApplyContext*)context; | |
143 | const char* depString = applyContext->getString(*applyContext->diagnostics, keyStringRef); | |
144 | if ( !depString ) | |
145 | return; | |
146 | ||
147 | applyContext->dependencies->push_back(depString); | |
148 | }; | |
149 | ||
150 | ApplyContext applyContext = { &diags, &dependencies, getString }; | |
151 | CFDictionaryApplyFunction(bundleLibrariesDictionaryRef, callback, &applyContext); | |
152 | ||
153 | if ( diags.hasError() ) | |
154 | return; | |
155 | } | |
156 | callback(bundleID, relativePath, dependencies); | |
157 | } | |
158 | ||
159 | CFRelease(plistRef); | |
160 | CFRelease(readStreamRef); | |
161 | } | |
162 | ||
163 | } // namespace dyld3 |