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; }
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; }
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); }
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; }
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; } }
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; }
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; }