1 /*
2 * <macho.c>
3 *
4 * Open Hack'Ware BIOS MACH-O executable file loader
5 *
6 * Copyright (c) 2004-2005 Jocelyn Mayer
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License V2
10 * as published by the Free Software Foundation
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include "bios.h"
25 #include "exec.h"
26
27 /* MACH-O executable loader */
28 /* FAT definitions */
29 /* CPU type definitions */
30 typedef enum cpu_type_t {
31 CPU_TYPE_ANY = -1,
32 CPU_TYPE_VAX = 1,
33 CPU_TYPE_MC680x0 = 6,
34 CPU_TYPE_I386 = 7,
35 CPU_TYPE_MIPS = 8,
36 CPU_TYPE_MC98000 = 10,
37 CPU_TYPE_HPPA = 11,
38 CPU_TYPE_ARM = 12,
39 CPU_TYPE_MC88000 = 13,
40 CPU_TYPE_SPARC = 14,
41 CPU_TYPE_I860 = 15,
42 CPU_TYPE_ALPHA = 16,
43 CPU_TYPE_POWERPC = 18,
44 } cpu_type_t;
45
46 /* Any CPU */
47 typedef enum cpu_subtype_any_t {
48 CPU_SUBTYPE_MULTIPLE = -1,
49 CPU_SUBTYPE_LITTLE_ENDIAN = 0,
50 CPU_SUBTYPE_BIG_ENDIAN = 1,
51 } cpu_subtype_any_t;
52
53 /* PowerPC */
54 typedef enum cpu_subtype_ppc_t {
55 CPU_SUBTYPE_PPC_ALL = 0,
56 CPU_SUBTYPE_PPC_601 = 1,
57 CPU_SUBTYPE_PPC_602 = 2,
58 CPU_SUBTYPE_PPC_603 = 3,
59 CPU_SUBTYPE_PPC_603e = 4,
60 CPU_SUBTYPE_PPC_603ev = 5,
61 CPU_SUBTYPE_PPC_604 = 6,
62 CPU_SUBTYPE_PPC_604e = 7,
63 CPU_SUBTYPE_PPC_620 = 8,
64 CPU_SUBTYPE_PPC_750 = 9,
65 CPU_SUBTYPE_PPC_7400 = 10,
66 CPU_SUBTYPE_PPC_7450 = 11,
67 } cpu_subtype_ppc_t;
68
69 /* Fat header definition */
70 #define FAT_MAGIC 0xCAFEBABE
71
72 typedef struct fat_head_t {
73 uint32_t magic;
74 uint32_t nfat_arch;
75 } fat_head_t;
76
77 typedef struct fat_arch_t {
78 cpu_type_t cpu_type;
79 cpu_subtype_ppc_t cpu_subtype;
80 uint32_t offset;
81 uint32_t size;
82 uint32_t align;
83 } fat_arch_t;
84
85 /* Mach-O binary definitions */
86 #define MACH_O_MAGIC 0xFEEDFACE
87
88 typedef enum filetype_t {
89 MH_OBJECT = 0x1,
90 MH_EXECUTE = 0x2,
91 MH_FVMLIB = 0x3,
92 MH_CORE = 0x4,
93 MH_PRELOAD = 0x5,
94 MH_DYLIB = 0x6,
95 MH_DYLINKER = 0x7,
96 MH_BUNDLE = 0x8,
97 } filetype_t;
98
99 enum {
100 MH_NOUNDEFS = 0x01,
101 MH_INCRLINK = 0x02,
102 MH_DYLDLINK = 0x04,
103 MH_BINDATLOAD = 0x08,
104 MH_PREBOUND = 0x10,
105 };
106
107 typedef struct mach_head_t {
108 uint32_t magic;
109 cpu_type_t cpu_type;
110 cpu_subtype_ppc_t subtype;
111 filetype_t file_type;
112 uint32_t nb_cmds;
113 uint32_t cmds_size;
114 uint32_t flags;
115 } mach_head_t;
116
117 typedef enum load_cmd_t {
118 LC_SEGMENT = 0x01,
119 LC_SYMTAB = 0x02,
120 LC_SYMSEG = 0x03,
121 LC_THREAD = 0x04,
122 LC_UNIXTHREAD = 0x05,
123 LC_LOADFVMLIB = 0x06,
124 LC_IDFVMLIB = 0x07,
125 LC_IDENT = 0x08,
126 LC_FVMFILE = 0x09,
127 LC_PREPAGE = 0x0A,
128 LC_DYSYMTAB = 0x0B,
129 LC_LOAD_DYLIB = 0x0C,
130 LC_ID_DYLIB = 0x0D,
131 LC_LOAD_DYLINKER = 0x0E,
132 LC_ID_DYLINKER = 0x0F,
133 LC_PREBOUND_DYLIB = 0x10,
134 LC_0x17 = 0x17,
135 } load_cmd_t;
136
137 typedef struct mach_load_cmd_t {
138 load_cmd_t cmd;
139 uint32_t cmd_size;
140 } mach_load_cmd_t;
141
142 typedef struct mach_string_t {
143 uint32_t offset;
144 } mach_string_t;
145
146 enum {
147 SG_HIGHVM = 0x1,
148 SG_FVMLIB = 0x2,
149 SG_NORELOC = 0x4,
150 };
151
152 typedef struct mach_segment_t {
153 unsigned char segname[16];
154 uint32_t vmaddr;
155 uint32_t vmsize;
156 uint32_t file_offset;
157 uint32_t file_size;
158 uint32_t max_prot;
159 uint32_t init_prot;
160 uint32_t nsects;
161 uint32_t flags;
162 } mach_segment_t;
163
164 enum {
165 SECTION_TYPE = 0xFF,
166 S_REGULAR = 0x0,
167 S_ZEROFILL = 0x1,
168 S_CSTRING_LITERALS = 0x2,
169 S_4BYTE_LITERALS = 0x3,
170 S_8BYTE_LITERALS = 0x4,
171 S_LITERAL_POINTERS = 0x5,
172 S_NON_LAZY_SYMBOL_POINTERS = 0x6,
173 S_LAZY_SYMBOL_POINTERS = 0x7,
174 S_SYMBOL_STUBS = 0x8,
175 S_MOD_INIT_FUNC_POINTERS = 0x9,
176 };
177
178 enum {
179 S_ATTR_PURE_INSTRUCTIONS = 0x80000000,
180 S_ATTR_SOME_INSTRUCTIONS = 0x00000400,
181 S_ATTR_EXT_RELOC = 0x00000200,
182 S_ATTR_LOC_RELOC = 0x00000100,
183 };
184
185 typedef struct mach_section_t {
186 unsigned char sectname[16];
187 unsigned char segname[16];
188 uint32_t vmaddr;
189 uint32_t size;
190 uint32_t offset;
191 uint32_t align;
192 uint32_t reloc_offset;
193 uint32_t nreloc;
194 uint32_t flags;
195 uint32_t res1;
196 uint32_t res2;
197 } mach_section_t;
198
199 typedef struct mach_symtab_t {
200 uint32_t offset;
201 uint32_t nsyms;
202 uint32_t str_offset;
203 uint32_t str_size;
204 } mach_symtab_t;
205
206 typedef struct mach_symseg_t {
207 uint32_t offset;
208 uint32_t size;
209 } mach_symseg_t;
210
211 typedef struct mach_unixth_t {
212 uint32_t flavor;
213 uint32_t count;
214 /* This is supposed to be a stack.
215 * Let's assume it's less than 1kB (arbitrary !)
216 */
217 uint32_t data[256];
218 } mach_unixth_t;
219
220 typedef struct mach_fvmlib_t {
221 uint32_t str_offset;
222 uint32_t minor_version;
223 uint32_t header_addr;
224 } mach_fvmlib_t;
225
226 typedef struct mach_fvmfile_t {
227 uint32_t str_offset;
228 uint32_t vmaddr;
229 } mach_fvmfile_t;
230
231 typedef struct mach_dysymtab_t {
232 uint32_t ilocal_syms;
233 uint32_t nlocal_syms;
234 uint32_t iext_syms;
235 uint32_t next_syms;
236 uint32_t iundef_syms;
237 uint32_t nundef_syms;
238 uint32_t toc_offset;
239 uint32_t ntoc;
240 uint32_t modtab_offset;
241 uint32_t nmodtab;
242 uint32_t extsym_offset;
243 uint32_t nextsym;
244 uint32_t indirect_offset;
245 uint32_t nindirect;
246 uint32_t ext_reloc_offset;
247 uint32_t next_reloc;
248 uint32_t local_reloc_offset;
249 uint32_t nlocal_reloc;
250 } mach_dysymtab_t;
251
252 typedef struct mach_dylib_t {
253 uint32_t str_offset;
254 uint32_t timestamp;
255 uint32_t cur_version;
256 uint32_t compat_version;
257 } mach_dylib_t;
258
259 typedef struct mach_prebound_t {
260 uint32_t str_offset;
261 uint32_t nb_modules;
262 unsigned char linked_modules[256];
263 } mach_prebound_t;
264
exec_load_macho(inode_t * file,void ** dest,void ** entry,void ** end,uint32_t loffset)265 int exec_load_macho (inode_t *file, void **dest, void **entry, void **end,
266 uint32_t loffset)
267 {
268 mach_head_t mhdr;
269 mach_load_cmd_t lcmd;
270 fat_head_t fhdr;
271 fat_arch_t fahdr;
272 void *address, *first, *last;
273 uint32_t k, j, best, offset;
274 int entry_set;
275
276 /* Probe FAT */
277 file_seek(file, loffset);
278 if (fs_read(file, &fhdr, sizeof(fat_head_t)) < 0) {
279 ERROR("Cannot load fat header...\n");
280 return -1;
281 }
282 fhdr.magic = get_be32(&fhdr.magic);
283 if (fhdr.magic != FAT_MAGIC)
284 goto macho_probe;
285 fhdr.nfat_arch = get_be32(&fhdr.nfat_arch);
286 DPRINTF("FAT file: %d archs\n", fhdr.nfat_arch);
287 /* Find the best architecture */
288 best = -1;
289 offset = 0;
290 for (k = 0; k < fhdr.nfat_arch; k++) {
291 if (fs_read(file, &fahdr, sizeof(fat_arch_t)) < 0) {
292 ERROR("Cannot load fat arch header\n");
293 return -1;
294 }
295 fahdr.cpu_type = get_be32(&fahdr.cpu_type);
296 if (fahdr.cpu_type != CPU_TYPE_POWERPC)
297 continue;
298 fahdr.cpu_subtype = get_be32(&fahdr.cpu_subtype);
299 fahdr.offset = get_be32(&fahdr.offset);
300 fahdr.size = get_be32(&fahdr.size);
301 fahdr.align = get_be32(&fahdr.align);
302 switch (fahdr.cpu_subtype) {
303 case CPU_SUBTYPE_PPC_750:
304 best = k;
305 offset = fahdr.offset;
306 goto fat_cpu_ok;
307 case CPU_SUBTYPE_PPC_ALL:
308 if (best == (uint32_t)-1) {
309 offset = fahdr.offset;
310 best = k;
311 }
312 break;
313 case CPU_SUBTYPE_PPC_603:
314 case CPU_SUBTYPE_PPC_603e:
315 case CPU_SUBTYPE_PPC_603ev:
316 case CPU_SUBTYPE_PPC_604:
317 case CPU_SUBTYPE_PPC_604e:
318 best = k;
319 offset = fahdr.offset;
320 break;
321 default:
322 break;
323 }
324 }
325 if (best == (uint32_t)-1) {
326 ERROR("No matching PPC FAT arch\n");
327 return -1;
328 }
329 DPRINTF("Use FAT arch %d at %08x %08x\n", best, offset, loffset);
330 fat_cpu_ok:
331 loffset += offset;
332
333 /* Probe macho */
334 macho_probe:
335 file_seek(file, loffset);
336 if (fs_read(file, &mhdr, sizeof(mach_head_t)) < 0) {
337 ERROR("Cannot load MACH-O header...\n");
338 return -1;
339 }
340 mhdr.magic = get_be32(&mhdr.magic);
341 if (mhdr.magic != MACH_O_MAGIC) {
342 ERROR("Not a MACH-O file\n");
343 return -2;
344 }
345 mhdr.cpu_type = get_be32(&mhdr.cpu_type);
346 mhdr.subtype = get_be32(&mhdr.subtype);
347 mhdr.file_type = get_be32(&mhdr.file_type);
348 mhdr.nb_cmds = get_be32(&mhdr.nb_cmds);
349 mhdr.cmds_size = get_be32(&mhdr.cmds_size);
350 mhdr.flags = get_be32(&mhdr.flags);
351 DPRINTF("MACHO-O file cpu %d %d file type %08x %d cmds size %08x flags "
352 "%08x\n", mhdr.cpu_type, mhdr.subtype, mhdr.file_type,
353 mhdr.nb_cmds, mhdr.cmds_size, mhdr.flags);
354 offset = sizeof(mach_head_t);
355 first = (void *)-1;
356 last = NULL;
357 entry_set = 0;
358 for (k = 0; k < mhdr.nb_cmds; k++) {
359 file_seek(file, loffset + offset);
360 if (fs_read(file, &lcmd, sizeof(mach_load_cmd_t)) < 0) {
361 ERROR("Unable to load MACH-O cmd %d\n", k);
362 return -1;
363 }
364 lcmd.cmd = get_be32(&lcmd.cmd);
365 lcmd.cmd_size = get_be32(&lcmd.cmd_size);
366 DPRINTF("Cmd %d : %08x size %08x (%08x %08x)\n", k, lcmd.cmd,
367 lcmd.cmd_size, offset, offset + loffset);
368 switch (lcmd.cmd) {
369 case LC_SEGMENT:
370 /* To be loaded for execution */
371 {
372 mach_segment_t segment;
373 mach_section_t section;
374 uint32_t pos;
375
376 pos = offset + sizeof(mach_load_cmd_t);
377 if (fs_read(file, &segment, sizeof(mach_segment_t)) < 0) {
378 ERROR("Cannot load MACH-O segment\n");
379 return -1;
380 }
381 pos += sizeof(mach_segment_t);
382 segment.vmaddr = get_be32(&segment.vmaddr);
383 segment.vmsize = get_be32(&segment.vmsize);
384 segment.file_offset = get_be32(&segment.file_offset);
385 segment.file_size = get_be32(&segment.file_size);
386 segment.max_prot = get_be32(&segment.max_prot);
387 segment.init_prot = get_be32(&segment.init_prot);
388 segment.nsects = get_be32(&segment.nsects);
389 segment.flags = get_be32(&segment.flags);
390 DPRINTF("MACH-O segment addr %08x size %08x off %08x fsize "
391 "%08x ns %d fl %08x\n", segment.vmaddr, segment.vmsize,
392 segment.file_offset, segment.file_size,
393 segment.nsects, segment.flags);
394 for (j = 0; j < segment.nsects; j++) {
395 file_seek(file, loffset + pos);
396 if (fs_read(file, §ion, sizeof(mach_section_t)) < 0) {
397 ERROR("Cannot load MACH-O section\n");
398 return -1;
399 }
400 pos += sizeof(mach_section_t);
401 section.vmaddr = get_be32(§ion.vmaddr);
402 section.size = get_be32(§ion.size);
403 section.offset = get_be32(§ion.offset);
404 section.align = get_be32(§ion.align);
405 section.reloc_offset = get_be32(§ion.reloc_offset);
406 section.nreloc = get_be32(§ion.nreloc);
407 section.flags = get_be32(§ion.flags);
408 section.res1 = get_be32(§ion.res1);
409 section.res2 = get_be32(§ion.res2);
410 DPRINTF("MACH-O section vmaddr %08x size %08x off %08x "
411 "flags %08x\n", section.vmaddr, section.size,
412 section.offset, section.flags);
413 switch (section.flags & SECTION_TYPE) {
414 case S_REGULAR:
415 case S_CSTRING_LITERALS:
416 case S_4BYTE_LITERALS:
417 case S_8BYTE_LITERALS:
418 case S_LITERAL_POINTERS:
419 case S_NON_LAZY_SYMBOL_POINTERS:
420 case S_LAZY_SYMBOL_POINTERS:
421 case S_SYMBOL_STUBS:
422 case S_MOD_INIT_FUNC_POINTERS:
423 DPRINTF("Load section of type %d from %08x to %08x"
424 " %08x\n", section.flags, section.offset,
425 section.vmaddr, section.size);
426 file_seek(file, section.offset + loffset);
427 address = (void *)section.vmaddr;
428 if (address < first && address != NULL)
429 first = address;
430 if (address + section.size > last)
431 last = address + section.size;
432 if (fs_read(file, address, section.size) < 0) {
433 ERROR("Cannot load MACH-O section %d %d...\n",
434 k, j);
435 return -1;
436 }
437 break;
438 case S_ZEROFILL:
439 DPRINTF("Fill zero section to %08x %08x\n",
440 section.vmaddr, section.size);
441 address = (void *)section.vmaddr;
442 if (address < first && address != NULL)
443 first = address;
444 if (address + section.size > last)
445 last = address + section.size;
446 memset(address, 0, section.size);
447 break;
448 default:
449 ERROR("Unknown MACH-O section type: %d\n",
450 section.flags);
451 return -1;
452 }
453 }
454 }
455 break;
456 case LC_SYMTAB:
457 /* Don't care */
458 break;
459 case LC_SYMSEG:
460 /* Don't care */
461 break;
462 case LC_UNIXTHREAD:
463 /* To be loaded for execution */
464 {
465 mach_unixth_t unixth;
466
467 if (fs_read(file, &unixth, sizeof(mach_unixth_t)) < 0) {
468 ERROR("Cannot load MACH-O UNIX thread\n");
469 return -1;
470 }
471 DPRINTF("Set entry point to %08x\n", unixth.data[0]);
472 *entry = (void *)unixth.data[0];
473 entry_set = 1;
474 }
475 break;
476 case LC_THREAD:
477 break;
478 case LC_LOADFVMLIB:
479 break;
480 case LC_IDFVMLIB:
481 break;
482 case LC_IDENT:
483 break;
484 case LC_FVMFILE:
485 break;
486 case LC_PREPAGE:
487 printf("Prepage command\n");
488 break;
489 case LC_DYSYMTAB:
490 break;
491 case LC_LOAD_DYLIB:
492 break;
493 case LC_ID_DYLIB:
494 break;
495 case LC_LOAD_DYLINKER:
496 /* To be loaded for execution */
497 break;
498 case LC_ID_DYLINKER:
499 break;
500 case LC_PREBOUND_DYLIB:
501 break;
502 case LC_0x17:
503 /* ? */
504 break;
505 default:
506 printf("unknown MACH-O command (%d %d)\n", k, lcmd.cmd);
507 return -1;
508 }
509 offset += lcmd.cmd_size;
510 }
511 *dest = first;
512 *end = last;
513 // if (entry_set == 0)
514 *entry = *dest;
515
516 return 0;
517 }
518