machoXX.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. #include <grub/file.h>
  2. #include <grub/mm.h>
  3. #include <grub/misc.h>
  4. #include <grub/i18n.h>
  5. #define min(a,b) (((a) < (b)) ? (a) : (b))
  6. static int
  7. SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
  8. {
  9. return macho->offsetXX != -1;
  10. }
  11. void
  12. SUFFIX (grub_macho_parse) (grub_macho_t macho, const char *filename)
  13. {
  14. union {
  15. struct grub_macho_lzss_header lzss;
  16. grub_macho_header_t macho;
  17. } head;
  18. /* Is there any candidate at all? */
  19. if (macho->offsetXX == -1)
  20. return;
  21. /* Read header and check magic. */
  22. if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
  23. || grub_file_read (macho->file, &head, sizeof (head))
  24. != sizeof (head))
  25. {
  26. if (!grub_errno)
  27. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  28. filename);
  29. macho->offsetXX = -1;
  30. return;
  31. }
  32. if (grub_memcmp (head.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
  33. sizeof (head.lzss.magic)) == 0)
  34. {
  35. macho->compressed_sizeXX = grub_be_to_cpu32 (head.lzss.compressed_size);
  36. macho->uncompressed_sizeXX
  37. = grub_be_to_cpu32 (head.lzss.uncompressed_size);
  38. if (macho->uncompressed_sizeXX < sizeof (head.macho))
  39. {
  40. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  41. filename);
  42. macho->offsetXX = -1;
  43. return;
  44. }
  45. /* Skip header check. */
  46. macho->compressedXX = 1;
  47. return;
  48. }
  49. if (head.macho.magic != GRUB_MACHO_MAGIC)
  50. {
  51. grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O header");
  52. macho->offsetXX = -1;
  53. return;
  54. }
  55. /* Read commands. */
  56. macho->ncmdsXX = head.macho.ncmds;
  57. macho->cmdsizeXX = head.macho.sizeofcmds;
  58. macho->cmdsXX = grub_malloc (macho->cmdsizeXX);
  59. if (! macho->cmdsXX)
  60. return;
  61. if (grub_file_seek (macho->file, macho->offsetXX
  62. + sizeof (grub_macho_header_t)) == (grub_off_t) -1
  63. || grub_file_read (macho->file, macho->cmdsXX,
  64. (grub_size_t) macho->cmdsizeXX)
  65. != (grub_ssize_t) macho->cmdsizeXX)
  66. {
  67. if (!grub_errno)
  68. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  69. filename);
  70. macho->offsetXX = -1;
  71. }
  72. }
  73. typedef int (*grub_macho_iter_hook_t)
  74. (grub_macho_t , struct grub_macho_cmd *,
  75. void *);
  76. static grub_err_t
  77. grub_macho_cmds_iterate (grub_macho_t macho,
  78. grub_macho_iter_hook_t hook,
  79. void *hook_arg,
  80. const char *filename)
  81. {
  82. grub_uint8_t *hdrs;
  83. int i;
  84. if (macho->compressedXX && !macho->uncompressedXX)
  85. {
  86. grub_uint8_t *tmp;
  87. grub_macho_header_t *head;
  88. macho->uncompressedXX = grub_malloc (macho->uncompressed_sizeXX);
  89. if (!macho->uncompressedXX)
  90. return grub_errno;
  91. tmp = grub_malloc (macho->compressed_sizeXX);
  92. if (!tmp)
  93. {
  94. grub_free (macho->uncompressedXX);
  95. macho->uncompressedXX = 0;
  96. return grub_errno;
  97. }
  98. if (grub_file_seek (macho->file, macho->offsetXX
  99. + GRUB_MACHO_LZSS_OFFSET) == (grub_off_t) -1
  100. || grub_file_read (macho->file, tmp,
  101. (grub_size_t) macho->compressed_sizeXX)
  102. != (grub_ssize_t) macho->compressed_sizeXX)
  103. {
  104. if (!grub_errno)
  105. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  106. filename);
  107. grub_free (tmp);
  108. grub_free (macho->uncompressedXX);
  109. macho->uncompressedXX = 0;
  110. macho->offsetXX = -1;
  111. return grub_errno;
  112. }
  113. if (grub_decompress_lzss (macho->uncompressedXX,
  114. macho->uncompressedXX
  115. + macho->uncompressed_sizeXX,
  116. tmp, tmp + macho->compressed_sizeXX)
  117. != macho->uncompressed_sizeXX)
  118. {
  119. if (!grub_errno)
  120. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  121. filename);
  122. grub_free (tmp);
  123. grub_free (macho->uncompressedXX);
  124. macho->uncompressedXX = 0;
  125. macho->offsetXX = -1;
  126. return grub_errno;
  127. }
  128. grub_free (tmp);
  129. head = (grub_macho_header_t *) macho->uncompressedXX;
  130. macho->ncmdsXX = head->ncmds;
  131. macho->cmdsizeXX = head->sizeofcmds;
  132. macho->cmdsXX = macho->uncompressedXX + sizeof (grub_macho_header_t);
  133. if (sizeof (grub_macho_header_t) + macho->cmdsizeXX
  134. >= macho->uncompressed_sizeXX)
  135. {
  136. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  137. filename);
  138. grub_free (macho->uncompressedXX);
  139. macho->uncompressedXX = 0;
  140. macho->offsetXX = -1;
  141. return grub_errno;
  142. }
  143. }
  144. if (! macho->cmdsXX)
  145. return grub_error (GRUB_ERR_BAD_OS, "couldn't find Mach-O commands");
  146. hdrs = macho->cmdsXX;
  147. for (i = 0; i < macho->ncmdsXX; i++)
  148. {
  149. struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
  150. if (hook (macho, hdr, hook_arg))
  151. break;
  152. hdrs += hdr->cmdsize;
  153. }
  154. return grub_errno;
  155. }
  156. grub_size_t
  157. SUFFIX (grub_macho_filesize) (grub_macho_t macho)
  158. {
  159. if (SUFFIX (grub_macho_contains_macho) (macho))
  160. return macho->endXX - macho->offsetXX;
  161. return 0;
  162. }
  163. grub_err_t
  164. SUFFIX (grub_macho_readfile) (grub_macho_t macho,
  165. const char *filename,
  166. void *dest)
  167. {
  168. grub_ssize_t read;
  169. if (! SUFFIX (grub_macho_contains_macho) (macho))
  170. return grub_error (GRUB_ERR_BAD_OS,
  171. "couldn't read architecture-specific part");
  172. if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1)
  173. return grub_errno;
  174. read = grub_file_read (macho->file, dest,
  175. macho->endXX - macho->offsetXX);
  176. if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
  177. {
  178. if (!grub_errno)
  179. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  180. filename);
  181. return grub_errno;
  182. }
  183. return GRUB_ERR_NONE;
  184. }
  185. struct calcsize_ctx
  186. {
  187. int flags;
  188. int nr_phdrs;
  189. grub_macho_addr_t *segments_start;
  190. grub_macho_addr_t *segments_end;
  191. };
  192. /* Run through the program headers to calculate the total memory size we
  193. should claim. */
  194. static int
  195. calcsize (grub_macho_t _macho __attribute__ ((unused)),
  196. struct grub_macho_cmd *hdr0,
  197. void *_arg)
  198. {
  199. grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
  200. struct calcsize_ctx *ctx = _arg;
  201. if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
  202. return 0;
  203. if (! hdr->vmsize)
  204. return 0;
  205. if (! hdr->filesize && (ctx->flags & GRUB_MACHO_NOBSS))
  206. return 0;
  207. ctx->nr_phdrs++;
  208. if (hdr->vmaddr < *ctx->segments_start)
  209. *ctx->segments_start = hdr->vmaddr;
  210. if (hdr->vmaddr + hdr->vmsize > *ctx->segments_end)
  211. *ctx->segments_end = hdr->vmaddr + hdr->vmsize;
  212. return 0;
  213. }
  214. /* Calculate the amount of memory spanned by the segments. */
  215. grub_err_t
  216. SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
  217. grub_macho_addr_t *segments_end, int flags,
  218. const char *filename)
  219. {
  220. struct calcsize_ctx ctx = {
  221. .flags = flags,
  222. .nr_phdrs = 0,
  223. .segments_start = segments_start,
  224. .segments_end = segments_end,
  225. };
  226. *segments_start = (grub_macho_addr_t) -1;
  227. *segments_end = 0;
  228. grub_macho_cmds_iterate (macho, calcsize, &ctx, filename);
  229. if (ctx.nr_phdrs == 0)
  230. return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
  231. if (*segments_end < *segments_start)
  232. /* Very bad addresses. */
  233. return grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
  234. return GRUB_ERR_NONE;
  235. }
  236. struct do_load_ctx
  237. {
  238. int flags;
  239. char *offset;
  240. const char *filename;
  241. int *darwin_version;
  242. };
  243. static int
  244. do_load(grub_macho_t _macho,
  245. struct grub_macho_cmd *hdr0,
  246. void *_arg)
  247. {
  248. grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
  249. struct do_load_ctx *ctx = _arg;
  250. if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
  251. return 0;
  252. if (! hdr->filesize && (ctx->flags & GRUB_MACHO_NOBSS))
  253. return 0;
  254. if (! hdr->vmsize)
  255. return 0;
  256. if (hdr->filesize)
  257. {
  258. grub_ssize_t read, toread = min (hdr->filesize, hdr->vmsize);
  259. if (_macho->uncompressedXX)
  260. {
  261. if (hdr->fileoff + (grub_size_t) toread
  262. > _macho->uncompressed_sizeXX)
  263. read = -1;
  264. else
  265. {
  266. read = toread;
  267. grub_memcpy (ctx->offset + hdr->vmaddr,
  268. _macho->uncompressedXX + hdr->fileoff, read);
  269. }
  270. }
  271. else
  272. {
  273. if (grub_file_seek (_macho->file, hdr->fileoff
  274. + _macho->offsetXX) == (grub_off_t) -1)
  275. return 1;
  276. read = grub_file_read (_macho->file, ctx->offset + hdr->vmaddr,
  277. toread);
  278. }
  279. if (read != toread)
  280. {
  281. /* XXX How can we free memory from `load_hook'? */
  282. if (!grub_errno)
  283. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  284. ctx->filename);
  285. return 1;
  286. }
  287. if (ctx->darwin_version)
  288. {
  289. const char *ptr = ctx->offset + hdr->vmaddr;
  290. const char *end = ptr + min (hdr->filesize, hdr->vmsize)
  291. - (sizeof ("Darwin Kernel Version ") - 1);
  292. for (; ptr < end; ptr++)
  293. if (grub_memcmp (ptr, "Darwin Kernel Version ",
  294. sizeof ("Darwin Kernel Version ") - 1) == 0)
  295. {
  296. ptr += sizeof ("Darwin Kernel Version ") - 1;
  297. *ctx->darwin_version = 0;
  298. end += (sizeof ("Darwin Kernel Version ") - 1);
  299. while (ptr < end && grub_isdigit (*ptr))
  300. *ctx->darwin_version = (*ptr++ - '0') + *ctx->darwin_version * 10;
  301. break;
  302. }
  303. }
  304. }
  305. if (hdr->filesize < hdr->vmsize)
  306. grub_memset (ctx->offset + hdr->vmaddr + hdr->filesize,
  307. 0, hdr->vmsize - hdr->filesize);
  308. return 0;
  309. }
  310. /* Load every loadable segment into memory specified by `_load_hook'. */
  311. grub_err_t
  312. SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename,
  313. char *offset, int flags, int *darwin_version)
  314. {
  315. struct do_load_ctx ctx = {
  316. .flags = flags,
  317. .offset = offset,
  318. .filename = filename,
  319. .darwin_version = darwin_version
  320. };
  321. if (darwin_version)
  322. *darwin_version = 0;
  323. grub_macho_cmds_iterate (macho, do_load, &ctx, filename);
  324. return grub_errno;
  325. }
  326. static int
  327. find_entry_point (grub_macho_t _macho __attribute__ ((unused)),
  328. struct grub_macho_cmd *hdr,
  329. void *_arg)
  330. {
  331. grub_macho_addr_t *entry_point = _arg;
  332. if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
  333. *entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
  334. return 0;
  335. }
  336. grub_macho_addr_t
  337. SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho, const char *filename)
  338. {
  339. grub_macho_addr_t entry_point = 0;
  340. grub_macho_cmds_iterate (macho, find_entry_point, &entry_point, filename);
  341. return entry_point;
  342. }