2006-04-26 16:39:16 +00:00
|
|
|
/* ELF Boot loader
|
|
|
|
|
* As we have seek, this implementation can be straightforward.
|
|
|
|
|
* 2003-07 by SONE Takeshi
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "openbios/config.h"
|
2010-03-14 14:21:02 +00:00
|
|
|
#include "kernel/kernel.h"
|
2010-03-14 17:06:20 +00:00
|
|
|
#include "arch/common/elf.h"
|
2006-04-26 16:39:16 +00:00
|
|
|
#include "asm/elf.h"
|
2010-03-14 17:06:20 +00:00
|
|
|
#include "arch/common/elf_boot.h"
|
2010-03-14 16:09:44 +00:00
|
|
|
#include "libopenbios/sys_info.h"
|
2010-03-14 15:05:53 +00:00
|
|
|
#include "libopenbios/ipchecksum.h"
|
2006-04-26 16:39:16 +00:00
|
|
|
#include "loadfs.h"
|
2008-07-07 18:35:51 +00:00
|
|
|
#include "boot.h"
|
2006-04-26 16:39:16 +00:00
|
|
|
#define printf printk
|
|
|
|
|
#define debug printk
|
|
|
|
|
|
2006-05-07 16:40:13 +00:00
|
|
|
#define addr_fixup(addr) ((addr) & 0x00ffffff)
|
2006-04-26 16:39:16 +00:00
|
|
|
|
|
|
|
|
static char *image_name, *image_version;
|
|
|
|
|
|
|
|
|
|
static void *calloc(size_t nmemb, size_t size)
|
|
|
|
|
{
|
|
|
|
|
size_t alloc_size = nmemb * size;
|
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
|
|
if (alloc_size < nmemb || alloc_size < size) {
|
|
|
|
|
printf("calloc overflow: %u, %u\n", nmemb, size);
|
2008-11-30 11:54:01 +00:00
|
|
|
return NULL;
|
2006-04-26 16:39:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mem = malloc(alloc_size);
|
|
|
|
|
memset(mem, 0, alloc_size);
|
|
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int check_mem_ranges(struct sys_info *info,
|
|
|
|
|
Elf_phdr *phdr, int phnum)
|
|
|
|
|
{
|
|
|
|
|
int i, j;
|
|
|
|
|
unsigned long start, end;
|
|
|
|
|
unsigned long prog_start, prog_end;
|
|
|
|
|
struct memrange *mem;
|
|
|
|
|
|
|
|
|
|
prog_start = virt_to_phys(&_start);
|
|
|
|
|
prog_end = virt_to_phys(&_end);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < phnum; i++) {
|
|
|
|
|
if (phdr[i].p_type != PT_LOAD)
|
|
|
|
|
continue;
|
2006-05-07 16:40:13 +00:00
|
|
|
start = addr_fixup(phdr[i].p_paddr);
|
2006-04-26 16:39:16 +00:00
|
|
|
end = start + phdr[i].p_memsz;
|
|
|
|
|
if (start < prog_start && end > prog_start)
|
|
|
|
|
goto conflict;
|
|
|
|
|
if (start < prog_end && end > prog_end)
|
|
|
|
|
goto conflict;
|
|
|
|
|
mem=info->memrange;
|
|
|
|
|
for (j = 0; j < info->n_memranges; j++) {
|
|
|
|
|
if (mem[j].base <= start && mem[j].base + mem[j].size >= end)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (j >= info->n_memranges)
|
|
|
|
|
goto badseg;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
conflict:
|
|
|
|
|
printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
|
|
|
|
|
|
|
|
|
|
badseg:
|
|
|
|
|
printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
|
2006-05-22 22:16:20 +00:00
|
|
|
unsigned short *sum_ptr,
|
|
|
|
|
unsigned int offset)
|
2006-04-26 16:39:16 +00:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *buf = NULL;
|
|
|
|
|
int retval = 0;
|
|
|
|
|
unsigned long addr, end;
|
|
|
|
|
Elf_Nhdr *nhdr;
|
|
|
|
|
const char *name;
|
|
|
|
|
void *desc;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < phnum; i++) {
|
|
|
|
|
if (phdr[i].p_type != PT_NOTE)
|
|
|
|
|
continue;
|
|
|
|
|
buf = malloc(phdr[i].p_filesz);
|
2006-05-22 22:16:20 +00:00
|
|
|
file_seek(offset + phdr[i].p_offset);
|
2008-07-07 18:35:51 +00:00
|
|
|
if ((uint32_t)lfile_read(buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
|
2006-04-26 16:39:16 +00:00
|
|
|
printf("Can't read note segment\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
addr = (unsigned long) buf;
|
|
|
|
|
end = addr + phdr[i].p_filesz;
|
|
|
|
|
while (addr < end) {
|
|
|
|
|
nhdr = (Elf_Nhdr *) addr;
|
|
|
|
|
addr += sizeof(Elf_Nhdr);
|
|
|
|
|
name = (const char *) addr;
|
|
|
|
|
addr += (nhdr->n_namesz+3) & ~3;
|
|
|
|
|
desc = (void *) addr;
|
|
|
|
|
addr += (nhdr->n_descsz+3) & ~3;
|
|
|
|
|
|
|
|
|
|
if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
|
|
|
|
|
&& memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
|
|
|
|
|
if (nhdr->n_type == EIN_PROGRAM_NAME) {
|
|
|
|
|
image_name = calloc(1, nhdr->n_descsz + 1);
|
|
|
|
|
memcpy(image_name, desc, nhdr->n_descsz);
|
|
|
|
|
}
|
|
|
|
|
if (nhdr->n_type == EIN_PROGRAM_VERSION) {
|
|
|
|
|
image_version = calloc(1, nhdr->n_descsz + 1);
|
|
|
|
|
memcpy(image_version, desc, nhdr->n_descsz);
|
|
|
|
|
}
|
|
|
|
|
if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
|
|
|
|
|
*sum_ptr = *(unsigned short *) desc;
|
|
|
|
|
debug("Image checksum: %#04x\n", *sum_ptr);
|
|
|
|
|
/* Where in the file */
|
|
|
|
|
retval = phdr[i].p_offset
|
|
|
|
|
+ (unsigned long) desc - (unsigned long) buf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
out:
|
2006-06-05 19:40:05 +00:00
|
|
|
file_close();
|
2006-04-26 16:39:16 +00:00
|
|
|
if (buf)
|
|
|
|
|
free(buf);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int load_segments(Elf_phdr *phdr, int phnum,
|
2006-05-22 22:16:20 +00:00
|
|
|
unsigned long checksum_offset,
|
|
|
|
|
unsigned int offset)
|
2006-04-26 16:39:16 +00:00
|
|
|
{
|
|
|
|
|
unsigned long bytes;
|
|
|
|
|
//unsigned int start_time, time;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
bytes = 0;
|
|
|
|
|
// start_time = currticks();
|
|
|
|
|
for (i = 0; i < phnum; i++) {
|
|
|
|
|
if (phdr[i].p_type != PT_LOAD)
|
|
|
|
|
continue;
|
|
|
|
|
debug("segment %d addr:%#x file:%#x mem:%#x ",
|
2006-05-07 16:40:13 +00:00
|
|
|
i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz);
|
2006-05-22 22:16:20 +00:00
|
|
|
file_seek(offset + phdr[i].p_offset);
|
2006-04-26 16:39:16 +00:00
|
|
|
debug("loading... ");
|
2008-07-07 18:35:51 +00:00
|
|
|
if ((uint32_t)lfile_read(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz)
|
2006-04-26 16:39:16 +00:00
|
|
|
!= phdr[i].p_filesz) {
|
|
|
|
|
printf("Can't read program segment %d\n", i);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
bytes += phdr[i].p_filesz;
|
|
|
|
|
debug("clearing... ");
|
2008-07-07 18:35:51 +00:00
|
|
|
memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0,
|
2006-04-26 16:39:16 +00:00
|
|
|
phdr[i].p_memsz - phdr[i].p_filesz);
|
|
|
|
|
if (phdr[i].p_offset <= checksum_offset
|
|
|
|
|
&& phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
|
|
|
|
|
debug("clearing checksum... ");
|
2006-05-07 16:40:13 +00:00
|
|
|
memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + checksum_offset
|
2006-04-26 16:39:16 +00:00
|
|
|
- phdr[i].p_offset), 0, 2);
|
|
|
|
|
}
|
|
|
|
|
debug("ok\n");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// time = currticks() - start_time;
|
|
|
|
|
//debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time,
|
|
|
|
|
// time? bytes/time : 0);
|
|
|
|
|
debug("Loaded %lu bytes \n", bytes);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
|
|
|
|
|
unsigned short image_sum)
|
|
|
|
|
{
|
|
|
|
|
unsigned short sum, part_sum;
|
|
|
|
|
unsigned long offset;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
sum = 0;
|
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
|
|
part_sum = ipchksum(ehdr, sizeof *ehdr);
|
|
|
|
|
sum = add_ipchksums(offset, sum, part_sum);
|
|
|
|
|
offset += sizeof *ehdr;
|
|
|
|
|
|
|
|
|
|
part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
|
|
|
|
|
sum = add_ipchksums(offset, sum, part_sum);
|
|
|
|
|
offset += phnum * sizeof(*phdr);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < phnum; i++) {
|
|
|
|
|
if (phdr[i].p_type != PT_LOAD)
|
|
|
|
|
continue;
|
2006-05-07 16:40:13 +00:00
|
|
|
part_sum = ipchksum(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_memsz);
|
2006-04-26 16:39:16 +00:00
|
|
|
sum = add_ipchksums(offset, sum, part_sum);
|
|
|
|
|
offset += phdr[i].p_memsz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sum != image_sum) {
|
|
|
|
|
printf("Verify FAILED (image:%#04x vs computed:%#04x)\n",
|
|
|
|
|
image_sum, sum);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline unsigned padded(unsigned s)
|
|
|
|
|
{
|
|
|
|
|
return (s + 3) & ~3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
|
|
|
|
|
unsigned type, const char *desc, unsigned descsz)
|
|
|
|
|
{
|
|
|
|
|
Elf_Nhdr nhdr;
|
|
|
|
|
unsigned ent_size, new_size, pad;
|
|
|
|
|
char *addr;
|
|
|
|
|
|
|
|
|
|
if (!bhdr)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
nhdr.n_namesz = name? strlen(name)+1 : 0;
|
|
|
|
|
nhdr.n_descsz = descsz;
|
|
|
|
|
nhdr.n_type = type;
|
|
|
|
|
ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
|
|
|
|
|
if (bhdr->b_size + ent_size > 0xffff) {
|
|
|
|
|
printf("Boot notes too big\n");
|
|
|
|
|
free(bhdr);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (bhdr->b_size + ent_size > bhdr->b_checksum) {
|
|
|
|
|
do {
|
|
|
|
|
new_size = bhdr->b_checksum * 2;
|
|
|
|
|
} while (new_size < bhdr->b_size + ent_size);
|
|
|
|
|
if (new_size > 0xffff)
|
|
|
|
|
new_size = 0xffff;
|
|
|
|
|
debug("expanding boot note size to %u\n", new_size);
|
|
|
|
|
#ifdef HAVE_REALLOC
|
|
|
|
|
bhdr = realloc(bhdr, new_size);
|
|
|
|
|
bhdr->b_checksum = new_size;
|
|
|
|
|
#else
|
|
|
|
|
printf("Boot notes too big\n");
|
|
|
|
|
free(bhdr);
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr = (char *) bhdr;
|
|
|
|
|
addr += bhdr->b_size;
|
|
|
|
|
memcpy(addr, &nhdr, sizeof(nhdr));
|
|
|
|
|
addr += sizeof(nhdr);
|
|
|
|
|
|
|
|
|
|
memcpy(addr, name, nhdr.n_namesz);
|
|
|
|
|
addr += nhdr.n_namesz;
|
|
|
|
|
pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
|
|
|
|
|
memset(addr, 0, pad);
|
|
|
|
|
addr += pad;
|
|
|
|
|
|
|
|
|
|
memcpy(addr, desc, nhdr.n_descsz);
|
|
|
|
|
addr += nhdr.n_descsz;
|
|
|
|
|
pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
|
|
|
|
|
memset(addr, 0, pad);
|
|
|
|
|
addr += pad;
|
|
|
|
|
|
|
|
|
|
bhdr->b_size += ent_size;
|
|
|
|
|
bhdr->b_records++;
|
|
|
|
|
return bhdr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
|
|
|
|
|
unsigned type, const char *desc)
|
|
|
|
|
{
|
|
|
|
|
return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
|
|
|
|
|
{
|
|
|
|
|
Elf_Bhdr *bhdr;
|
|
|
|
|
|
|
|
|
|
bhdr = malloc(256);
|
|
|
|
|
bhdr->b_signature = ELF_BHDR_MAGIC;
|
|
|
|
|
bhdr->b_size = sizeof *bhdr;
|
|
|
|
|
bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
|
|
|
|
|
bhdr->b_records = 0;
|
|
|
|
|
|
|
|
|
|
if (info->firmware)
|
|
|
|
|
bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
|
|
|
|
|
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
|
|
|
|
|
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
|
|
|
|
|
if (cmdline)
|
|
|
|
|
bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
|
|
|
|
|
if (!bhdr)
|
|
|
|
|
return bhdr;
|
|
|
|
|
bhdr->b_checksum = 0;
|
|
|
|
|
bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
|
|
|
|
|
return bhdr;
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-23 14:29:29 +00:00
|
|
|
int elf_load(struct sys_info *info, const char *filename, const char *cmdline,
|
|
|
|
|
const void *romvec)
|
2006-04-26 16:39:16 +00:00
|
|
|
{
|
|
|
|
|
Elf_ehdr ehdr;
|
|
|
|
|
Elf_phdr *phdr = NULL;
|
|
|
|
|
unsigned long phdr_size;
|
|
|
|
|
unsigned long checksum_offset;
|
2008-11-29 19:31:28 +00:00
|
|
|
unsigned short checksum = 0;
|
2006-04-26 16:39:16 +00:00
|
|
|
Elf_Bhdr *boot_notes = NULL;
|
|
|
|
|
int retval = -1;
|
|
|
|
|
int image_retval;
|
2006-05-22 22:16:20 +00:00
|
|
|
unsigned int offset;
|
2006-04-26 16:39:16 +00:00
|
|
|
|
2008-11-30 11:54:01 +00:00
|
|
|
image_name = image_version = NULL;
|
2006-04-26 16:39:16 +00:00
|
|
|
|
|
|
|
|
if (!file_open(filename))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2006-05-22 22:16:20 +00:00
|
|
|
for (offset = 0; offset < 16 * 512; offset += 512) {
|
|
|
|
|
if (lfile_read(&ehdr, sizeof ehdr) != sizeof ehdr) {
|
|
|
|
|
debug("Can't read ELF header\n");
|
|
|
|
|
retval = LOADER_NOT_SUPPORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ehdr.e_ident[EI_MAG0] == ELFMAG0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
file_seek(offset);
|
2006-04-26 16:39:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_CLASS] != ARCH_ELF_CLASS
|
|
|
|
|
|| ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA
|
|
|
|
|
|| ehdr.e_ident[EI_VERSION] != EV_CURRENT
|
|
|
|
|
|| ehdr.e_type != ET_EXEC
|
|
|
|
|
|| !ARCH_ELF_MACHINE_OK(ehdr.e_machine)
|
|
|
|
|
|| ehdr.e_version != EV_CURRENT
|
|
|
|
|
|| ehdr.e_phentsize != sizeof(Elf_phdr)) {
|
|
|
|
|
debug("Not a bootable ELF image\n");
|
|
|
|
|
retval = LOADER_NOT_SUPPORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
phdr_size = ehdr.e_phnum * sizeof *phdr;
|
|
|
|
|
phdr = malloc(phdr_size);
|
2006-05-22 22:16:20 +00:00
|
|
|
file_seek(offset + ehdr.e_phoff);
|
2008-07-07 18:35:51 +00:00
|
|
|
if ((uint32_t)lfile_read(phdr, phdr_size) != phdr_size) {
|
2006-04-26 16:39:16 +00:00
|
|
|
printf("Can't read program header\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2006-05-22 22:16:20 +00:00
|
|
|
checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum, offset);
|
2006-04-26 16:39:16 +00:00
|
|
|
|
|
|
|
|
printf("Loading %s", image_name ? image_name : "image");
|
|
|
|
|
if (image_version)
|
|
|
|
|
printf(" version %s", image_version);
|
|
|
|
|
printf("...\n");
|
|
|
|
|
|
2006-05-22 22:16:20 +00:00
|
|
|
if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, offset))
|
2006-04-26 16:39:16 +00:00
|
|
|
goto out;
|
2008-07-07 18:35:51 +00:00
|
|
|
|
2006-04-26 16:39:16 +00:00
|
|
|
if (checksum_offset) {
|
|
|
|
|
if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boot_notes = build_boot_notes(info, cmdline);
|
|
|
|
|
|
|
|
|
|
//debug("current time: %lu\n", currticks());
|
|
|
|
|
|
2006-05-07 16:40:13 +00:00
|
|
|
debug("entry point is %#x\n", addr_fixup(ehdr.e_entry));
|
2006-04-26 16:39:16 +00:00
|
|
|
printf("Jumping to entry point...\n");
|
2006-05-10 23:00:34 +00:00
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
|
{
|
2008-07-07 18:35:51 +00:00
|
|
|
int (*entry)(const void *romvec_ptr, int p2, int p3, int p4, int p5);
|
2006-05-10 23:00:34 +00:00
|
|
|
|
|
|
|
|
entry = (void *) addr_fixup(ehdr.e_entry);
|
|
|
|
|
image_retval = entry(romvec, 0, 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2006-05-07 16:40:13 +00:00
|
|
|
image_retval = start_elf(addr_fixup(ehdr.e_entry), virt_to_phys(boot_notes));
|
2006-05-10 23:00:34 +00:00
|
|
|
#endif
|
2006-04-26 16:39:16 +00:00
|
|
|
|
|
|
|
|
// console_init(); FIXME
|
|
|
|
|
printf("Image returned with return value %#x\n", image_retval);
|
|
|
|
|
retval = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
2006-06-05 19:40:05 +00:00
|
|
|
file_close();
|
2006-04-26 16:39:16 +00:00
|
|
|
if (phdr)
|
|
|
|
|
free(phdr);
|
|
|
|
|
if (boot_notes)
|
|
|
|
|
free(boot_notes);
|
|
|
|
|
if (image_name)
|
|
|
|
|
free(image_name);
|
|
|
|
|
if (image_version)
|
|
|
|
|
free(image_version);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|