dyld-832.7.1.tar.gz
[apple/dyld.git] / testing / run-static / run-static.cpp
1
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <mach-o/loader.h>
6 #include <signal.h>
7 #include <sys/mman.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 #include <algorithm>
12
13 #include <TargetConditionals.h>
14
15 #include "ClosureFileSystemPhysical.h"
16 #include "MachOAnalyzer.h"
17 #include "MachOFile.h"
18
19 #include "../testing/test-cases/kernel-test-runner.h"
20
21 const bool isLoggingEnabled = false;
22
23 int entryFunc(const TestRunnerFunctions* funcs);
24 typedef __typeof(&entryFunc) EntryFuncTy;
25
26 TestRunnerFunctions testFuncs = {
27 .version = 1,
28 .mhs = { nullptr, nullptr, nullptr, nullptr },
29 .basePointers = { nullptr, nullptr, nullptr, nullptr },
30 .printf = &::printf,
31 .exit = &::exit,
32 .testPass = &_PASS,
33 .testFail = &_FAIL,
34 .testLog = &_LOG,
35 .testTimeout = &_TIMEOUT,
36 };
37
38 struct LoadedMachO {
39 const dyld3::MachOAnalyzer* ma = nullptr;
40 // base pointer is the same as 'ma' when the binary has __TEXT first,
41 // but will point at where we mapped __DATA if building a reverse auxKC.
42 const void* basePointer = nullptr;
43 };
44
45 LoadedMachO loadPath(const char* binaryPath) {
46 __block Diagnostics diag;
47 dyld3::closure::FileSystemPhysical fileSystem;
48 dyld3::closure::LoadedFileInfo info;
49 char realerPath[MAXPATHLEN];
50 __block bool printedError = false;
51 if (!fileSystem.loadFile(binaryPath, info, realerPath, ^(const char* format, ...) {
52 fprintf(stderr, "run-static: ");
53 va_list list;
54 va_start(list, format);
55 vfprintf(stderr, format, list);
56 va_end(list);
57 printedError = true;
58 })) {
59 if (!printedError )
60 fprintf(stderr, "run-static: %s: file not found\n", binaryPath);
61 exit(1);
62 }
63
64 const char* currentArchName = dyld3::MachOFile::currentArchName();
65 const dyld3::GradedArchs& currentArchs = dyld3::GradedArchs::forName(currentArchName);
66 __block const dyld3::MachOFile* mf = nullptr;
67 __block uint64_t sliceOffset = 0;
68 if ( dyld3::FatFile::isFatFile(info.fileContent) ) {
69 const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent;
70 ff->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType,
71 const void* sliceStart, uint64_t sliceSize, bool& stop) {
72 const dyld3::MachOFile* sliceMF = (dyld3::MachOFile*)sliceStart;
73 if ( currentArchs.grade(sliceMF->cputype, sliceMF->cpusubtype, false) != 0 ) {
74 mf = sliceMF;
75 sliceOffset = (uint64_t)mf - (uint64_t)ff;
76 stop = true;
77 return;
78 }
79 });
80
81 if ( diag.hasError() ) {
82 fprintf(stderr, "Error: %s\n", diag.errorMessage());
83 return { nullptr, nullptr };
84 }
85
86 if ( mf == nullptr ) {
87 fprintf(stderr, "Could not use binary '%s' because it does not contain a slice compatible with host '%s'\n",
88 binaryPath, currentArchName);
89 return { nullptr, nullptr };
90 }
91 } else {
92 mf = (dyld3::MachOFile*)info.fileContent;
93 if ( !mf->isMachO(diag, info.sliceLen) ) {
94 fprintf(stderr, "Could not use binary '%s' because '%s'\n", binaryPath, diag.errorMessage());
95 return { nullptr, nullptr };
96 }
97
98 if ( currentArchs.grade(mf->cputype, mf->cpusubtype, false) == 0 ) {
99 fprintf(stderr, "Could not use binary '%s' because 'incompatible arch'\n", binaryPath);
100 return { nullptr, nullptr };
101 }
102 }
103
104 if ( !mf->isFileSet() ) {
105 fprintf(stderr, "Could not use binary '%s' because 'it is not a static executable'\n", binaryPath);
106 return { nullptr, nullptr };
107 }
108
109 uint64_t mappedSize = ((dyld3::MachOAnalyzer*)mf)->mappedSize();
110 vm_address_t mappedAddr;
111 if ( ::vm_allocate(mach_task_self(), &mappedAddr, (size_t)mappedSize, VM_FLAGS_ANYWHERE) != 0 ) {
112 fprintf(stderr, "Could not use binary '%s' because 'vm allocation failure'\n", binaryPath);
113 return { nullptr, nullptr };
114 }
115
116 int fd = open(binaryPath, O_RDONLY);
117 if ( fd == 0 ) {
118 fprintf(stderr, "Could not open binary '%s' because '%s'\n", binaryPath, strerror(errno));
119 return { nullptr, nullptr };
120 }
121
122 __block uint64_t baseAddress = ~0ULL;
123 __block uint64_t textSegVMAddr = ~0ULL;
124 mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
125 baseAddress = std::min(baseAddress, info.vmAddr);
126 if ( strcmp(info.segName, "__TEXT") == 0 ) {
127 textSegVMAddr = info.vmAddr;
128 }
129 });
130
131 uint64_t loadAddress = (uint64_t)mappedAddr;
132 if ( isLoggingEnabled ) {
133 fprintf(stderr, "Mapping binary built at 0x%llx to 0x%llx\n", baseAddress, loadAddress);
134 }
135 mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) {
136 uint64_t requestedLoadAddress = info.vmAddr - baseAddress + loadAddress;
137 if ( isLoggingEnabled )
138 fprintf(stderr, "Mapping %p: %s with perms %d\n", (void*)requestedLoadAddress, info.segName, info.protections);
139 if ( info.vmSize == 0 )
140 return;
141 size_t readBytes = pread(fd, (void*)requestedLoadAddress, (uintptr_t)info.fileSize, sliceOffset + info.fileOffset);
142 if ( readBytes != info.fileSize ) {
143 fprintf(stderr, "Didn't read enough bytes\n");
144 exit(1);
145 }
146 // __DATA_CONST is read-only when we actually run live, but this test runner fixes up __DATA_CONST after this vm_protect
147 // For now just don't make __DATA_CONST read only
148 uint32_t protections = info.protections;
149 if ( !strcmp(info.segName, "__DATA_CONST") )
150 protections = VM_PROT_READ | VM_PROT_WRITE;
151 const bool setCurrentPermissions = false;
152 kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)requestedLoadAddress, (uintptr_t)info.vmSize, setCurrentPermissions, protections);
153 if ( r != KERN_SUCCESS ) {
154 diag.error("vm_protect didn't work because %d", r);
155 stop = true;
156 return;
157 }
158 });
159
160 if ( diag.hasError() ) {
161 fprintf(stderr, "Error: %s\n", diag.errorMessage());
162 return { nullptr, nullptr };
163 }
164
165 if ( textSegVMAddr != baseAddress ) {
166 // __DATA is first. ma should still point to __TEXT
167 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)(mappedAddr + textSegVMAddr - baseAddress);
168 if ( !ma->validMachOForArchAndPlatform(diag, (size_t)mappedSize, binaryPath, currentArchs, dyld3::Platform::unknown, false) ) {
169 fprintf(stderr, "Error: %s\n", diag.errorMessage());
170 exit(1);
171 }
172 return { ma, (const void*)mappedAddr };
173 }
174
175 // __TEXT is first, so ma and base address are the same
176 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mappedAddr;
177 if ( !ma->validMachOForArchAndPlatform(diag, (size_t)mappedSize, binaryPath, currentArchs, dyld3::Platform::unknown, false) ) {
178 fprintf(stderr, "Error: %s\n", diag.errorMessage());
179 exit(1);
180 }
181 return { ma, (const void*)mappedAddr };
182 }
183
184 int main(int argc, const char * argv[]) {
185 bool unsupported = false;
186 #if TARGET_OS_WATCH
187 // HACK: Watch archs are not supported right now, so just return
188 unsupported = true;
189 #endif
190 if ( unsupported ) {
191 funcs = &testFuncs;
192 PASS("Success");
193 }
194
195 if ( (argc < 2) || (argc > 5) ) {
196 fprintf(stderr, "Usage: run-static *path to static binary* [- - *path to auc kc*]\n");
197 return 1;
198 }
199
200 for (unsigned i = 1; i != argc; ++i) {
201 if ( !strcmp(argv[i], "-") )
202 continue;
203 LoadedMachO macho = loadPath(argv[i]);
204 if ( macho.ma == nullptr )
205 return 1;
206 testFuncs.mhs[i - 1] = macho.ma;
207 testFuncs.basePointers[i - 1] = macho.basePointer;
208 }
209
210 uint64_t entryOffset = 0;
211 bool usesCRT = false;
212 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)testFuncs.mhs[0];
213 if ( !ma->getEntry(entryOffset, usesCRT) ) {
214 fprintf(stderr, "Could not use binary '%s' because 'no entry defined'\n", argv[1]);
215 return 1;
216 }
217
218 EntryFuncTy entryFunc = (EntryFuncTy)((uint8_t*)testFuncs.mhs[0] + entryOffset);
219 #if __has_feature(ptrauth_calls)
220 entryFunc = (EntryFuncTy)__builtin_ptrauth_sign_unauthenticated((void*)entryFunc, 0, 0);
221 #endif
222 fprintf(stderr, "Entering static binary at %p\n", entryFunc);
223 //kill(getpid(), SIGSTOP);
224 int returnCode = entryFunc(&testFuncs);
225 if ( returnCode != 0 ) {
226 fprintf(stderr, "Binary '%s' returned non-zero value %d\n", argv[1], returnCode);
227 return returnCode;
228 }
229 return 0;
230 }