Ejemplo n.º 1
0
error_code sys_spu_elf_get_information(u32 elf_img, vm::ptr<u32> entry, vm::ptr<s32> nseg)
{
	sysPrxForUser.warning("sys_spu_elf_get_information(elf_img=0x%x, entry=*0x%x, nseg=*0x%x)", elf_img, entry, nseg);

	// Initialize ELF loader
	vm::var<spu_elf_info> info({0});

	if (auto res = info->init(vm::cast(elf_img)))
	{
		return res;
	}

	// Reject SCE header
	if (info->sce0.se_magic == 0x53434500)
	{
		return CELL_ENOEXEC;
	}

	// Load ELF header
	vm::var<elf_ehdr<elf_be, u64>> ehdr({0});

	if (info->ldr->get_ehdr(ehdr) || ehdr->e_machine != elf_machine::spu || !ehdr->e_phnum)
	{
		return CELL_ENOEXEC;
	}

	// Load program headers
	vm::var<elf_phdr<elf_be, u64>[]> phdr(ehdr->e_phnum);

	if (info->ldr->get_phdr(phdr, ehdr->e_phnum))
	{
		return CELL_ENOEXEC;
	}

	const s32 num_segs = sys_spu_image::get_nsegs<false>(phdr);

	if (num_segs < 0)
	{
		return CELL_ENOEXEC;
	}

	*entry = static_cast<u32>(ehdr->e_entry);
	*nseg  = num_segs;
	return CELL_OK;
}
Ejemplo n.º 2
0
error_code sys_spu_elf_get_segments(u32 elf_img, vm::ptr<sys_spu_segment> segments, s32 nseg)
{
	sysPrxForUser.warning("sys_spu_elf_get_segments(elf_img=0x%x, segments=*0x%x, nseg=0x%x)", elf_img, segments, nseg);

	// Initialize ELF loader
	vm::var<spu_elf_info> info({0});

	if (auto res = info->init(vm::cast(elf_img)))
	{
		return res;
	}

	// Load ELF header
	vm::var<elf_ehdr<elf_be, u64>> ehdr({0});

	if (info->ldr->get_ehdr(ehdr) || ehdr->e_machine != elf_machine::spu || !ehdr->e_phnum)
	{
		return CELL_ENOEXEC;
	}

	// Load program headers
	vm::var<elf_phdr<elf_be, u64>[]> phdr(ehdr->e_phnum);

	if (info->ldr->get_phdr(phdr, ehdr->e_phnum))
	{
		return CELL_ENOEXEC;
	}

	const s32 num_segs = sys_spu_image::fill<false>(segments, nseg, phdr, elf_img);

	if (num_segs == -2)
	{
		return CELL_ENOMEM;
	}
	else if (num_segs < 0)
	{
		return CELL_ENOEXEC;
	}

	return CELL_OK;
}
Ejemplo n.º 3
0
int
main(int argc, char **argv)
{
    int c, i;
    int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
    int count = 0, waittime = 0;
    char *memf = NULL, *nlistf = NULL;
    struct devstat_match *matches;
    struct itimerval alarmspec;
    int num_matches = 0;
    char errbuf[_POSIX2_LINE_MAX];
    kvm_t *kd = NULL;
    long generation;
    int num_devices_specified;
    int num_selected, num_selections;
    long select_generation;
    char **specified_devices;
    devstat_select_mode select_mode;
    float f;
    int havelast = 0;

    matches = NULL;
    maxshowdevs = 3;

    while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:xz?")) != -1) {
        switch(c) {
        case 'c':
            cflag++;
            count = atoi(optarg);
            if (count < 1)
                errx(1, "count %d is < 1", count);
            break;
        case 'C':
            Cflag++;
            break;
        case 'd':
            dflag++;
            break;
        case 'h':
            hflag++;
            break;
        case 'I':
            Iflag++;
            break;
        case 'K':
            Kflag++;
            break;
        case 'M':
            memf = optarg;
            break;
        case 'n':
            nflag++;
            maxshowdevs = atoi(optarg);
            if (maxshowdevs < 0)
                errx(1, "number of devices %d is < 0",
                     maxshowdevs);
            break;
        case 'N':
            nlistf = optarg;
            break;
        case 'o':
            oflag++;
            break;
        case 't':
            tflag++;
            if (devstat_buildmatch(optarg, &matches,
                                   &num_matches) != 0)
                errx(1, "%s", devstat_errbuf);
            break;
        case 'T':
            Tflag++;
            break;
        case 'w':
            wflag++;
            f = atof(optarg);
            waittime = f * 1000;
            if (waittime < 1)
                errx(1, "wait time is < 1ms");
            break;
        case 'x':
            xflag++;
            break;
        case 'z':
            zflag++;
            break;
        default:
            usage();
            exit(1);
            break;
        }
    }

    argc -= optind;
    argv += optind;

    if (nlistf != NULL || memf != NULL) {
        kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);

        if (kd == NULL)
            errx(1, "kvm_openfiles: %s", errbuf);

        if (kvm_nlist(kd, namelist) == -1)
            errx(1, "kvm_nlist: %s", kvm_geterr(kd));
    }

    /*
     * Make sure that the userland devstat version matches the kernel
     * devstat version.  If not, exit and print a message informing
     * the user of his mistake.
     */
    if (devstat_checkversion(kd) < 0)
        errx(1, "%s", devstat_errbuf);

    /*
     * Make sure Tflag and/or Cflag are set if dflag == 0.  If dflag is
     * greater than 0, they may be 0 or non-zero.
     */
    if (dflag == 0 && xflag == 0) {
        Cflag = 1;
        Tflag = 1;
    }

    /* find out how many devices we have */
    if ((num_devices = devstat_getnumdevs(kd)) < 0)
        err(1, "can't get number of devices");

    /*
     * Figure out how many devices we should display.
     */
    if (nflag == 0) {
        if (xflag > 0)
            maxshowdevs = num_devices;
        else if (oflag > 0) {
            if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
                maxshowdevs = 5;
            else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
                maxshowdevs = 5;
            else
                maxshowdevs = 4;
        } else {
            if ((dflag > 0) && (Cflag == 0))
                maxshowdevs = 4;
            else
                maxshowdevs = 3;
        }
    }

    cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
    if (cur.dinfo == NULL)
        err(1, "calloc failed");

    last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
    if (last.dinfo == NULL)
        err(1, "calloc failed");

    /*
     * Grab all the devices.  We don't look to see if the list has
     * changed here, since it almost certainly has.  We only look for
     * errors.
     */
    if (devstat_getdevs(kd, &cur) == -1)
        errx(1, "%s", devstat_errbuf);

    num_devices = cur.dinfo->numdevs;
    generation = cur.dinfo->generation;

    /*
     * If the user specified any devices on the command line, see if
     * they are in the list of devices we have now.
     */
    specified_devices = (char **)malloc(sizeof(char *));
    if (specified_devices == NULL)
        err(1, "malloc failed");

    for (num_devices_specified = 0; *argv; ++argv) {
        if (isdigit(**argv))
            break;
        num_devices_specified++;
        specified_devices = (char **)realloc(specified_devices,
                                             sizeof(char *) *
                                             num_devices_specified);
        if (specified_devices == NULL)
            err(1, "realloc failed");

        specified_devices[num_devices_specified - 1] = *argv;

    }
    if (nflag == 0 && maxshowdevs < num_devices_specified)
        maxshowdevs = num_devices_specified;

    dev_select = NULL;

    if ((num_devices_specified == 0) && (num_matches == 0))
        select_mode = DS_SELECT_ADD;
    else
        select_mode = DS_SELECT_ONLY;

    /*
     * At this point, selectdevs will almost surely indicate that the
     * device list has changed, so we don't look for return values of 0
     * or 1.  If we get back -1, though, there is an error.
     */
    if (devstat_selectdevs(&dev_select, &num_selected,
                           &num_selections, &select_generation, generation,
                           cur.dinfo->devices, num_devices, matches,
                           num_matches, specified_devices,
                           num_devices_specified, select_mode, maxshowdevs,
                           hflag) == -1)
        errx(1, "%s", devstat_errbuf);

    /*
     * Look for the traditional wait time and count arguments.
     */
    if (*argv) {
        f = atof(*argv);
        waittime = f * 1000;

        /* Let the user know he goofed, but keep going anyway */
        if (wflag != 0)
            warnx("discarding previous wait interval, using"
                  " %g instead", waittime / 1000.0);
        wflag++;

        if (*++argv) {
            count = atoi(*argv);
            if (cflag != 0)
                warnx("discarding previous count, using %d"
                      " instead", count);
            cflag++;
        } else
            count = -1;
    }

    /*
     * If the user specified a count, but not an interval, we default
     * to an interval of 1 second.
     */
    if ((wflag == 0) && (cflag > 0))
        waittime = 1 * 1000;

    /*
     * If the user specified a wait time, but not a count, we want to
     * go on ad infinitum.  This can be redundant if the user uses the
     * traditional method of specifying the wait, since in that case we
     * already set count = -1 above.  Oh well.
     */
    if ((wflag > 0) && (cflag == 0))
        count = -1;

    bzero(cur.cp_time, sizeof(cur.cp_time));
    cur.tk_nout = 0;
    cur.tk_nin = 0;

    /*
     * Set the snap time to the system boot time (ie: zero), so the
     * stats are calculated since system boot.
     */
    cur.snap_time = 0;

    /*
     * If the user stops the program (control-Z) and then resumes it,
     * print out the header again.
     */
    (void)signal(SIGCONT, needhdr);

    /*
     * If our standard output is a tty, then install a SIGWINCH handler
     * and set wresized so that our first iteration through the main
     * iostat loop will peek at the terminal's current rows to find out
     * how many lines can fit in a screenful of output.
     */
    if (isatty(fileno(stdout)) != 0) {
        wresized = 1;
        (void)signal(SIGWINCH, needresize);
    } else {
        wresized = 0;
        wrows = IOSTAT_DEFAULT_ROWS;
    }

    /*
     * Register a SIGINT handler so that we can print out final statistics
     * when we get that signal
     */
    (void)signal(SIGINT, needreturn);

    /*
     * Register a SIGALRM handler to implement sleeps if the user uses the
     * -c or -w options
     */
    (void)signal(SIGALRM, alarm_clock);
    alarmspec.it_interval.tv_sec = waittime / 1000;
    alarmspec.it_interval.tv_usec = 1000 * (waittime % 1000);
    alarmspec.it_value.tv_sec = waittime / 1000;
    alarmspec.it_value.tv_usec = 1000 * (waittime % 1000);
    setitimer(ITIMER_REAL, &alarmspec, NULL);

    for (headercount = 1;;) {
        struct devinfo *tmp_dinfo;
        long tmp;
        long double etime;
        sigset_t sigmask, oldsigmask;

        if (Tflag > 0) {
            if ((readvar(kd, "kern.tty_nin", X_TTY_NIN, &cur.tk_nin,
                         sizeof(cur.tk_nin)) != 0)
                    || (readvar(kd, "kern.tty_nout", X_TTY_NOUT,
                                &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
                Tflag = 0;
                warnx("disabling TTY statistics");
            }
        }

        if (Cflag > 0) {
            if (kd == NULL) {
                if (readvar(kd, "kern.cp_time", 0,
                            &cur.cp_time, sizeof(cur.cp_time)) != 0)
                    Cflag = 0;
            } else {
                if (kvm_getcptime(kd, cur.cp_time) < 0) {
                    warnx("kvm_getcptime: %s",
                          kvm_geterr(kd));
                    Cflag = 0;
                }
            }
            if (Cflag == 0)
                warnx("disabling CPU time statistics");
        }

        if (!--headercount) {
            phdr();
            if (wresized != 0)
                doresize();
            headercount = wrows;
        }

        tmp_dinfo = last.dinfo;
        last.dinfo = cur.dinfo;
        cur.dinfo = tmp_dinfo;

        last.snap_time = cur.snap_time;

        /*
         * Here what we want to do is refresh our device stats.
         * devstat_getdevs() returns 1 when the device list has changed.
         * If the device list has changed, we want to go through
         * the selection process again, in case a device that we
         * were previously displaying has gone away.
         */
        switch (devstat_getdevs(kd, &cur)) {
        case -1:
            errx(1, "%s", devstat_errbuf);
            break;
        case 1: {
            int retval;

            num_devices = cur.dinfo->numdevs;
            generation = cur.dinfo->generation;
            retval = devstat_selectdevs(&dev_select, &num_selected,
                                        &num_selections,
                                        &select_generation,
                                        generation,
                                        cur.dinfo->devices,
                                        num_devices, matches,
                                        num_matches,
                                        specified_devices,
                                        num_devices_specified,
                                        select_mode, maxshowdevs,
                                        hflag);
            switch(retval) {
            case -1:
                errx(1, "%s", devstat_errbuf);
                break;
            case 1:
                phdr();
                if (wresized != 0)
                    doresize();
                headercount = wrows;
                break;
            default:
                break;
            }
            break;
        }
        default:
            break;
        }

        /*
         * We only want to re-select devices if we're in 'top'
         * mode.  This is the only mode where the devices selected
         * could actually change.
         */
        if (hflag > 0) {
            int retval;
            retval = devstat_selectdevs(&dev_select, &num_selected,
                                        &num_selections,
                                        &select_generation,
                                        generation,
                                        cur.dinfo->devices,
                                        num_devices, matches,
                                        num_matches,
                                        specified_devices,
                                        num_devices_specified,
                                        select_mode, maxshowdevs,
                                        hflag);
            switch(retval) {
            case -1:
                errx(1,"%s", devstat_errbuf);
                break;
            case 1:
                phdr();
                if (wresized != 0)
                    doresize();
                headercount = wrows;
                break;
            default:
                break;
            }
        }

        if (Tflag > 0) {
            tmp = cur.tk_nin;
            cur.tk_nin -= last.tk_nin;
            last.tk_nin = tmp;
            tmp = cur.tk_nout;
            cur.tk_nout -= last.tk_nout;
            last.tk_nout = tmp;
        }

        etime = cur.snap_time - last.snap_time;

        if (etime == 0.0)
            etime = 1.0;

        for (i = 0; i < CPUSTATES; i++) {
            tmp = cur.cp_time[i];
            cur.cp_time[i] -= last.cp_time[i];
            last.cp_time[i] = tmp;
        }

        if (xflag == 0 && Tflag > 0)
            printf("%4.0Lf %5.0Lf", cur.tk_nin / etime,
                   cur.tk_nout / etime);

        devstats(hflag, etime, havelast);

        if (xflag == 0) {
            if (Cflag > 0)
                cpustats();

            printf("\n");
        }
        fflush(stdout);

        if ((count >= 0 && --count <= 0) || return_requested)
            break;

        /*
         * Use sigsuspend to safely sleep until either signal is
         * received
         */
        alarm_rang = 0;
        sigemptyset(&sigmask);
        sigaddset(&sigmask, SIGINT);
        sigaddset(&sigmask, SIGALRM);
        sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask);
        while (! (alarm_rang || return_requested) ) {
            sigsuspend(&oldsigmask);
        }
        sigprocmask(SIG_UNBLOCK, &sigmask, NULL);

        havelast = 1;
    }

    exit(0);
}
Ejemplo n.º 4
0
static grub_err_t
CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer)
{
  Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer;
  char *phdr_base;
  int lowest_segment = -1, highest_segment = -1;
  int i;
  grub_size_t code_size;

  if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX)
    return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF class");

  if (ehdr->e_ident[EI_MAG0] != ELFMAG0
      || ehdr->e_ident[EI_MAG1] != ELFMAG1
      || ehdr->e_ident[EI_MAG2] != ELFMAG2
      || ehdr->e_ident[EI_MAG3] != ELFMAG3
      || ehdr->e_version != EV_CURRENT
      || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
      || ehdr->e_machine != E_MACHINE)
    return grub_error(GRUB_ERR_UNKNOWN_OS, "no valid ELF header found");

  if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
    return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF file type");

  /* FIXME: Should we support program headers at strange locations?  */
  if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
    return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");

