2N/A
2N/A#include <grub/file.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/misc.h>
2N/A
2N/A#define min(a,b) (((a) < (b)) ? (a) : (b))
2N/A
2N/Aint
2N/ASUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
2N/A{
2N/A return macho->offsetXX != -1;
2N/A}
2N/A
2N/Avoid
2N/ASUFFIX (grub_macho_parse) (grub_macho_t macho)
2N/A{
2N/A grub_macho_header_t head;
2N/A
2N/A /* Is there any candidate at all? */
2N/A if (macho->offsetXX == -1)
2N/A return;
2N/A
2N/A /* Read header and check magic*/
2N/A if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
2N/A || grub_file_read (macho->file, &head, sizeof (head))
2N/A != sizeof(head))
2N/A {
2N/A grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
2N/A macho->offsetXX = -1;
2N/A return;
2N/A }
2N/A if (head.magic != GRUB_MACHO_MAGIC)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O " XX "-bit header");
2N/A macho->offsetXX = -1;
2N/A return;
2N/A }
2N/A
2N/A /* Read commands. */
2N/A macho->ncmdsXX = head.ncmds;
2N/A macho->cmdsizeXX = head.sizeofcmds;
2N/A macho->cmdsXX = grub_malloc(macho->cmdsizeXX);
2N/A if (! macho->cmdsXX)
2N/A {
2N/A grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read commands");
2N/A return;
2N/A }
2N/A if (grub_file_read (macho->file, macho->cmdsXX,
2N/A (grub_size_t) macho->cmdsizeXX)
2N/A != (grub_ssize_t) macho->cmdsizeXX)
2N/A {
2N/A grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
2N/A macho->offsetXX = -1;
2N/A }
2N/A}
2N/A
2N/Atypedef int NESTED_FUNC_ATTR (*grub_macho_iter_hook_t)
2N/A(grub_macho_t , struct grub_macho_cmd *,
2N/A void *);
2N/A
2N/Astatic grub_err_t
2N/Agrub_macho_cmds_iterate (grub_macho_t macho,
2N/A grub_macho_iter_hook_t hook,
2N/A void *hook_arg)
2N/A{
2N/A grub_uint8_t *hdrs = macho->cmdsXX;
2N/A int i;
2N/A if (! macho->cmdsXX)
2N/A return grub_error (GRUB_ERR_BAD_OS, "couldn't find " XX "-bit Mach-O");
2N/A for (i = 0; i < macho->ncmdsXX; i++)
2N/A {
2N/A struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
2N/A if (hook (macho, hdr, hook_arg))
2N/A break;
2N/A hdrs += hdr->cmdsize;
2N/A }
2N/A
2N/A return grub_errno;
2N/A}
2N/A
2N/Agrub_size_t
2N/ASUFFIX (grub_macho_filesize) (grub_macho_t macho)
2N/A{
2N/A if (SUFFIX (grub_macho_contains_macho) (macho))
2N/A return macho->endXX - macho->offsetXX;
2N/A return 0;
2N/A}
2N/A
2N/Agrub_err_t
2N/ASUFFIX (grub_macho_readfile) (grub_macho_t macho, void *dest)
2N/A{
2N/A grub_ssize_t read;
2N/A if (! SUFFIX (grub_macho_contains_macho) (macho))
2N/A return grub_error (GRUB_ERR_BAD_OS,
2N/A "couldn't read architecture-specific part");
2N/A
2N/A if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1)
2N/A {
2N/A grub_error_push ();
2N/A return grub_error (GRUB_ERR_BAD_OS,
2N/A "invalid offset in program header");
2N/A }
2N/A
2N/A read = grub_file_read (macho->file, dest,
2N/A macho->endXX - macho->offsetXX);
2N/A if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
2N/A {
2N/A grub_error_push ();
2N/A return grub_error (GRUB_ERR_BAD_OS,
2N/A "couldn't read architecture-specific part");
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Calculate the amount of memory spanned by the segments. */
2N/Agrub_err_t
2N/ASUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
2N/A grub_macho_addr_t *segments_end, int flags)
2N/A{
2N/A int nr_phdrs = 0;
2N/A
2N/A /* Run through the program headers to calculate the total memory size we
2N/A should claim. */
2N/A auto int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho,
2N/A struct grub_macho_cmd *phdr, void *_arg);
2N/A int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho __attribute__ ((unused)),
2N/A struct grub_macho_cmd *hdr0,
2N/A void *_arg __attribute__ ((unused)))
2N/A {
2N/A grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
2N/A if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
2N/A return 0;
2N/A
2N/A if (! hdr->vmsize)
2N/A return 0;
2N/A
2N/A if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
2N/A return 0;
2N/A
2N/A nr_phdrs++;
2N/A if (hdr->vmaddr < *segments_start)
2N/A *segments_start = hdr->vmaddr;
2N/A if (hdr->vmaddr + hdr->vmsize > *segments_end)
2N/A *segments_end = hdr->vmaddr + hdr->vmsize;
2N/A return 0;
2N/A }
2N/A
2N/A *segments_start = (grub_macho_addr_t) -1;
2N/A *segments_end = 0;
2N/A
2N/A grub_macho_cmds_iterate (macho, calcsize, 0);
2N/A
2N/A if (nr_phdrs == 0)
2N/A return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
2N/A
2N/A if (*segments_end < *segments_start)
2N/A /* Very bad addresses. */
2N/A return grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Load every loadable segment into memory specified by `_load_hook'. */
2N/Agrub_err_t
2N/ASUFFIX (grub_macho_load) (grub_macho_t macho, char *offset, int flags)
2N/A{
2N/A grub_err_t err = 0;
2N/A auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
2N/A struct grub_macho_cmd *hdr0,
2N/A void *_arg __attribute__ ((unused)));
2N/A int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
2N/A struct grub_macho_cmd *hdr0,
2N/A void *_arg __attribute__ ((unused)))
2N/A {
2N/A grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
2N/A
2N/A if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
2N/A return 0;
2N/A
2N/A if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
2N/A return 0;
2N/A if (! hdr->vmsize)
2N/A return 0;
2N/A
2N/A if (grub_file_seek (_macho->file, hdr->fileoff
2N/A + _macho->offsetXX) == (grub_off_t) -1)
2N/A {
2N/A grub_error_push ();
2N/A grub_error (GRUB_ERR_BAD_OS,
2N/A "invalid offset in program header");
2N/A return 1;
2N/A }
2N/A
2N/A if (hdr->filesize)
2N/A {
2N/A grub_ssize_t read;
2N/A read = grub_file_read (_macho->file, offset + hdr->vmaddr,
2N/A min (hdr->filesize, hdr->vmsize));
2N/A if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
2N/A {
2N/A /* XXX How can we free memory from `load_hook'? */
2N/A grub_error_push ();
2N/A err=grub_error (GRUB_ERR_BAD_OS,
2N/A "couldn't read segment from file: "
2N/A "wanted 0x%lx bytes; read 0x%lx bytes",
2N/A hdr->filesize, read);
2N/A return 1;
2N/A }
2N/A }
2N/A
2N/A if (hdr->filesize < hdr->vmsize)
2N/A grub_memset (offset + hdr->vmaddr + hdr->filesize,
2N/A 0, hdr->vmsize - hdr->filesize);
2N/A return 0;
2N/A }
2N/A
2N/A grub_macho_cmds_iterate (macho, do_load, 0);
2N/A
2N/A return err;
2N/A}
2N/A
2N/Agrub_macho_addr_t
2N/ASUFFIX (grub_macho_get_entry_point) (grub_macho_t macho)
2N/A{
2N/A grub_macho_addr_t entry_point = 0;
2N/A auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho,
2N/A struct grub_macho_cmd *hdr,
2N/A void *_arg __attribute__ ((unused)));
2N/A int NESTED_FUNC_ATTR hook(grub_macho_t _macho __attribute__ ((unused)),
2N/A struct grub_macho_cmd *hdr,
2N/A void *_arg __attribute__ ((unused)))
2N/A {
2N/A if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
2N/A entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
2N/A return 0;
2N/A }
2N/A grub_macho_cmds_iterate (macho, hook, 0);
2N/A return entry_point;
2N/A}