Chameleon

Chameleon Svn Source Tree

Root/branches/xZenu/src/modules/ModuleSystem/macho.c

Source at commit 1406 created 12 years 10 months ago.
By meklort, Revert drivers.c so that kexts are only loaded when OSBundleRequired is set and that value is not safe mode. Added some comments about it too.
1/*
2 * Copyright 2010 Evan Lojewski. All rights reserved.
3 *
4 */
5#ifndef CONFIG_MACHO_DEBUG
6#define CONFIG_MACHO_DEBUG 0
7#endif
8
9
10#include <stdlib.h>
11#include "modules.h"
12
13extern void start_built_in_modules();
14
15#if CONFIG_MACHO_DEBUG
16#define DBG(x...)printf(x);
17#define DBGPAUSE()getchar()
18#else
19#define DBG(x...)
20#define DBGPAUSE()
21#endif
22
23// NOTE: Global so that modules can link with this
24UInt64 textAddress = 0;
25UInt64 textSection = 0;
26
27/********************************************************************************/
28/*Macho Parser*/
29/********************************************************************************/
30
31/*
32 * Parse through a macho module. The module will be rebased and binded
33 * as specified in the macho header. If the module is sucessfuly laoded
34 * the module iinit address will be returned.
35 * NOTE; all dependecies will be loaded before this module is started
36 * NOTE: If the module is unable to load ot completeion, the modules
37 * symbols will still be available.
38 */
39void* parse_mach(void* binary,
40 int(*dylib_loader)(char*),
41 long long(*symbol_handler)(char*, long long, char),
42 void (*section_handler)(char* section, char* segment, long long offset, long long address)
43)
44{
45char is64 = false;
46void (*module_start)(void) = NULL;
47
48// Module info
49/*char* moduleName = NULL;
50 UInt32 moduleVersion = 0;
51 UInt32 moduleCompat = 0;
52 */
53// TODO convert all of the structs to a union
54struct load_command *loadCommand = NULL;
55struct dylib_command* dylibCommand = NULL;
56struct dyld_info_command* dyldInfoCommand = NULL;
57
58struct symtab_command* symtabCommand = NULL;
59struct segment_command *segCommand = NULL;
60struct segment_command_64 *segCommand64 = NULL;
61
62//struct dysymtab_command* dysymtabCommand = NULL;
63UInt32 binaryIndex = 0;
64UInt16 cmd = 0;
65
66textSection = 0;
67textAddress = 0;// reinitialize text location in case it doesn't exist;
68
69// Parse through the load commands
70if(((struct mach_header*)binary)->magic == MH_MAGIC)
71{
72is64 = false;
73binaryIndex += sizeof(struct mach_header);
74}
75else if(((struct mach_header_64*)binary)->magic == MH_MAGIC_64)
76{
77// NOTE: modules cannot be 64bit...
78is64 = true;
79binaryIndex += sizeof(struct mach_header_64);
80}
81else
82{
83verbose("Invalid mach magic 0x%X\n", ((struct mach_header*)binary)->magic);
84//getchar();
85return NULL;
86}
87
88
89
90/*if(((struct mach_header*)binary)->filetype != MH_DYLIB)
91 {
92 printf("Module is not a dylib. Unable to load.\n");
93 getchar();
94 return NULL; // Module is in the incorrect format
95 }*/
96
97while(cmd < ((struct mach_header*)binary)->ncmds)
98{
99cmd++;
100
101loadCommand = binary + binaryIndex;
102UInt32 cmdSize = loadCommand->cmdsize;
103
104
105switch ((loadCommand->cmd & 0x7FFFFFFF))
106{
107case LC_SYMTAB:
108symtabCommand = binary + binaryIndex;
109break;
110
111case LC_SEGMENT: // 32bit macho
112 {
113 segCommand = binary + binaryIndex;
114
115 UInt32 sectionIndex;
116
117 sectionIndex = sizeof(struct segment_command);
118
119 struct section *sect;
120
121 while(sectionIndex < segCommand->cmdsize)
122 {
123 sect = binary + binaryIndex + sectionIndex;
124
125 sectionIndex += sizeof(struct section);
126
127 if(section_handler) section_handler(sect->sectname, segCommand->segname, sect->offset, sect->addr);
128
129
130
131 if((strcmp("__TEXT", segCommand->segname) == 0) && (strcmp("__text", sect->sectname) == 0))
132 {
133 // __TEXT,__text found, save the offset and address for when looking for the calls.
134 textSection = sect->offset;
135 textAddress = sect->addr;
136 }
137 }
138 }
139break;
140case LC_SEGMENT_64:// 64bit macho's
141 {
142 segCommand64 = binary + binaryIndex;
143 UInt32 sectionIndex;
144
145 sectionIndex = sizeof(struct segment_command_64);
146
147 struct section_64 *sect;
148
149 while(sectionIndex < segCommand64->cmdsize)
150 {
151 sect = binary + binaryIndex + sectionIndex;
152
153 sectionIndex += sizeof(struct section_64);
154
155 if(section_handler) section_handler(sect->sectname, segCommand->segname, sect->offset, sect->addr);
156
157
158 if((strcmp("__TEXT", segCommand->segname) == 0) && (strcmp("__text", sect->sectname) == 0))
159 {
160 // __TEXT,__text found, save the offset and address for when looking for the calls.
161 textSection = sect->offset;
162 textAddress = sect->addr;
163 }
164 }
165}
166break;
167
168
169case LC_LOAD_DYLIB:
170case LC_LOAD_WEAK_DYLIB ^ LC_REQ_DYLD:
171dylibCommand = binary + binaryIndex;
172char* module = binary + binaryIndex + ((UInt32)*((UInt32*)&dylibCommand->dylib.name));
173// Possible enhancments: verify version
174// =dylibCommand->dylib.current_version;
175// =dylibCommand->dylib.compatibility_version;
176if(dylib_loader)
177{
178char* name = malloc(strlen(module) + strlen(".dylib") + 1);
179sprintf(name, "%s.dylib", module);
180
181if (!dylib_loader(name))
182{
183// NOTE: any symbols exported by dep will be replace with the void function
184free(name);
185}
186}
187
188break;
189
190case LC_ID_DYLIB:
191dylibCommand = binary + binaryIndex;
192/*moduleName =binary + binaryIndex + ((UInt32)*((UInt32*)&dylibCommand->dylib.name));
193 moduleVersion =dylibCommand->dylib.current_version;
194 moduleCompat =dylibCommand->dylib.compatibility_version;
195 */
196break;
197
198case LC_DYLD_INFO:
199//case LC_DYLD_INFO_ONLY:// compressed info, 10.6+ macho files, already handeled
200// Bind and rebase info is stored here
201dyldInfoCommand = binary + binaryIndex;
202break;
203
204case LC_DYSYMTAB:
205case LC_UUID:
206case LC_UNIXTHREAD:
207break;
208
209default:
210DBG("Unhandled loadcommand 0x%X\n", loadCommand->cmd & 0x7FFFFFFF);
211break;
212
213}
214
215binaryIndex += cmdSize;
216}
217
218// bind_macho uses the symbols, if the textAdd does not exist (Symbols.dylib, no code), addresses are static and not relative
219module_start = (void*)handle_symtable((UInt32)binary, symtabCommand, symbol_handler, is64);
220
221if(dyldInfoCommand)
222{
223// Rebase the module before binding it.
224if(dyldInfoCommand->rebase_off)rebase_macho(binary, (char*)dyldInfoCommand->rebase_off,dyldInfoCommand->rebase_size);
225// Bind all symbols.
226if(dyldInfoCommand->bind_off)bind_macho(binary, (UInt8*)dyldInfoCommand->bind_off,dyldInfoCommand->bind_size);
227if(dyldInfoCommand->weak_bind_off)bind_macho(binary, (UInt8*)dyldInfoCommand->weak_bind_off,dyldInfoCommand->weak_bind_size);
228if(dyldInfoCommand->lazy_bind_off)bind_macho(binary, (UInt8*)dyldInfoCommand->lazy_bind_off,dyldInfoCommand->lazy_bind_size);
229}
230
231return module_start;
232
233}
234
235/*
236 * parse the symbol table
237 * Lookup any undefined symbols
238 */
239
240void* handle_symtable(UInt32 base, struct symtab_command* symtabCommand, long long(*symbol_handler)(char*, long long, char), char is64)
241{
242
243void* module_start= (void*) -1;
244UInt32 symbolIndex= 0;
245char* symbolString= base + (char*)symtabCommand->stroff;
246
247if(!is64)
248{
249struct nlist* symbolEntry = (void*)base + symtabCommand->symoff;
250while(symbolIndex < symtabCommand->nsyms)
251{
252// If the symbol is exported by this module
253if(symbolEntry->n_value &&
254 symbol_handler(symbolString + symbolEntry->n_un.n_strx, textAddress ? (long long)base + symbolEntry->n_value : symbolEntry->n_value, is64) != 0xFFFFFFFF)
255{
256
257// Module start located. Start is an alias so don't register it
258module_start = (void*)(textAddress ? base + symbolEntry->n_value : symbolEntry->n_value);
259}
260
261symbolEntry++;
262symbolIndex++;// TODO remove
263}
264}
265else
266{
267/*
268struct nlist_64* symbolEntry = (void*)base + symtabCommand->symoff;
269// NOTE First entry is *not* correct, but we can ignore it (i'm getting radar:// right now, verify later)
270while(symbolIndex < symtabCommand->nsyms)
271{
272
273
274// If the symbol is exported by this module
275if(symbolEntry->n_value &&
276 symbol_handler(symbolString + symbolEntry->n_un.n_strx, textAddress ? (long long)base + symbolEntry->n_value : symbolEntry->n_value, is64) != 0xFFFFFFFF)
277{
278
279// Module start located. Start is an alias so don't register it
280module_start = textAddress ? base + symbolEntry->n_value : symbolEntry->n_value;
281}
282
283symbolEntry++;
284symbolIndex++;// TODO remove
285}
286 */
287}
288return module_start;
289}
290
291// Based on code from dylibinfo.cpp and ImageLoaderMachOCompressed.cpp
292void rebase_macho(void* base, char* rebase_stream, UInt32 size)
293{
294rebase_stream += (UInt32)base;
295
296UInt8 immediate = 0;
297UInt8 opcode = 0;
298UInt8 type = 0;
299UInt32 segmentAddress = 0;
300
301
302UInt32 tmp = 0;
303UInt32 tmp2 = 0;
304UInt8 bits = 0;
305int index = 0;
306unsigned int i = 0;
307
308while(i < size)
309{
310immediate = rebase_stream[i] & REBASE_IMMEDIATE_MASK;
311opcode = rebase_stream[i] & REBASE_OPCODE_MASK;
312
313
314switch(opcode)
315{
316case REBASE_OPCODE_DONE:
317// Rebase complete, reset vars
318immediate = 0;
319opcode = 0;
320type = 0;
321segmentAddress = 0;
322default:
323break;
324
325
326case REBASE_OPCODE_SET_TYPE_IMM:
327type = immediate;
328break;
329
330
331case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
332// Locate address to begin rebasing
333segmentAddress = 0;
334struct segment_command* segCommand = NULL; // NOTE: 32bit only
335
336unsigned int binIndex = 0;
337index = 0;
338do
339{
340segCommand = base + sizeof(struct mach_header) + binIndex;
341
342
343binIndex += segCommand->cmdsize;
344index++;
345}
346while(index <= immediate);
347
348
349segmentAddress = segCommand->fileoff;
350
351tmp = 0;
352bits = 0;
353do
354{
355tmp |= (rebase_stream[++i] & 0x7f) << bits;
356bits += 7;
357}
358while(rebase_stream[i] & 0x80);
359
360segmentAddress += tmp;
361break;
362
363
364case REBASE_OPCODE_ADD_ADDR_ULEB:
365// Add value to rebase address
366tmp = 0;
367bits = 0;
368do
369{
370tmp <<= bits;
371tmp |= rebase_stream[++i] & 0x7f;
372bits += 7;
373}
374while(rebase_stream[i] & 0x80);
375
376segmentAddress +=tmp;
377break;
378
379case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
380segmentAddress += immediate * sizeof(void*);
381break;
382
383
384case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
385index = 0;
386for (index = 0; index < immediate; ++index) {
387rebase_location(base + segmentAddress, (char*)base, type);
388segmentAddress += sizeof(void*);
389}
390break;
391
392
393case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
394tmp = 0;
395bits = 0;
396do
397{
398tmp |= (rebase_stream[++i] & 0x7f) << bits;
399bits += 7;
400}
401while(rebase_stream[i] & 0x80);
402
403index = 0;
404for (index = 0; index < tmp; ++index) {
405//DBG("\tRebasing 0x%X\n", segmentAddress);
406rebase_location(base + segmentAddress, (char*)base, type);
407segmentAddress += sizeof(void*);
408}
409break;
410
411case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
412tmp = 0;
413bits = 0;
414do
415{
416tmp |= (rebase_stream[++i] & 0x7f) << bits;
417bits += 7;
418}
419while(rebase_stream[i] & 0x80);
420
421rebase_location(base + segmentAddress, (char*)base, type);
422
423segmentAddress += tmp + sizeof(void*);
424break;
425
426case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
427tmp = 0;
428bits = 0;
429do
430{
431tmp |= (rebase_stream[++i] & 0x7f) << bits;
432bits += 7;
433}
434while(rebase_stream[i] & 0x80);
435
436
437tmp2 = 0;
438bits = 0;
439do
440{
441tmp2 |= (rebase_stream[++i] & 0x7f) << bits;
442bits += 7;
443}
444while(rebase_stream[i] & 0x80);
445
446index = 0;
447for (index = 0; index < tmp; ++index) {
448
449rebase_location(base + segmentAddress, (char*)base, type);
450
451segmentAddress += tmp2 + sizeof(void*);
452}
453break;
454}
455i++;
456}
457}
458
459inline void rebase_location(UInt32* location, char* base, int type)
460{
461switch(type)
462{
463case REBASE_TYPE_POINTER:
464case REBASE_TYPE_TEXT_ABSOLUTE32:
465*location += (UInt32)base;
466break;
467
468default:
469break;
470}
471}
472
473
474UInt32 read_uleb(UInt8* bind_stream, unsigned int* i)
475{
476 // Read in offset
477 UInt32 tmp = 0;
478 UInt8 bits = 0;
479 do
480 {
481 if(bits < sizeof(UInt32)*8) // hack
482 {
483 tmp |= (bind_stream[++(*i)] & 0x7f) << bits;
484 bits += 7;
485 }
486 else
487 {
488 ++(*i);
489 }
490 }
491 while(bind_stream[*i] & 0x80);
492 return tmp;
493}
494
495
496// Based on code from dylibinfo.cpp and ImageLoaderMachOCompressed.cpp
497// NOTE: this uses 32bit values, and not 64bit values.
498// There is a possibility that this could cause issues,
499// however the modules are 32 bits, so it shouldn't matter too much
500void bind_macho(void* base, UInt8* bind_stream, UInt32 size)
501{
502bind_stream += (UInt32)base;
503
504UInt8 immediate = 0;
505UInt8 opcode = 0;
506UInt8 type = BIND_TYPE_POINTER;
507
508UInt32 segmentAddress = 0;
509
510UInt32 address = 0;
511
512SInt32 addend = 0;
513SInt32 libraryOrdinal = 0;
514
515const char* symbolName = NULL;
516UInt8 symboFlags = 0;
517UInt32 symbolAddr = 0xFFFFFFFF;
518
519// Temperary variables
520UInt32 tmp = 0;
521UInt32 tmp2 = 0;
522UInt32 index = 0;
523unsigned int i = 0;
524
525while(i < size)
526{
527immediate = bind_stream[i] & BIND_IMMEDIATE_MASK;
528opcode = bind_stream[i] & BIND_OPCODE_MASK;
529
530
531switch(opcode)
532{
533case BIND_OPCODE_DONE:
534// reset vars
535type = BIND_TYPE_POINTER;
536segmentAddress = 0;
537address = 0;
538addend = 0;
539libraryOrdinal = 0;
540symbolAddr = 0xFFFFFFFF;
541default:
542break;
543
544case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
545libraryOrdinal = immediate;
546break;
547
548case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
549libraryOrdinal = read_uleb(bind_stream, &i);
550break;
551
552case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
553libraryOrdinal = immediate ? (SInt8)(BIND_OPCODE_MASK | immediate) : immediate;
554break;
555
556case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
557symboFlags = immediate;
558symbolName = (char*)&bind_stream[++i];
559i += strlen((char*)&bind_stream[i]);
560
561symbolAddr = lookup_all_symbols(symbolName);
562break;
563
564case BIND_OPCODE_SET_TYPE_IMM:
565type = immediate;
566break;
567
568case BIND_OPCODE_SET_ADDEND_SLEB:
569addend = read_uleb(bind_stream, &i);
570if(!(bind_stream[i-1] & 0x40)) addend *= -1;
571break;
572
573case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
574segmentAddress = 0;
575
576// Locate address
577struct segment_command* segCommand = NULL;// NOTE: 32bit only
578
579unsigned int binIndex = 0;
580index = 0;
581do
582{
583segCommand = base + sizeof(struct mach_header) + binIndex;
584binIndex += segCommand->cmdsize;
585index++;
586}
587while(index <= immediate);
588
589segmentAddress = segCommand->fileoff;
590
591segmentAddress += read_uleb(bind_stream, &i);
592break;
593
594case BIND_OPCODE_ADD_ADDR_ULEB:
595segmentAddress += read_uleb(bind_stream, &i);
596break;
597
598case BIND_OPCODE_DO_BIND:
599if(symbolAddr != 0xFFFFFFFF)
600{
601address = segmentAddress + (UInt32)base;
602
603bind_location((UInt32*)address, (char*)symbolAddr, addend, type);
604}
605else
606{
607printf("Unable to bind symbol %s\n", symbolName);
608getchar();
609}
610
611segmentAddress += sizeof(void*);
612break;
613
614case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
615// Read in offset
616tmp = read_uleb(bind_stream, &i);
617
618if(symbolAddr != 0xFFFFFFFF)
619{
620address = segmentAddress + (UInt32)base;
621
622bind_location((UInt32*)address, (char*)symbolAddr, addend, type);
623}
624else
625{
626printf("Unable to bind symbol %s\n", symbolName);
627getchar();
628}
629
630segmentAddress += tmp + sizeof(void*);
631
632
633break;
634
635case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
636if(symbolAddr != 0xFFFFFFFF)
637{
638address = segmentAddress + (UInt32)base;
639
640bind_location((UInt32*)address, (char*)symbolAddr, addend, type);
641}
642else
643{
644printf("Unable to bind symbol %s\n", symbolName);
645getchar();
646}
647segmentAddress += (immediate * sizeof(void*)) + sizeof(void*);
648
649
650break;
651
652case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
653tmp = read_uleb(bind_stream, &i);
654
655tmp2 = read_uleb(bind_stream, &i);
656
657if(symbolAddr != 0xFFFFFFFF)
658{
659for(index = 0; index < tmp; index++)
660{
661
662address = segmentAddress + (UInt32)base;
663bind_location((UInt32*)address, (char*)symbolAddr, addend, type);
664segmentAddress += tmp2 + sizeof(void*);
665}
666}
667else
668{
669printf("Unable to bind symbol %s\n", symbolName);
670getchar();
671}
672break;
673}
674i++;
675}
676}
677
678
679inline void bind_location(UInt32* location, char* value, UInt32 addend, int type)
680{
681// do actual update
682char* newValue = value + addend;
683
684switch (type) {
685case BIND_TYPE_POINTER:
686case BIND_TYPE_TEXT_ABSOLUTE32:
687break;
688
689case BIND_TYPE_TEXT_PCREL32:
690newValue -= ((UInt32)location + 4);
691
692break;
693default:
694return;
695}
696//DBG("Binding 0x%X to 0x%X (was 0x%X)\n", location, newValue, *location);
697*location = (UInt32)newValue;
698}
699
700/*
701* Locate the symbol for an already loaded function and modify the beginning of
702* the function to jump directly to the new one
703* example: replace_function("_HelloWorld_start", &replacement_start);
704*/
705int replace_function(const char* symbol, void* newAddress)
706{
707UInt32* jumpPointer = malloc(sizeof(UInt32*));
708UInt32 addr = lookup_all_symbols(symbol);
709
710char* binary = (char*)addr;
711if(addr != 0xFFFFFFFF)
712{
713//DBG("Replacing %s to point to 0x%x\n", symbol, newAddress);
714*binary++ = 0xFF;// Jump
715*binary++ = 0x25;// Long Jump
716*((UInt32*)binary) = (UInt32)jumpPointer;
717
718*jumpPointer = (UInt32)newAddress;
719return 1;
720}
721return 0;
722}
723
724/********************************************************************************/
725/*dyld / Linker Interface*/
726/********************************************************************************/
727
728void dyld_stub_binder()
729{
730printf("ERROR: dyld_stub_binder was called, should have been take care of by the linker.\n");
731getchar();
732}

Archive Download this file

Revision: 1406