#ifdef MULTIBOOT_LOAD_ELF64
  /* We still in 32-bit mode.  */
  if (ehdr->e_entry > 0xffffffff)
    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
#endif

  phdr_base = (char *) buffer + ehdr->e_phoff;
#define phdr(i)			((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))

  for (i = 0; i < ehdr->e_phnum; i++)
    if (phdr(i)->p_type == PT_LOAD && phdr(i)->p_filesz != 0)
      {
	/* Beware that segment 0 isn't necessarily loadable */
	if (lowest_segment == -1
	    || phdr(i)->p_paddr < phdr(lowest_segment)->p_paddr)
	  lowest_segment = i;
	if (highest_segment == -1
	    || phdr(i)->p_paddr > phdr(highest_segment)->p_paddr)
	  highest_segment = i;
      }

  if (lowest_segment == -1)
    return grub_error (GRUB_ERR_BAD_OS, "ELF contains no loadable segments");

  code_size = (phdr(highest_segment)->p_paddr + phdr(highest_segment)->p_memsz) - phdr(lowest_segment)->p_paddr;
  grub_multiboot_payload_dest = phdr(lowest_segment)->p_paddr;

  grub_multiboot_pure_size += code_size;

  grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536;
  grub_multiboot_payload_orig
    = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi);

  if (!grub_multiboot_payload_orig)
    return grub_errno;

  /* Load every loadable segment in memory.  */
  for (i = 0; i < ehdr->e_phnum; i++)
    {
      if (phdr(i)->p_type == PT_LOAD && phdr(i)->p_filesz != 0)
        {
	  char *load_this_module_at = (char *) (grub_multiboot_payload_orig + (long) (phdr(i)->p_paddr - phdr(lowest_segment)->p_paddr));

	  grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n",
			i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr);

	  if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
	      == (grub_off_t) -1)
	    return grub_error (GRUB_ERR_BAD_OS,
			       "invalid offset in program header");

          if (grub_file_read (file, load_this_module_at, phdr(i)->p_filesz)
              != (grub_ssize_t) phdr(i)->p_filesz)
	    return grub_error (GRUB_ERR_BAD_OS,
			       "couldn't read segment from file");

          if (phdr(i)->p_filesz < phdr(i)->p_memsz)
            grub_memset (load_this_module_at + phdr(i)->p_filesz, 0,
			 phdr(i)->p_memsz - phdr(i)->p_filesz);
        }
    }

  for (i = 0; i < ehdr->e_phnum; i++)
    if (phdr(i)->p_vaddr <= ehdr->e_entry
	&& phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry)
      {
	grub_multiboot_payload_eip = grub_multiboot_payload_dest
	  + (ehdr->e_entry - phdr(i)->p_vaddr) + (phdr(i)->p_paddr  - phdr(lowest_segment)->p_paddr);
	break;
      }

  if (i == ehdr->e_phnum)
    return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment");

