source: trunk/Cocoa/F-Script Anywhere/Source/mach_inject/mach_inject.c@ 238

Last change on this file since 238 was 217, checked in by rchin, 19 years ago
  • Updated to new mach_inject with support for x86
  • Updated nib files to 10.2+ format so that future updates to the f-script framework won't break fsa
  • Adjusted some code to work with the new x86 mach_inject (sends an additional argument)
  • Updates code to work with newer defines in cctools
File size: 11.6 KB
Line 
1 /*******************************************************************************
2 mach_inject.c
3 Copyright (c) 2003-2005 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
4 Some rights reserved: <http://creativecommons.org/licenses/by/2.0/>
5
6 ***************************************************************************/
7
8#include "mach_inject.h"
9
10#include <mach-o/dyld.h>
11#include <mach-o/getsect.h>
12#include <mach/mach.h>
13#include <sys/stat.h>
14#include <sys/errno.h>
15#include <assert.h>
16#include <stdlib.h> // for malloc()
17#include <stdio.h> // for printf()
18#include <mach-o/fat.h> // for fat structure decoding
19#include <mach-o/arch.h> // to know which is local arch
20#include <fcntl.h> // for open/close
21// for mmap()
22#include <sys/types.h>
23#include <sys/mman.h>
24
25#ifndef COMPILE_TIME_ASSERT( exp )
26 #define COMPILE_TIME_ASSERT( exp ) { switch (0) { case 0: case (exp):; } }
27#endif
28#define ASSERT_CAST( CAST_TO, CAST_FROM ) \
29 COMPILE_TIME_ASSERT( sizeof(CAST_TO)==sizeof(CAST_FROM) )
30
31#if defined(__i386__)
32void* fixedUpImageFromImage (
33 const void *image,
34 unsigned long imageSize,
35 unsigned int jumpTableOffset,
36 unsigned int jumpTableSize,
37 ptrdiff_t fixUpOffset);
38#endif /* __i386__ */
39
40/*******************************************************************************
41*
42* Interface
43*
44*******************************************************************************/
45#pragma mark -
46#pragma mark (Interface)
47
48 mach_error_t
49mach_inject(
50 const mach_inject_entry threadEntry,
51 const void *paramBlock,
52 size_t paramSize,
53 pid_t targetProcess,
54 vm_size_t stackSize )
55{
56 assert( threadEntry );
57 assert( targetProcess > 0 );
58 assert( stackSize == 0 || stackSize > 1024 );
59
60 // Find the image.
61 const void *image;
62 unsigned long imageSize;
63 unsigned int jumpTableOffset;
64 unsigned int jumpTableSize;
65 mach_error_t err = machImageForPointer( threadEntry, &image, &imageSize, &jumpTableOffset, &jumpTableSize );
66
67 // Initialize stackSize to default if requested.
68 if( stackSize == 0 )
69 /** @bug
70 We only want an 8K default, fix the plop-in-the-middle code below.
71 */
72 stackSize = 16 * 1024;
73
74 // Convert PID to Mach Task ref.
75 mach_port_t remoteTask = 0;
76 if( !err ) {
77 err = task_for_pid( mach_task_self(), targetProcess, &remoteTask );
78#if defined(__i386__)
79 if (err == 5) fprintf(stderr, "Could not access task for pid %d. You probably need to add user to procmod group\n", targetProcess);
80#endif
81 }
82
83 /** @todo
84 Would be nice to just allocate one block for both the remote stack
85 *and* the remoteCode (including the parameter data block once that's
86 written.
87 */
88
89 // Allocate the remoteStack.
90 vm_address_t remoteStack = (vm_address_t)NULL;
91 if( !err )
92 err = vm_allocate( remoteTask, &remoteStack, stackSize, 1 );
93
94 // Allocate the code.
95 vm_address_t remoteCode = (vm_address_t)NULL;
96 if( !err )
97 err = vm_allocate( remoteTask, &remoteCode, imageSize, 1 );
98 if( !err ) {
99 ASSERT_CAST( pointer_t, image );
100#if defined (__ppc__) || defined (__ppc64__)
101 err = vm_write( remoteTask, remoteCode, (pointer_t) image, imageSize );
102#elif defined (__i386__)
103 // on intel, jump table use relative jump instructions (jmp), which means
104 // the offset needs to be corrected. We thus copy the image and fix the offset by hand.
105 ptrdiff_t fixUpOffset = (ptrdiff_t) (image - remoteCode);
106 void * fixedUpImage = fixedUpImageFromImage(image, imageSize, jumpTableOffset, jumpTableSize, fixUpOffset);
107 err = vm_write( remoteTask, remoteCode, (pointer_t) fixedUpImage, imageSize );
108 free(fixedUpImage);
109#endif
110 }
111
112 // Allocate the paramBlock if specified.
113 vm_address_t remoteParamBlock = (vm_address_t)NULL;
114 if( !err && paramBlock != NULL && paramSize ) {
115 err = vm_allocate( remoteTask, &remoteParamBlock, paramSize, 1 );
116 if( !err ) {
117 ASSERT_CAST( pointer_t, paramBlock );
118 err = vm_write( remoteTask, remoteParamBlock,
119 (pointer_t) paramBlock, paramSize );
120 }
121 }
122
123 // Calculate offsets.
124 ptrdiff_t threadEntryOffset, imageOffset;
125 if( !err ) {
126 //assert( (void*)threadEntry >= image && (void*)threadEntry <= (image+imageSize) );
127 ASSERT_CAST( void*, threadEntry );
128 threadEntryOffset = ((void*) threadEntry) - image;
129
130 ASSERT_CAST( void*, remoteCode );
131 imageOffset = ((void*) remoteCode) - image;
132 }
133
134 // Allocate the thread.
135 thread_act_t remoteThread;
136#if defined (__ppc__) || defined (__ppc64__)
137 if( !err ) {
138 ppc_thread_state_t remoteThreadState;
139
140 /** @bug
141 Stack math should be more sophisticated than this (ala redzone).
142 */
143 remoteStack += stackSize / 2;
144
145 bzero( &remoteThreadState, sizeof(remoteThreadState) );
146
147 ASSERT_CAST( unsigned int, remoteCode );
148 remoteThreadState.srr0 = (unsigned int) remoteCode;
149 remoteThreadState.srr0 += threadEntryOffset;
150 assert( remoteThreadState.srr0 < (remoteCode + imageSize) );
151
152 ASSERT_CAST( unsigned int, remoteStack );
153 remoteThreadState.r1 = (unsigned int) remoteStack;
154
155 ASSERT_CAST( unsigned int, imageOffset );
156 remoteThreadState.r3 = (unsigned int) imageOffset;
157
158 ASSERT_CAST( unsigned int, remoteParamBlock );
159 remoteThreadState.r4 = (unsigned int) remoteParamBlock;
160
161 ASSERT_CAST( unsigned int, paramSize );
162 remoteThreadState.r5 = (unsigned int) paramSize;
163
164 ASSERT_CAST( unsigned int, 0xDEADBEEF );
165 remoteThreadState.lr = (unsigned int) 0xDEADBEEF;
166
167#if 0
168 printf( "remoteCode start: %p\n", (void*) remoteCode );
169 printf( "remoteCode size: %ld\n", imageSize );
170 printf( "remoteCode pc: %p\n", (void*) remoteThreadState.srr0 );
171 printf( "remoteCode end: %p\n",
172 (void*) (((char*)remoteCode)+imageSize) );
173 fflush(0);
174#endif
175
176 err = thread_create_running( remoteTask, PPC_THREAD_STATE,
177 (thread_state_t) &remoteThreadState, PPC_THREAD_STATE_COUNT,
178 &remoteThread );
179 }
180#elif defined (__i386__)
181 if( !err ) {
182
183 i386_thread_state_t remoteThreadState;
184 bzero( &remoteThreadState, sizeof(remoteThreadState) );
185
186 vm_address_t dummy_thread_struct = remoteStack;
187 remoteStack += (stackSize / 2); // this is the real stack
188 // (*) increase the stack, since we're simulating a CALL instruction, which normally pushes return address on the stack
189 remoteStack -= 4;
190
191#define PARAM_COUNT 4
192#define STACK_CONTENTS_SIZE ((1+PARAM_COUNT) * sizeof(unsigned int))
193 unsigned int stackContents[1 + PARAM_COUNT]; // 1 for the return address and 1 for each param
194 // first entry is return address (see above *)
195 stackContents[0] = 0xDEADBEEF; // invalid return address.
196 // then we push function parameters one by one.
197 stackContents[1] = imageOffset;
198 stackContents[2] = remoteParamBlock;
199 stackContents[3] = paramSize;
200 // We use the remote stack we allocated as the fake thread struct. We should probably use a dedicated memory zone.
201 // We don't fill it with 0, vm_allocate did it for us
202 stackContents[4] = dummy_thread_struct;
203
204 // push stackContents
205 err = vm_write( remoteTask, remoteStack,
206 (pointer_t) stackContents, STACK_CONTENTS_SIZE);
207
208 // set remote Program Counter
209 remoteThreadState.eip = (unsigned int) (remoteCode);
210 remoteThreadState.eip += threadEntryOffset;
211
212 // set remote Stack Pointer
213 ASSERT_CAST( unsigned int, remoteStack );
214 remoteThreadState.esp = (unsigned int) remoteStack;
215
216#if 0
217 printf( "remoteCode start: %p\n", (void*) remoteCode );
218 printf( "remoteCode size: %ld\n", imageSize );
219 printf( "remoteCode pc: %p\n", (void*) remoteThreadState.eip );
220 printf( "remoteCode end: %p\n",
221 (void*) (((char*)remoteCode)+imageSize) );
222 fflush(0);
223#endif
224 // create thread and launch it
225 err = thread_create_running( remoteTask, i386_THREAD_STATE,
226 (thread_state_t) &remoteThreadState, i386_THREAD_STATE_COUNT,
227 &remoteThread );
228 }
229#else
230#error architecture not supported
231#endif
232
233 if( err ) {
234 if( remoteParamBlock )
235 vm_deallocate( remoteTask, remoteParamBlock, paramSize );
236 if( remoteCode )
237 vm_deallocate( remoteTask, remoteCode, imageSize );
238 if( remoteStack )
239 vm_deallocate( remoteTask, remoteStack, stackSize );
240 }
241
242 return err;
243}
244
245 mach_error_t
246machImageForPointer(
247 const void *pointer,
248 const void **image,
249 unsigned long *size,
250 unsigned int *jumpTableOffset,
251 unsigned int *jumpTableSize )
252{
253 assert( pointer );
254 assert( image );
255 assert( size );
256
257 unsigned long p = (unsigned long) pointer;
258
259 if (jumpTableOffset && jumpTableSize) {
260 *jumpTableOffset = 0;
261 *jumpTableSize = 0;
262 }
263
264 unsigned long imageIndex, imageCount = _dyld_image_count();
265 for( imageIndex = 0; imageIndex < imageCount; imageIndex++ ) {
266 const struct mach_header *header = _dyld_get_image_header( imageIndex );
267 const struct section *section = getsectbynamefromheader( header,
268 SEG_TEXT,
269 SECT_TEXT );
270 long start = section->addr + _dyld_get_image_vmaddr_slide( imageIndex );
271 long stop = start + section->size;
272 if( p >= start && p <= stop ) {
273 // It is truely insane we have to stat() the file system in order
274 // to discover the size of an in-memory data structure.
275 const char *imageName = _dyld_get_image_name( imageIndex );
276 assert( imageName );
277 struct stat sb;
278 if( stat( imageName, &sb ) )
279 return unix_err( errno );
280 if( image ) {
281 ASSERT_CAST( void*, header );
282 *image = (void*) header;
283 }
284 if( size ) {
285 ;//assertUInt32( st_size );
286 *size = sb.st_size;
287
288 // needed for Universal binaries. Check if file is fat and get image size from there.
289 int fd = open (imageName, O_RDONLY);
290 size_t mapSize = *size;
291 char * fileImage = mmap (NULL, mapSize, PROT_READ, MAP_FILE, fd, 0);
292
293 struct fat_header* fatHeader = (struct fat_header *)fileImage;
294 if (fatHeader->magic == OSSwapBigToHostInt32(FAT_MAGIC)) {
295 //printf("This is a fat binary\n");
296 uint32_t archCount = OSSwapBigToHostInt32(fatHeader->nfat_arch);
297
298 NXArchInfo const *localArchInfo = NXGetLocalArchInfo();
299
300 struct fat_arch* arch = (struct fat_arch *)(fileImage + sizeof(struct fat_header));
301 struct fat_arch* matchingArch = NULL;
302
303 int archIndex = 0;
304 for (archIndex = 0; archIndex < archCount; archIndex++) {
305 cpu_type_t cpuType = OSSwapBigToHostInt32(arch[archIndex].cputype);
306 cpu_subtype_t cpuSubtype = OSSwapBigToHostInt32(arch[archIndex].cpusubtype);
307
308 if (localArchInfo->cputype == cpuType) {
309 matchingArch = arch + archIndex;
310 if (localArchInfo->cpusubtype == cpuSubtype) break;
311 }
312 }
313
314 if (matchingArch) {
315 *size = OSSwapBigToHostInt32(matchingArch->size);
316 //printf ("found arch-specific size : %p\n", *size);
317 }
318 }
319
320 munmap (fileImage, mapSize);
321 close (fd);
322 }
323 if (jumpTableOffset && jumpTableSize) {
324 const struct section * jumpTableSection = getsectbynamefromheader( header,
325 "__IMPORT",
326 "__jump_table" );
327 if (jumpTableSection) {
328 *jumpTableOffset = jumpTableSection->offset;
329 *jumpTableSize = jumpTableSection->size;
330 }
331 }
332 return err_none;
333 }
334 }
335
336 return err_threadEntry_image_not_found;
337}
338
339#if defined(__i386__)
340void* fixedUpImageFromImage (
341 const void *image,
342 unsigned long imageSize,
343 unsigned int jumpTableOffset,
344 unsigned int jumpTableSize,
345 ptrdiff_t fixUpOffset)
346{
347 // first copy the full image
348 void *fixedUpImage = (void *) malloc ((size_t)imageSize);
349 bcopy(image, fixedUpImage, imageSize);
350
351 // address of jump table in copied image
352 void *jumpTable = fixedUpImage + jumpTableOffset;
353 // each JMP instruction is 5 bytes (E9 xx xx xx xx) where E9 is the opcode for JMP
354 int jumpTableCount = jumpTableSize / 5;
355
356 // skip first "E9"
357 jumpTable++;
358
359 int entry=0;
360 for (entry = 0; entry < jumpTableCount; entry++) {
361 unsigned int jmpValue = *((unsigned int *)jumpTable);
362 jmpValue += fixUpOffset;
363 *((unsigned int *)jumpTable) = jmpValue;
364 jumpTable+=5;
365 }
366
367 return fixedUpImage;
368}
369#endif /* __i386__ */
Note: See TracBrowser for help on using the repository browser.