machoXX.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include <grub/file.h>
  2. #include <grub/mm.h>
  3. #include <grub/misc.h>
  4. #define min(a,b) (((a) < (b)) ? (a) : (b))
  5. int
  6. SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
  7. {
  8. return macho->offsetXX != -1;
  9. }
  10. void
  11. SUFFIX (grub_macho_parse) (grub_macho_t macho)
  12. {
  13. grub_macho_header_t head;
  14. /* Is there any candidate at all? */
  15. if (macho->offsetXX == -1)
  16. return;
  17. /* Read header and check magic*/
  18. if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
  19. || grub_file_read (macho->file, &head, sizeof (head))
  20. != sizeof(head))
  21. {
  22. grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
  23. macho->offsetXX = -1;
  24. return;
  25. }
  26. if (head.magic != GRUB_MACHO_MAGIC)
  27. {
  28. grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O " XX "-bit header");
  29. macho->offsetXX = -1;
  30. return;
  31. }
  32. /* Read commands. */
  33. macho->ncmdsXX = head.ncmds;
  34. macho->cmdsizeXX = head.sizeofcmds;
  35. macho->cmdsXX = grub_malloc(macho->cmdsizeXX);
  36. if (! macho->cmdsXX)
  37. {
  38. grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read commands");
  39. return;
  40. }
  41. if (grub_file_read (macho->file, macho->cmdsXX,
  42. (grub_size_t) macho->cmdsizeXX)
  43. != (grub_ssize_t) macho->cmdsizeXX)
  44. {
  45. grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
  46. macho->offsetXX = -1;
  47. }
  48. }
  49. typedef int (*grub_macho_iter_hook_t)
  50. (grub_macho_t , struct grub_macho_cmd *,
  51. void *);
  52. static grub_err_t
  53. grub_macho_cmds_iterate (grub_macho_t macho,
  54. grub_macho_iter_hook_t hook,
  55. void *hook_arg)
  56. {
  57. grub_uint8_t *hdrs = macho->cmdsXX;
  58. int i;
  59. if (! macho->cmdsXX)
  60. return grub_error (GRUB_ERR_BAD_OS, "couldn't find " XX "-bit Mach-O");
  61. for (i = 0; i < macho->ncmdsXX; i++)
  62. {
  63. struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
  64. if (hook (macho, hdr, hook_arg))
  65. break;
  66. hdrs += hdr->cmdsize;
  67. }
  68. return grub_errno;
  69. }
  70. grub_size_t
  71. SUFFIX (grub_macho_filesize) (grub_macho_t macho)
  72. {
  73. if (SUFFIX (grub_macho_contains_macho) (macho))
  74. return macho->endXX - macho->offsetXX;
  75. return 0;
  76. }
  77. grub_err_t
  78. SUFFIX (grub_macho_readfile) (grub_macho_t macho, void *dest)
  79. {
  80. grub_ssize_t read;
  81. if (! SUFFIX (grub_macho_contains_macho) (macho))
  82. return grub_error (GRUB_ERR_BAD_OS,
  83. "couldn't read architecture-specific part");
  84. if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1)
  85. {
  86. grub_error_push ();
  87. return grub_error (GRUB_ERR_BAD_OS,
  88. "invalid offset in program header");
  89. }
  90. read = grub_file_read (macho->file, dest,
  91. macho->endXX - macho->offsetXX);
  92. if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
  93. {
  94. grub_error_push ();
  95. return grub_error (GRUB_ERR_BAD_OS,
  96. "couldn't read architecture-specific part");
  97. }
  98. return GRUB_ERR_NONE;
  99. }
  100. struct grub_macho_size_closure
  101. {
  102. grub_macho_addr_t *segments_start;
  103. grub_macho_addr_t *segments_end;
  104. int flags;
  105. int nr_phdrs;
  106. };
  107. /* Run through the program headers to calculate the total memory size we
  108. should claim. */
  109. static int
  110. grub_macho_size_calcsize (grub_macho_t _macho __attribute__ ((unused)),
  111. struct grub_macho_cmd *hdr0,
  112. void *closure)
  113. {
  114. struct grub_macho_size_closure *c = closure;
  115. grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
  116. if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
  117. return 0;
  118. if (! hdr->vmsize)
  119. return 0;
  120. if (! hdr->filesize && (c->flags & GRUB_MACHO_NOBSS))
  121. return 0;
  122. c->nr_phdrs++;
  123. if (hdr->vmaddr < *(c->segments_start))
  124. *(c->segments_start) = hdr->vmaddr;
  125. if (hdr->vmaddr + hdr->vmsize > *(c->segments_end))
  126. *(c->segments_end) = hdr->vmaddr + hdr->vmsize;
  127. return 0;
  128. }
  129. /* Calculate the amount of memory spanned by the segments. */
  130. grub_err_t
  131. SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
  132. grub_macho_addr_t *segments_end, int flags)
  133. {
  134. struct grub_macho_size_closure c;
  135. c.segments_start = segments_start;
  136. c.segments_end = segments_end;
  137. c.flags = flags;
  138. c.nr_phdrs = 0;
  139. *segments_start = (grub_macho_addr_t) -1;
  140. *segments_end = 0;
  141. grub_macho_cmds_iterate (macho, grub_macho_size_calcsize, &c);
  142. if (c.nr_phdrs == 0)
  143. return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
  144. if (*segments_end < *segments_start)
  145. /* Very bad addresses. */
  146. return grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
  147. return GRUB_ERR_NONE;
  148. }
  149. struct grub_macho_load_closure
  150. {
  151. char *offset;
  152. int flags;
  153. grub_err_t err;
  154. };
  155. static int
  156. grub_macho_load_do_load (grub_macho_t _macho,
  157. struct grub_macho_cmd *hdr0,
  158. void *closure)
  159. {
  160. struct grub_macho_load_closure *c = closure;
  161. grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
  162. if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
  163. return 0;
  164. if (! hdr->filesize && (c->flags & GRUB_MACHO_NOBSS))
  165. return 0;
  166. if (! hdr->vmsize)
  167. return 0;
  168. if (grub_file_seek (_macho->file, hdr->fileoff
  169. + _macho->offsetXX) == (grub_off_t) -1)
  170. {
  171. grub_error_push ();
  172. grub_error (GRUB_ERR_BAD_OS,
  173. "invalid offset in program header");
  174. return 1;
  175. }
  176. if (hdr->filesize)
  177. {
  178. grub_ssize_t read;
  179. read = grub_file_read (_macho->file, c->offset + hdr->vmaddr,
  180. min (hdr->filesize, hdr->vmsize));
  181. if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
  182. {
  183. /* XXX How can we free memory from `load_hook'? */
  184. grub_error_push ();
  185. c->err = grub_error (GRUB_ERR_BAD_OS,
  186. "couldn't read segment from file: "
  187. "wanted 0x%lx bytes; read 0x%lx bytes",
  188. hdr->filesize, read);
  189. return 1;
  190. }
  191. }
  192. if (hdr->filesize < hdr->vmsize)
  193. grub_memset (c->offset + hdr->vmaddr + hdr->filesize,
  194. 0, hdr->vmsize - hdr->filesize);
  195. return 0;
  196. }
  197. /* Load every loadable segment into memory specified by `_load_hook'. */
  198. grub_err_t
  199. SUFFIX (grub_macho_load) (grub_macho_t macho, char *offset, int flags)
  200. {
  201. struct grub_macho_load_closure c;
  202. c.offset = offset;
  203. c.flags = flags;
  204. c.err = 0;
  205. grub_macho_cmds_iterate (macho, grub_macho_load_do_load, &c);
  206. return c.err;
  207. }
  208. static int
  209. grub_macho_get_entry_point_hook (grub_macho_t _macho __attribute__ ((unused)),
  210. struct grub_macho_cmd *hdr,
  211. void *closure)
  212. {
  213. grub_macho_addr_t *entry_point = closure;
  214. if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
  215. *entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
  216. return 0;
  217. }
  218. grub_macho_addr_t
  219. SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho)
  220. {
  221. grub_macho_addr_t entry_point = 0;
  222. grub_macho_cmds_iterate (macho, grub_macho_get_entry_point_hook,
  223. &entry_point);
  224. return entry_point;
  225. }