#undef phdr

  return grub_errno;
}
Ejemplo n.º 5
0
error_code sys_spu_image_import(vm::ptr<sys_spu_image> img, u32 src, u32 type)
{
	sysPrxForUser.warning("sys_spu_image_import(img=*0x%x, src=0x%x, type=%d)", img, src, type);

	if (type != SYS_SPU_IMAGE_PROTECT && type != SYS_SPU_IMAGE_DIRECT)
	{
		return CELL_EINVAL;
	}

	// Initialize ELF loader
	vm::var<spu_elf_info> info({0});

	if (auto res = info->init(vm::cast(src)))
	{
		return res;
	}

	// Reject SCE header
	if (info->sce0.se_magic == 0x53434500)
	{
		return CELL_ENOEXEC;
	}

	// Load ELF header
	vm::var<elf_ehdr<elf_be, u64>> ehdr({0});

	if (info->ldr->get_ehdr(ehdr) || ehdr->e_machine != elf_machine::spu || !ehdr->e_phnum)
	{
		return CELL_ENOEXEC;
	}

	// Load program headers
	vm::var<elf_phdr<elf_be, u64>[]> phdr(ehdr->e_phnum);

	if (info->ldr->get_phdr(phdr, ehdr->e_phnum))
	{
		return CELL_ENOEXEC;
	}

	if (type == SYS_SPU_IMAGE_PROTECT)
	{
		u32 img_size = 0;

		for (const auto& p : phdr)
		{
			if (p.p_type != 1 && p.p_type != 4)
			{
				return CELL_ENOEXEC;
			}

			img_size = std::max<u32>(img_size, static_cast<u32>(p.p_offset + p.p_filesz));
		}

		return _sys_spu_image_import(img, src, img_size, 0);
	}
	else
	{
		s32 num_segs = sys_spu_image::get_nsegs(phdr);

		if (num_segs < 0)
		{
			return CELL_ENOEXEC;
		}

		img->nsegs       = num_segs;
		img->entry_point = static_cast<u32>(ehdr->e_entry);

		vm::ptr<sys_spu_segment> segs = vm::cast(vm::alloc(num_segs * sizeof(sys_spu_segment), vm::main));

		if (!segs)
		{
			return CELL_ENOMEM;
		}

		if (sys_spu_image::fill(segs, num_segs, phdr, src) != num_segs)
		{
			vm::dealloc(segs.addr());
			return CELL_ENOEXEC;
		}

		img->type = SYS_SPU_IMAGE_TYPE_USER;
		img->segs = segs;
		return CELL_OK;
	}
}
Ejemplo n.º 6
0
bool SharedLibrary::Load(const char* full_path,
                         size_t load_address,
                         size_t file_offset,
                         Error* error) {
  // First, record the path.
  LOG("%s: full path '%s'\n", __FUNCTION__, full_path);

  size_t full_path_len = strlen(full_path);
  if (full_path_len >= sizeof(full_path_)) {
    error->Format("Path too long: %s", full_path);
    return false;
  }

  strlcpy(full_path_, full_path, sizeof(full_path_));
  base_name_ = GetBaseNamePtr(full_path_);

  // Load the ELF binary in memory.
  LOG("%s: Loading ELF segments for %s\n", __FUNCTION__, base_name_);

  {
    ElfLoader loader;
    if (!loader.LoadAt(full_path_, file_offset, load_address, error)) {
      return false;
    }

    if (!view_.InitUnmapped(loader.load_start(),
                            loader.loaded_phdr(),
                            loader.phdr_count(),
                            error)) {
      return false;
    }

    if (!symbols_.Init(&view_)) {
      *error = "Missing or malformed symbol table";
      return false;
    }
  }

  if (phdr_table_get_relro_info(view_.phdr(),
                                view_.phdr_count(),
                                view_.load_bias(),
                                &relro_start_,
                                &relro_size_) < 0) {
    relro_start_ = 0;
    relro_size_ = 0;
  }

#ifdef __arm__
  LOG("%s: Extracting ARM.exidx table for %s\n", __FUNCTION__, base_name_);
  (void)phdr_table_get_arm_exidx(
      phdr(), phdr_count(), load_bias(), &arm_exidx_, &arm_exidx_count_);
#endif

  LOG("%s: Parsing dynamic table for %s\n", __FUNCTION__, base_name_);
  ElfView::DynamicIterator dyn(&view_);
  for (; dyn.HasNext(); dyn.GetNext()) {
    ELF::Addr dyn_value = dyn.GetValue();
    uintptr_t dyn_addr = dyn.GetAddress(load_bias());
    switch (dyn.GetTag()) {
      case DT_DEBUG:
        if (view_.dynamic_flags() & PF_W) {
          *dyn.GetValuePointer() =
              reinterpret_cast<uintptr_t>(Globals::GetRDebug()->GetAddress());
        }
        break;
      case DT_INIT:
        LOG("  DT_INIT addr=%p\n", dyn_addr);
        init_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
        break;
      case DT_FINI:
        LOG("  DT_FINI addr=%p\n", dyn_addr);
        fini_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
        break;
      case DT_INIT_ARRAY:
        LOG("  DT_INIT_ARRAY addr=%p\n", dyn_addr);
        init_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
        break;
      case DT_INIT_ARRAYSZ:
        init_array_count_ = dyn_value / sizeof(ELF::Addr);
        LOG("  DT_INIT_ARRAYSZ value=%p count=%p\n",
            dyn_value,
            init_array_count_);
        break;
      case DT_FINI_ARRAY:
        LOG("  DT_FINI_ARRAY addr=%p\n", dyn_addr);
        fini_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
        break;
      case DT_FINI_ARRAYSZ:
        fini_array_count_ = dyn_value / sizeof(ELF::Addr);
        LOG("  DT_FINI_ARRAYSZ value=%p count=%p\n",
            dyn_value,
            fini_array_count_);
        break;
      case DT_PREINIT_ARRAY:
        LOG("  DT_PREINIT_ARRAY addr=%p\n", dyn_addr);
        preinit_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
        break;
      case DT_PREINIT_ARRAYSZ:
        preinit_array_count_ = dyn_value / sizeof(ELF::Addr);
        LOG("  DT_PREINIT_ARRAYSZ value=%p count=%p\n",
            dyn_value,
            preinit_array_count_);
        break;
      case DT_SYMBOLIC:
        LOG("  DT_SYMBOLIC\n");
        has_DT_SYMBOLIC_ = true;
        break;
      case DT_FLAGS:
        if (dyn_value & DF_SYMBOLIC)
          has_DT_SYMBOLIC_ = true;
        break;
#if defined(__mips__)
      case DT_MIPS_RLD_MAP:
        *dyn.GetValuePointer() =
            reinterpret_cast<ELF::Addr>(Globals::GetRDebug()->GetAddress());
        break;
#endif
      default:
        ;
    }
  }

  LOG("%s: Load complete for %s\n", __FUNCTION__, base_name_);
  return true;
}
Ejemplo n.º 7
0
static grub_err_t
CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, const char *filename, void *buffer)
{
  Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer;
  char *phdr_base;
  int i;

  if (ehdr->e_ident[EI_MAG0] != ELFMAG0
      || ehdr->e_ident[EI_MAG1] != ELFMAG1
      || ehdr->e_ident[EI_MAG2] != ELFMAG2
      || ehdr->e_ident[EI_MAG3] != ELFMAG3
      || ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
    return grub_error(GRUB_ERR_UNKNOWN_OS, N_("invalid arch-independent ELF magic"));

  if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX || ehdr->e_machine != E_MACHINE
      || ehdr->e_version != EV_CURRENT)
    return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic"));

  if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
    return grub_error (GRUB_ERR_UNKNOWN_OS, N_("this ELF file is not of the right type"));

  /* FIXME: Should we support program headers at strange locations?  */
  if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
    return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");

  phdr_base = (char *) buffer + ehdr->e_phoff;
#define phdr(i)			((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))

  /* Load every loadable segment in memory.  */
  for (i = 0; i < ehdr->e_phnum; i++)
    {
      if (phdr(i)->p_type == PT_LOAD)
        {
	  grub_err_t err;
	  void *source;

	  grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n",
			i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr);

	  {
	    grub_relocator_chunk_t ch;
	    err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
						   &ch, phdr(i)->p_paddr,
						   phdr(i)->p_memsz);
	    if (err)
	      {
		grub_dprintf ("multiboot_loader", "Error loading phdr %d\n", i);
		return err;
	      }
	    source = get_virtual_current_address (ch);
	  }

	  if (phdr(i)->p_filesz != 0)
	    {
	      if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
		  == (grub_off_t) -1)
		return grub_errno;

	      if (grub_file_read (file, source, phdr(i)->p_filesz)
		  != (grub_ssize_t) phdr(i)->p_filesz)
		{
		  if (!grub_errno)
		    grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
				filename);
		  return grub_errno;
		}
	    }

          if (phdr(i)->p_filesz < phdr(i)->p_memsz)
            grub_memset ((grub_uint8_t *) source + phdr(i)->p_filesz, 0,
			 phdr(i)->p_memsz - phdr(i)->p_filesz);
        }
    }

  for (i = 0; i < ehdr->e_phnum; i++)
    if (phdr(i)->p_vaddr <= ehdr->e_entry
	&& phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry)
      {
	grub_multiboot_payload_eip = (ehdr->e_entry - phdr(i)->p_vaddr)
	  + phdr(i)->p_paddr;
#ifdef MULTIBOOT_LOAD_ELF64
# ifdef __mips
  /* We still in 32-bit mode.  */
  if ((ehdr->e_entry - phdr(i)->p_vaddr)
      + phdr(i)->p_paddr < 0xffffffff80000000ULL)
    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
# else
  /* We still in 32-bit mode.  */
  if ((ehdr->e_entry - phdr(i)->p_vaddr)
      + phdr(i)->p_paddr > 0xffffffff)
    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
# endif
#endif
	break;
      }

  if (i == ehdr->e_phnum)
    return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment");

