1/* 2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19FILE_LICENCE ( GPL2_OR_LATER ); 20 21/** 22 * @file 23 * 24 * ELF image format 25 * 26 * A "pure" ELF image is not a bootable image. There are various 27 * bootable formats based upon ELF (e.g. Multiboot), which share 28 * common ELF-related functionality. 29 */ 30 31#include <errno.h> 32#include <elf.h> 33#include <gpxe/uaccess.h> 34#include <gpxe/segment.h> 35#include <gpxe/image.h> 36#include <gpxe/elf.h> 37 38typedef Elf32_Ehdr Elf_Ehdr; 39typedef Elf32_Phdr Elf_Phdr; 40typedef Elf32_Off Elf_Off; 41 42/** 43 * Load ELF segment into memory 44 * 45 * @v image ELF file 46 * @v phdr ELF program header 47 * @v ehdr ELF executable header 48 * @ret rc Return status code 49 */ 50static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, 51 Elf_Ehdr *ehdr ) { 52 physaddr_t dest; 53 userptr_t buffer; 54 unsigned long e_offset; 55 int rc; 56 57 /* Do nothing for non-PT_LOAD segments */ 58 if ( phdr->p_type != PT_LOAD ) 59 return 0; 60 61 /* Check segment lies within image */ 62 if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) { 63 DBGC ( image, "ELF %p segment outside image\n", image ); 64 return -ENOEXEC; 65 } 66 67 /* Find start address: use physical address for preference, 68 * fall back to virtual address if no physical address 69 * supplied. 70 */ 71 dest = phdr->p_paddr; 72 if ( ! dest ) 73 dest = phdr->p_vaddr; 74 if ( ! dest ) { 75 DBGC ( image, "ELF %p segment loads to physical address 0\n", 76 image ); 77 return -ENOEXEC; 78 } 79 buffer = phys_to_user ( dest ); 80 81 DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image, 82 phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ), 83 phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ), 84 ( phdr->p_paddr + phdr->p_memsz ) ); 85 86 /* Verify and prepare segment */ 87 if ( ( rc = prep_segment ( buffer, phdr->p_filesz, 88 phdr->p_memsz ) ) != 0 ) { 89 DBGC ( image, "ELF %p could not prepare segment: %s\n", 90 image, strerror ( rc ) ); 91 return rc; 92 } 93 94 /* Copy image to segment */ 95 memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); 96 97 /* Set execution address, if it lies within this segment */ 98 if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) { 99 image->priv.phys = ehdr->e_entry; 100 DBGC ( image, "ELF %p found physical entry point at %lx\n", 101 image, image->priv.phys ); 102 } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) ) 103 < phdr->p_filesz ) { 104 if ( ! image->priv.phys ) { 105 image->priv.phys = ( dest + e_offset ); 106 DBGC ( image, "ELF %p found virtual entry point at %lx" 107 " (virt %lx)\n", image, image->priv.phys, 108 ( ( unsigned long ) ehdr->e_entry ) ); 109 } 110 } 111 112 return 0; 113} 114 115/** 116 * Load ELF image into memory 117 * 118 * @v image ELF file 119 * @ret rc Return status code 120 */ 121int elf_load ( struct image *image ) { 122 Elf_Ehdr ehdr; 123 Elf_Phdr phdr; 124 Elf_Off phoff; 125 unsigned int phnum; 126 int rc; 127 128 /* Image type must already have been set by caller */ 129 assert ( image->type != NULL ); 130 131 /* Read ELF header */ 132 copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); 133 if ( memcmp ( &ehdr.e_ident[EI_MAG0], ELFMAG, SELFMAG ) != 0 ) { 134 DBGC ( image, "ELF %p has invalid signature\n", image ); 135 return -ENOEXEC; 136 } 137 138 /* Invalidate execution address */ 139 image->priv.phys = 0; 140 141 /* Read ELF program headers */ 142 for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ; 143 phoff += ehdr.e_phentsize, phnum-- ) { 144 if ( phoff > image->len ) { 145 DBGC ( image, "ELF %p program header %d outside " 146 "image\n", image, phnum ); 147 return -ENOEXEC; 148 } 149 copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) ); 150 if ( ( rc = elf_load_segment ( image, &phdr, &ehdr ) ) != 0 ) 151 return rc; 152 } 153 154 /* Check for a valid execution address */ 155 if ( ! image->priv.phys ) { 156 DBGC ( image, "ELF %p entry point %lx outside image\n", 157 image, ( ( unsigned long ) ehdr.e_entry ) ); 158 return -ENOEXEC; 159 } 160 161 return 0; 162} 163