Chameleon

Chameleon Svn Source Tree

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

Source at commit 1247 created 7 years 11 months ago.
By meklort, Update makefiles... initial work for fat modules
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 "boot.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: 1247