#if defined (__i386__) || defined (__x86_64__)
  
#elif defined (__mips)
  grub_multiboot_payload_eip |= 0x80000000;
#else
#error Please complete this
#endif

  if (ehdr->e_shnum)
    {
      grub_uint8_t *shdr, *shdrptr;

      shdr = grub_malloc (ehdr->e_shnum * ehdr->e_shentsize);
      if (!shdr)
	return grub_errno;
      
      if (grub_file_seek (file, ehdr->e_shoff) == (grub_off_t) -1)
	return grub_errno;

      if (grub_file_read (file, shdr, ehdr->e_shnum * ehdr->e_shentsize)
              != (grub_ssize_t) ehdr->e_shnum * ehdr->e_shentsize)
	{
	  if (!grub_errno)
	    grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
			filename);
	  return grub_errno;
	}
      
      for (shdrptr = shdr, i = 0; i < ehdr->e_shnum;
	   shdrptr += ehdr->e_shentsize, i++)
	{
	  Elf_Shdr *sh = (Elf_Shdr *) shdrptr;
	  void *src;
	  grub_addr_t target;
	  grub_err_t err;

	  /* This section is a loaded section,
	     so we don't care.  */
	  if (sh->sh_addr != 0)
	    continue;
		      
	  /* This section is empty, so we don't care.  */
	  if (sh->sh_size == 0)
	    continue;

	  {
	    grub_relocator_chunk_t ch;
	    err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator,
						    &ch, 0,
						    (0xffffffff - sh->sh_size)
						    + 1, sh->sh_size,
						    sh->sh_addralign,
						    GRUB_RELOCATOR_PREFERENCE_NONE,
						    0);
	    if (err)
	      {
		grub_dprintf ("multiboot_loader", "Error loading shdr %d\n", i);
		return err;
	      }
	    src = get_virtual_current_address (ch);
	    target = get_physical_target_address (ch);
	  }

	  if (grub_file_seek (file, sh->sh_offset) == (grub_off_t) -1)
	    return grub_errno;

          if (grub_file_read (file, src, sh->sh_size)
              != (grub_ssize_t) sh->sh_size)
	    {
	      if (!grub_errno)
		grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
			    filename);
	      return grub_errno;
	    }
	  sh->sh_addr = target;
	}
      grub_multiboot_add_elfsyms (ehdr->e_shnum, ehdr->e_shentsize,
				  ehdr->e_shstrndx, shdr);
    }

#undef phdr

  return grub_errno;
}