obj_macho.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. /*
  2. * BURG - Brand-new Universal loadeR from GRUB
  3. * Copyright 2009 Bean Lee - All Rights Reserved
  4. *
  5. * BURG is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * BURG is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with BURG. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <config.h>
  19. #include <grub/types.h>
  20. #include <grub/util/obj.h>
  21. #include <grub/util/misc.h>
  22. #include <grub/macho.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #if GRUB_TARGET_SIZEOF_VOID_P == 4
  27. #define grub_target_to_host grub_target_to_host32
  28. #define grub_host_to_target grub_host_to_target32
  29. #define grub_macho_header grub_macho_header32
  30. #define grub_macho_segment grub_macho_segment32
  31. #define grub_macho_section grub_macho_section32
  32. #define grub_macho_nlist grub_macho_nlist32
  33. #define GRUB_MACHO_MAGIC GRUB_MACHO_MAGIC32
  34. #define GRUB_MACHO_CMD_SEGMENT GRUB_MACHO_CMD_SEGMENT32
  35. #elif GRUB_TARGET_SIZEOF_VOID_P == 8
  36. #define grub_target_to_host grub_target_to_host64
  37. #define grub_host_to_target grub_host_to_target64
  38. #define grub_macho_header grub_macho_header64
  39. #define grub_macho_segment grub_macho_segment64
  40. #define grub_macho_section grub_macho_section64
  41. #define grub_macho_nlist grub_macho_nlist64
  42. #define GRUB_MACHO_MAGIC GRUB_MACHO_MAGIC64
  43. #define GRUB_MACHO_CMD_SEGMENT GRUB_MACHO_CMD_SEGMENT64
  44. #endif
  45. static const char *
  46. get_symbol_name (const char *name)
  47. {
  48. if (*name == '_')
  49. name++;
  50. return grub_obj_map_symbol (name);
  51. }
  52. static void
  53. add_segments (struct grub_util_obj *obj,
  54. struct grub_util_obj_segment **segments,
  55. char *image,
  56. struct grub_macho_segment *cmd, int num_cmds)
  57. {
  58. int i, seg_idx;
  59. seg_idx = 1;
  60. for (i = 0; i < num_cmds ; i++,
  61. cmd = (struct grub_macho_segment *)
  62. (((char *) cmd) + grub_target_to_host32 (cmd->cmdsize)))
  63. {
  64. int j;
  65. struct grub_macho_section *s;
  66. if (grub_target_to_host32 (cmd->cmd) != GRUB_MACHO_CMD_SEGMENT)
  67. continue;
  68. s = (struct grub_macho_section *) (cmd + 1);
  69. for (j = 0; j < (int) grub_target_to_host32 (cmd->nsects);
  70. j++, s++, seg_idx++)
  71. {
  72. int type, jump_table, jump_pointers;
  73. if (! strcmp (s->segname, "modattr"))
  74. {
  75. grub_obj_add_attr (obj,
  76. image + grub_target_to_host32 (s->offset),
  77. grub_target_to_host (s->size));
  78. continue;
  79. }
  80. jump_table = 0;
  81. jump_pointers = 0;
  82. if (! strcmp (s->segname, "__IMPORT"))
  83. {
  84. if (! strcmp (s->sectname, "__jump_table"))
  85. jump_table = 1;
  86. else if (! strcmp (s->sectname, "__pointers"))
  87. jump_pointers = 1;
  88. else
  89. continue;
  90. }
  91. /* The sectname and segname are joined together. */
  92. if (! strcmp (s->sectname, "__picsymbolstub1__TEXT"))
  93. jump_table = 1;
  94. if ((! strcmp (s->segname, "__DATA")) &&
  95. ((! strcmp (s->sectname, "__la_symbol_ptr")) ||
  96. (! strcmp (s->sectname, "__nl_symbol_ptr"))))
  97. jump_pointers = 1;
  98. if ((jump_table) || (jump_pointers))
  99. {
  100. struct grub_util_obj_segment *p;
  101. p = xmalloc_zero (sizeof (*p));
  102. p->segment.type = GRUB_OBJ_SEG_INFO;
  103. p->segment.size = grub_target_to_host (s->size);
  104. p->segment.align = 1;
  105. p->vaddr = grub_target_to_host (s->addr);
  106. p->index = grub_target_to_host32 (s->reserved1);
  107. p->raw_size = grub_target_to_host32 (s->reserved2);
  108. p->is_jmptab = jump_table;
  109. segments[seg_idx] = p;
  110. continue;
  111. }
  112. if (! strcmp (s->segname, "__TEXT"))
  113. {
  114. if (! strcmp (s->sectname, "__eh_frame"))
  115. continue;
  116. type = ((! strcmp (s->sectname, "__text")) ?
  117. GRUB_OBJ_SEG_TEXT : GRUB_OBJ_SEG_RDATA);
  118. }
  119. else if (! strcmp (s->segname, "__DATA"))
  120. {
  121. type = ((! strcmp (s->sectname, "__bss")) ?
  122. GRUB_OBJ_SEG_BSS : GRUB_OBJ_SEG_DATA);
  123. }
  124. else
  125. type = 0;
  126. if ((type) && (s->size))
  127. {
  128. struct grub_util_obj_segment *p;
  129. p = xmalloc_zero (sizeof (*p));
  130. p->segment.type = type;
  131. p->segment.align = 1 << grub_target_to_host32 (s->align);
  132. p->segment.size = grub_target_to_host (s->size);
  133. p->vaddr = grub_target_to_host (s->addr);
  134. segments[seg_idx] = p;
  135. if (type != GRUB_OBJ_SEG_BSS)
  136. {
  137. p->raw_size = p->segment.size;
  138. p->data = xmalloc (p->raw_size);
  139. memcpy (p->data, image + grub_target_to_host32 (s->offset),
  140. p->raw_size);
  141. }
  142. grub_list_push (GRUB_AS_LIST_P (&obj->segments),
  143. GRUB_AS_LIST (p));
  144. }
  145. }
  146. }
  147. }
  148. static void
  149. add_symbols (struct grub_util_obj *obj,
  150. struct grub_util_obj_segment **segments,
  151. char *image,
  152. struct grub_macho_symtab *sym)
  153. {
  154. int i;
  155. struct grub_macho_nlist *n;
  156. char *strtab;
  157. strtab = image + grub_target_to_host32 (sym->stroff);
  158. n = (struct grub_macho_nlist *) (image +
  159. grub_target_to_host32 (sym->symoff));
  160. for (i = 0; i < (int) grub_target_to_host32 (sym->nsyms); i++, n++)
  161. {
  162. const char *name;
  163. int type;
  164. if ((n->n_sect) && (! segments[n->n_sect]))
  165. continue;
  166. name = get_symbol_name (strtab + grub_target_to_host32 (n->n_strx));
  167. if ((! (n->n_type & GRUB_MACHO_N_EXT)) &&
  168. (strcmp (name, "grub_mod_init")) &&
  169. (strcmp (name, "grub_mod_fini")))
  170. continue;
  171. type = (n->n_type & GRUB_MACHO_N_TYPE);
  172. if (type == GRUB_MACHO_N_UNDEF)
  173. grub_obj_add_csym (obj, name, grub_target_to_host (n->n_value));
  174. else if (type == GRUB_MACHO_N_SECT)
  175. {
  176. struct grub_util_obj_symbol *p;
  177. p = xmalloc_zero (sizeof (*p));
  178. p->name = xstrdup (name);
  179. p->segment = segments[n->n_sect];
  180. p->symbol.offset = (grub_target_to_host (n->n_value)
  181. - p->segment->vaddr);
  182. grub_list_push (GRUB_AS_LIST_P (&obj->symbols), GRUB_AS_LIST (p));
  183. }
  184. }
  185. }
  186. #if defined(GRUB_TARGET_I386) || defined(GRUB_TARGET_POWERPC)
  187. static struct grub_util_obj_segment *
  188. find_segment (struct grub_util_obj_segment **segments, int num_segs,
  189. grub_uint32_t vaddr)
  190. {
  191. int i;
  192. for (i = 1; i < num_segs; i++)
  193. {
  194. if ((segments[i]) &&
  195. (vaddr >= segments[i]->vaddr) &&
  196. (vaddr < segments[i]->vaddr + segments[i]->segment.size))
  197. return segments[i];
  198. }
  199. /* Try including the end address. */
  200. for (i = 1; i < num_segs; i++)
  201. {
  202. if ((segments[i]) &&
  203. (vaddr >= segments[i]->vaddr) &&
  204. (vaddr <= segments[i]->vaddr + segments[i]->segment.size))
  205. return segments[i];
  206. }
  207. grub_util_error ("can\'t locate segment for address %x", vaddr);
  208. return 0;
  209. }
  210. static grub_uint32_t
  211. handle_abs_symbol (struct grub_util_obj_reloc *p, grub_uint32_t addr,
  212. grub_uint32_t v, grub_uint32_t value,
  213. int scattered, int rel, int ext, int rt,
  214. struct grub_util_obj_segment **segments, int num_segs,
  215. struct grub_macho_nlist *symtab, char *strtab,
  216. grub_uint32_t *istab)
  217. {
  218. struct grub_macho_nlist *n;
  219. grub_uint32_t addend, d;
  220. if (rt == GRUB_OBJ_REL_TYPE_32)
  221. d = 4;
  222. else if (rt == GRUB_OBJ_REL_TYPE_16)
  223. d = 2;
  224. else if (rt == GRUB_OBJ_REL_TYPE_24)
  225. d = 0;
  226. else
  227. grub_util_error ("invalid relocation %d", rt);
  228. addend = (rel) ? -d : 0;
  229. if (! ext)
  230. {
  231. struct grub_util_obj_segment *import_seg;
  232. int o;
  233. if (scattered)
  234. import_seg = find_segment (segments, num_segs, value);
  235. else
  236. import_seg = segments[value];
  237. if (import_seg->segment.type == GRUB_OBJ_SEG_INFO)
  238. {
  239. if (! rel)
  240. grub_util_error ("This should be relative");
  241. if (! import_seg->is_jmptab)
  242. grub_util_error ("This should be jump table");
  243. if (! istab)
  244. grub_util_error ("indirect symbol table empty");
  245. o = (((p->segment->vaddr + addr + v + d
  246. - import_seg->vaddr) / import_seg->raw_size)
  247. + import_seg->index);
  248. n = symtab + grub_target_to_host32 (istab[o]);
  249. }
  250. else
  251. {
  252. if (rel)
  253. v += p->segment->vaddr + addr + d;
  254. p->symbol_segment = find_segment (segments, num_segs, v);
  255. addend = v - p->symbol_segment->vaddr;
  256. if (rel)
  257. addend -= d;
  258. n = 0;
  259. }
  260. }
  261. else
  262. {
  263. n = symtab + value;
  264. addend = v;
  265. if (rel)
  266. addend += addr;
  267. }
  268. if (n)
  269. {
  270. const char *name;
  271. name = get_symbol_name (strtab + grub_target_to_host32 (n->n_strx));
  272. p->symbol_name = xstrdup (name);
  273. }
  274. return addend;
  275. }
  276. static grub_uint32_t
  277. handle_sdiff_symbol (struct grub_util_obj_reloc *p, grub_uint32_t addr,
  278. grub_uint32_t v, grub_uint32_t value,
  279. int scattered, int rel,
  280. struct grub_util_obj *obj,
  281. struct grub_macho_scattered_relocation_info *r,
  282. struct grub_util_obj_segment **segments, int num_segs,
  283. struct grub_macho_nlist *symtab, char *strtab,
  284. grub_uint32_t *istab)
  285. {
  286. grub_uint32_t addend, info, nv;
  287. if (rel)
  288. grub_util_error ("this relocation shouldn't be relative");
  289. if (! scattered)
  290. grub_util_error ("this relocation should be scattered");
  291. info = grub_target_to_host32 (r->r_info);
  292. if (! GRUB_MACHO_SREL_SCATTERED (info))
  293. grub_util_error ("This should be scattered");
  294. if (GRUB_MACHO_SREL_TYPE (info) != GRUB_MACHO_RELOC_PAIR)
  295. grub_util_error ("This should be relocation pair");
  296. nv = grub_target_to_host32 (r->r_value);
  297. if (find_segment (segments, num_segs, nv) != p->segment)
  298. grub_util_error ("current segment not matched");
  299. v += nv;
  300. p->symbol_segment = find_segment (segments, num_segs, value);
  301. p->reloc.type |= GRUB_OBJ_REL_FLAG_REL;
  302. if (p->symbol_segment->segment.type == GRUB_OBJ_SEG_INFO)
  303. {
  304. struct grub_macho_nlist *n;
  305. struct grub_util_obj_segment *seg;
  306. const char *name;
  307. int idx;
  308. if (p->symbol_segment->is_jmptab)
  309. grub_util_error ("This should be pointer");
  310. if (v != value)
  311. grub_util_error ("Offset not matched %x %x", v, value);
  312. seg = p->symbol_segment;
  313. idx = (v - seg->vaddr) / sizeof (grub_target_addr_t);
  314. n = symtab + grub_target_to_host32 (istab[seg->index + idx]);
  315. name = strtab + grub_target_to_host32 (n->n_strx);
  316. addend = grub_obj_add_got (obj, get_symbol_name (name));
  317. addend += addr - (nv - p->segment->vaddr);
  318. p->symbol_segment = obj->got_segment;
  319. }
  320. else
  321. addend = v - p->symbol_segment->vaddr + addr - (nv - p->segment->vaddr);
  322. return addend;
  323. }
  324. #endif
  325. static void
  326. add_relocs (struct grub_util_obj *obj,
  327. struct grub_util_obj_segment **segments, int num_segs,
  328. char *image,
  329. struct grub_macho_segment *cmd, int num_cmds,
  330. struct grub_macho_symtab *sym, grub_uint32_t *istab)
  331. {
  332. int i, seg_idx;
  333. struct grub_macho_nlist *symtab;
  334. char *strtab;
  335. (void) num_segs;
  336. (void) istab;
  337. if (sym)
  338. {
  339. strtab = image + grub_target_to_host32 (sym->stroff);
  340. symtab =
  341. (struct grub_macho_nlist *) (image +
  342. grub_target_to_host32 (sym->symoff));
  343. }
  344. else
  345. {
  346. strtab = 0;
  347. symtab = 0;
  348. }
  349. seg_idx = 1;
  350. for (i = 0; i < num_cmds ; i++,
  351. cmd = (struct grub_macho_segment *)
  352. (((char *) cmd) + grub_target_to_host32 (cmd->cmdsize)))
  353. {
  354. int j;
  355. struct grub_macho_section *s;
  356. if (grub_target_to_host32 (cmd->cmd) != GRUB_MACHO_CMD_SEGMENT)
  357. continue;
  358. s = (struct grub_macho_section *) (cmd + 1);
  359. for (j = 0; j < (int) grub_target_to_host32 (cmd->nsects);
  360. j++, s++, seg_idx++)
  361. {
  362. struct grub_macho_scattered_relocation_info *r;
  363. int k;
  364. if ((! segments[seg_idx]) ||
  365. (segments[seg_idx]->segment.type == GRUB_OBJ_SEG_INFO) ||
  366. (! s->nreloc))
  367. continue;
  368. r = (struct grub_macho_scattered_relocation_info *)
  369. (image + grub_target_to_host32 (s->reloff));
  370. for (k = 0; k < (int) grub_target_to_host32 (s->nreloc); r++, k++)
  371. {
  372. grub_uint32_t addr, value, info;
  373. grub_target_addr_t addend, v;
  374. int scattered, type, len, rel, ext, rt;
  375. struct grub_util_obj_reloc *p;
  376. info = grub_target_to_host32 (r->r_info);
  377. if (GRUB_MACHO_SREL_SCATTERED (info))
  378. {
  379. scattered = 1;
  380. addr = GRUB_MACHO_SREL_ADDRESS (info);
  381. value = grub_target_to_host32 (r->r_value);
  382. type = GRUB_MACHO_SREL_TYPE (info);
  383. len = GRUB_MACHO_SREL_LENGTH (info);
  384. rel = GRUB_MACHO_SREL_PCREL (info);
  385. ext = 0;
  386. }
  387. else
  388. {
  389. struct grub_macho_relocation_info *r1;
  390. r1 = (struct grub_macho_relocation_info *) r;
  391. scattered = 0;
  392. addr = info;
  393. info = grub_target_to_host32 (r1->r_info);
  394. value = GRUB_MACHO_REL_SYMBOLNUM (info);
  395. type = GRUB_MACHO_REL_TYPE (info);
  396. len = GRUB_MACHO_REL_LENGTH (info);
  397. rel = GRUB_MACHO_REL_PCREL (info);
  398. ext = GRUB_MACHO_REL_EXTERN (info);
  399. }
  400. p = xmalloc_zero (sizeof (*p));
  401. p->segment = segments[seg_idx];
  402. addend = 0;
  403. if (len == 2)
  404. {
  405. rt = GRUB_OBJ_REL_TYPE_32;
  406. v = grub_target_to_host32 (*((grub_uint32_t *)
  407. (p->segment->data + addr)));
  408. }
  409. else if (len == 1)
  410. {
  411. rt = GRUB_OBJ_REL_TYPE_16;
  412. v = grub_target_to_host16 (*((grub_uint16_t *)
  413. (p->segment->data + addr)));
  414. }
  415. #ifdef GRUB_TARGET_X86_64
  416. else if (len == 3)
  417. {
  418. rt = GRUB_OBJ_REL_TYPE_64;
  419. v = grub_target_to_host64 (*((grub_uint64_t *)
  420. (p->segment->data + addr)));
  421. }
  422. #endif
  423. else
  424. grub_util_error ("invalid relocation length %d", len);
  425. #ifdef GRUB_TARGET_X86_64
  426. if (rel)
  427. {
  428. if (len == 1)
  429. addend = -2LL;
  430. else if (len == 2)
  431. addend = -4LL;
  432. else if (len == 3)
  433. addend = -8LL;
  434. }
  435. #endif
  436. p->reloc.type = rt;
  437. if (rel)
  438. p->reloc.type |= GRUB_OBJ_REL_FLAG_REL;
  439. switch (type)
  440. {
  441. #if defined(GRUB_TARGET_I386)
  442. case GRUB_MACHO_RELOC_VANILLA:
  443. addend = handle_abs_symbol (p, addr, v, value, scattered,
  444. rel, ext, rt, segments, num_segs,
  445. symtab, strtab, istab);
  446. break;
  447. case GRUB_MACHO_RELOC_SECTDIFF:
  448. case GRUB_MACHO_RELOC_LOCAL_SECTDIFF:
  449. r++;
  450. k++;
  451. addend = handle_sdiff_symbol (p, addr, v, value, scattered,
  452. rel, obj, r, segments, num_segs,
  453. symtab, strtab, istab);
  454. break;
  455. #elif defined(GRUB_TARGET_POWERPC)
  456. case GRUB_MACHO_RELOC_VANILLA:
  457. addend = handle_abs_symbol (p, addr, v, value, scattered,
  458. rel, ext, GRUB_OBJ_REL_TYPE_24,
  459. segments, num_segs,
  460. symtab, strtab, istab);
  461. break;
  462. case GRUB_MACHO_PPC_RELOC_SDIFF:
  463. case GRUB_MACHO_PPC_RELOC_LOCAL_SDIFF:
  464. r++;
  465. k++;
  466. addend = handle_sdiff_symbol (p, addr, v, value, scattered,
  467. rel, obj, r, segments, num_segs,
  468. symtab, strtab, istab);
  469. break;
  470. case GRUB_MACHO_PPC_RELOC_BR24:
  471. v &= 0x3fffffc;
  472. if (v & 0x2000000)
  473. v |= 0xfc000000;
  474. rt = GRUB_OBJ_REL_TYPE_24;
  475. p->reloc.type = rt | GRUB_OBJ_REL_FLAG_REL;
  476. addend = handle_abs_symbol (p, addr, v, value, scattered,
  477. rel, ext, rt, segments, num_segs,
  478. symtab, strtab, istab);
  479. break;
  480. case GRUB_MACHO_PPC_RELOC_LO16:
  481. case GRUB_MACHO_PPC_RELOC_HI16:
  482. case GRUB_MACHO_PPC_RELOC_HA16:
  483. if (len != 2)
  484. grub_util_error ("invalid length");
  485. v = grub_target_to_host16 (*((grub_uint16_t *)
  486. (p->segment->data + addr + 2)));
  487. r++;
  488. k++;
  489. info = grub_target_to_host32 (r->r_info);
  490. if ((scattered) || (GRUB_MACHO_SREL_SCATTERED (info)))
  491. grub_util_error ("this relocation shouldn't be scattered");
  492. info = grub_target_to_host32 (r->r_value);
  493. if (GRUB_MACHO_REL_TYPE (info) != GRUB_MACHO_RELOC_PAIR)
  494. grub_util_error ("This should be relocation pair");
  495. /* info field is actually r_address. */
  496. if (type == GRUB_MACHO_PPC_RELOC_LO16)
  497. {
  498. p->reloc.type = GRUB_OBJ_REL_TYPE_16;
  499. v |= grub_target_to_host32 (r->r_info) << 16;
  500. }
  501. else if (type == GRUB_MACHO_PPC_RELOC_HI16)
  502. {
  503. p->reloc.type = GRUB_OBJ_REL_TYPE_16HI;
  504. v = (v << 16) | grub_target_to_host32 (r->r_info);
  505. }
  506. else
  507. {
  508. p->reloc.type = GRUB_OBJ_REL_TYPE_16HA;
  509. v = (v << 16) | grub_target_to_host32 (r->r_info);
  510. if (v & 0x8000)
  511. v -= 0x10000;
  512. }
  513. addend = handle_abs_symbol (p, addr, v, value, scattered,
  514. rel, ext, GRUB_OBJ_REL_TYPE_24,
  515. segments, num_segs,
  516. symtab, strtab, istab);
  517. addr += 2;
  518. if (rel)
  519. addend += 2;
  520. break;
  521. case GRUB_MACHO_PPC_RELOC_LO16_SDIFF:
  522. case GRUB_MACHO_PPC_RELOC_HI16_SDIFF:
  523. case GRUB_MACHO_PPC_RELOC_HA16_SDIFF:
  524. if (len != 2)
  525. grub_util_error ("invalid length");
  526. v = grub_target_to_host16 (*((grub_uint16_t *)
  527. (p->segment->data + addr + 2)));
  528. r++;
  529. k++;
  530. info = grub_target_to_host32 (r->r_info);
  531. if (type == GRUB_MACHO_PPC_RELOC_LO16_SDIFF)
  532. {
  533. p->reloc.type = GRUB_OBJ_REL_TYPE_16;
  534. v |= GRUB_MACHO_SREL_ADDRESS (info) << 16;
  535. }
  536. else if (type == GRUB_MACHO_PPC_RELOC_HI16_SDIFF)
  537. {
  538. p->reloc.type = GRUB_OBJ_REL_TYPE_16HI;
  539. v = (v << 16) | GRUB_MACHO_SREL_ADDRESS (info);
  540. }
  541. else
  542. {
  543. p->reloc.type = GRUB_OBJ_REL_TYPE_16HA;
  544. v = (v << 16) | GRUB_MACHO_SREL_ADDRESS (info);
  545. if (v & 0x8000)
  546. v -= 0x10000;
  547. }
  548. addend = handle_sdiff_symbol (p, addr, v, value, scattered,
  549. rel, obj, r, segments, num_segs,
  550. symtab, strtab, istab);
  551. if (addr == 0x374)
  552. {
  553. p->segment->file_off = 0x1234;
  554. }
  555. addr += 2;
  556. addend += 2;
  557. break;
  558. #elif defined(GRUB_TARGET_X86_64)
  559. case GRUB_MACHO_X86_64_RELOC_UNSIGNED:
  560. case GRUB_MACHO_X86_64_RELOC_SIGNED:
  561. case GRUB_MACHO_X86_64_RELOC_BRANCH:
  562. case GRUB_MACHO_X86_64_RELOC_SIGNED1:
  563. case GRUB_MACHO_X86_64_RELOC_SIGNED2:
  564. case GRUB_MACHO_X86_64_RELOC_SIGNED4:
  565. if (scattered)
  566. grub_util_error ("This shouldn't be scattered");
  567. if (! ext)
  568. {
  569. p->symbol_segment = segments[value];
  570. if (rel)
  571. v += p->segment->vaddr + addr - addend;
  572. v -= p->symbol_segment->vaddr;
  573. }
  574. else
  575. {
  576. struct grub_macho_nlist *n;
  577. n = symtab + value;
  578. if ((n->n_type & GRUB_MACHO_N_TYPE) == GRUB_MACHO_N_SECT)
  579. {
  580. p->symbol_segment = segments[n->n_sect];
  581. addend += (grub_target_to_host (n->n_value)
  582. - p->symbol_segment->vaddr);
  583. }
  584. else
  585. {
  586. const char *name;
  587. name = strtab + grub_target_to_host32 (n->n_strx);
  588. p->symbol_name = xstrdup (get_symbol_name (name));
  589. }
  590. }
  591. addend += v;
  592. break;
  593. case GRUB_MACHO_X86_64_RELOC_GOT_LD:
  594. case GRUB_MACHO_X86_64_RELOC_GOT:
  595. {
  596. struct grub_macho_nlist *n;
  597. const char *name;
  598. if (! rel)
  599. grub_util_error ("This should be relative");
  600. if (! ext)
  601. grub_util_error ("This should be extern");
  602. n = symtab + value;
  603. name = strtab + grub_target_to_host32 (n->n_strx);
  604. addend += grub_obj_add_got (obj, get_symbol_name (name));
  605. p->symbol_segment = obj->got_segment;
  606. break;
  607. }
  608. case GRUB_MACHO_X86_64_RELOC_SUB:
  609. {
  610. struct grub_macho_nlist *n1;
  611. struct grub_macho_nlist *n2;
  612. r++;
  613. k++;
  614. info = grub_target_to_host32 (r->r_info);
  615. if ((scattered) || (GRUB_MACHO_SREL_SCATTERED (info)))
  616. grub_util_error ("This shouldn't be scattered");
  617. info = grub_target_to_host32 (r->r_value);
  618. if (GRUB_MACHO_REL_TYPE (info)
  619. != GRUB_MACHO_X86_64_RELOC_UNSIGNED)
  620. grub_util_error ("no matching relocation pair");
  621. if ((rel) || (GRUB_MACHO_REL_PCREL (info)))
  622. grub_util_error ("This shouldn't be relative");
  623. if ((! ext) || (! GRUB_MACHO_REL_EXTERN (info)))
  624. grub_util_error ("This should be extern");
  625. n1 = symtab + value;
  626. n2 = symtab + GRUB_MACHO_REL_SYMBOLNUM (info);
  627. if (((n1->n_type & GRUB_MACHO_N_TYPE) != GRUB_MACHO_N_SECT)
  628. || (n1->n_sect != seg_idx))
  629. grub_util_error ("Can\'t substract from other section");
  630. addend = (v + addr - grub_target_to_host (n1->n_value)
  631. + p->segment->vaddr);
  632. if ((n2->n_type & GRUB_MACHO_N_TYPE) == GRUB_MACHO_N_SECT)
  633. {
  634. p->symbol_segment = segments[n2->n_sect];
  635. addend += (grub_target_to_host (n2->n_value)
  636. - p->symbol_segment->vaddr);
  637. }
  638. else
  639. {
  640. const char *name;
  641. name = strtab + grub_target_to_host32 (n2->n_strx);
  642. p->symbol_name = xstrdup (get_symbol_name (name));
  643. }
  644. p->reloc.type |= GRUB_OBJ_REL_FLAG_REL;
  645. break;
  646. }
  647. #endif
  648. default:
  649. grub_util_error ("invalid mach-o relocation type %d", type);
  650. }
  651. p->reloc.offset = addr;
  652. #ifdef GRUB_TARGET_USE_ADDEND
  653. p->reloc.addend = addend;
  654. #else
  655. if (rt == GRUB_OBJ_REL_TYPE_32)
  656. *((grub_uint32_t *) (p->segment->data + addr)) =
  657. grub_host_to_target32 (addend);
  658. else if (rt == GRUB_OBJ_REL_TYPE_16)
  659. *((grub_uint16_t *) (p->segment->data + addr)) =
  660. grub_host_to_target16 (addend);
  661. else if (rt == GRUB_OBJ_REL_TYPE_64)
  662. *((grub_uint64_t *) (p->segment->data + addr)) =
  663. grub_host_to_target64 (addend);
  664. else
  665. grub_util_error ("invalid relocation type %d", rt);
  666. #endif
  667. grub_list_push (GRUB_AS_LIST_P (&obj->relocs),
  668. GRUB_AS_LIST (p));
  669. }
  670. }
  671. }
  672. }
  673. int
  674. grub_obj_import_macho (struct grub_util_obj *obj, char *image, int size)
  675. {
  676. struct grub_macho_header *head;
  677. struct grub_macho_segment *cmd;
  678. struct grub_macho_symtab *sym;
  679. struct grub_macho_dysymtab *dsym;
  680. grub_uint32_t *istab;
  681. struct grub_util_obj_segment **segments;
  682. int i, num_cmds, num_segs;
  683. head = (struct grub_macho_header *) image;
  684. if ((size < (int) sizeof (*head)) ||
  685. (grub_target_to_host32 (head->magic) != GRUB_MACHO_MAGIC))
  686. return 0;
  687. cmd = (struct grub_macho_segment *) (head + 1);
  688. num_cmds = grub_target_to_host32 (head->ncmds);
  689. num_segs = 1;
  690. sym = 0;
  691. dsym = 0;
  692. for (i = 0; i < num_cmds ; i++,
  693. cmd = (struct grub_macho_segment *)
  694. (((char *) cmd) + grub_target_to_host32 (cmd->cmdsize)))
  695. {
  696. if (grub_target_to_host32 (cmd->cmd) == GRUB_MACHO_CMD_SEGMENT)
  697. num_segs += grub_target_to_host32 (cmd->nsects);
  698. else if (grub_target_to_host32 (cmd->cmd) == GRUB_MACHO_CMD_SYMTAB)
  699. sym = (struct grub_macho_symtab *) cmd;
  700. else if (grub_target_to_host32 (cmd->cmd) == GRUB_MACHO_CMD_DYSYMTAB)
  701. dsym = (struct grub_macho_dysymtab *) cmd;
  702. }
  703. istab = (dsym) ? (grub_uint32_t *)
  704. (image + grub_target_to_host32 (dsym->indirectsymoff)) : 0;
  705. segments = xmalloc_zero (num_segs * sizeof (segments[0]));
  706. cmd = (struct grub_macho_segment *) (head + 1);
  707. add_segments (obj, segments, image, cmd, num_cmds);
  708. if (sym)
  709. add_symbols (obj, segments, image, sym);
  710. add_relocs (obj, segments, num_segs, image, cmd, num_cmds, sym, istab);
  711. for (i = 1; i < num_segs; i++)
  712. if ((segments[i]) && (segments[i]->segment.type == GRUB_OBJ_SEG_INFO))
  713. free (segments[i]);
  714. free (segments);
  715. return 1;
  716. }