iPXE
elf.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /**
00027  * @file
00028  *
00029  * ELF image format
00030  *
00031  * A "pure" ELF image is not a bootable image.  There are various
00032  * bootable formats based upon ELF (e.g. Multiboot), which share
00033  * common ELF-related functionality.
00034  */
00035 
00036 #include <errno.h>
00037 #include <elf.h>
00038 #include <ipxe/uaccess.h>
00039 #include <ipxe/segment.h>
00040 #include <ipxe/image.h>
00041 #include <ipxe/elf.h>
00042 
00043 /**
00044  * Load ELF segment into memory
00045  *
00046  * @v image             ELF file
00047  * @v phdr              ELF program header
00048  * @v dest              Destination address
00049  * @ret rc              Return status code
00050  */
00051 static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
00052                               physaddr_t dest ) {
00053         userptr_t buffer = phys_to_user ( dest );
00054         int rc;
00055 
00056         DBGC ( image, "ELF %p loading segment [%x,%x) to [%lx,%lx,%lx)\n",
00057                image, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
00058                dest, ( dest + phdr->p_filesz ), ( dest + phdr->p_memsz ) );
00059 
00060         /* Verify and prepare segment */
00061         if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
00062                                    phdr->p_memsz ) ) != 0 ) {
00063                 DBGC ( image, "ELF %p could not prepare segment: %s\n",
00064                        image, strerror ( rc ) );
00065                 return rc;
00066         }
00067 
00068         /* Copy image to segment */
00069         memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
00070 
00071         return 0;
00072 }
00073 
00074 /**
00075  * Process ELF segment
00076  *
00077  * @v image             ELF file
00078  * @v ehdr              ELF executable header
00079  * @v phdr              ELF program header
00080  * @v process           Segment processor
00081  * @ret entry           Entry point, if found
00082  * @ret max             Maximum used address
00083  * @ret rc              Return status code
00084  */
00085 static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
00086                          int ( * process ) ( struct image *image,
00087                                              Elf_Phdr *phdr, physaddr_t dest ),
00088                          physaddr_t *entry, physaddr_t *max ) {
00089         physaddr_t dest;
00090         physaddr_t end;
00091         unsigned long e_offset;
00092         int rc;
00093 
00094         /* Do nothing for non-PT_LOAD segments */
00095         if ( phdr->p_type != PT_LOAD )
00096                 return 0;
00097 
00098         /* Check segment lies within image */
00099         if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
00100                 DBGC ( image, "ELF %p segment outside image\n", image );
00101                 return -ENOEXEC;
00102         }
00103 
00104         /* Find start address: use physical address for preference,
00105          * fall back to virtual address if no physical address
00106          * supplied.
00107          */
00108         dest = phdr->p_paddr;
00109         if ( ! dest )
00110                 dest = phdr->p_vaddr;
00111         if ( ! dest ) {
00112                 DBGC ( image, "ELF %p segment loads to physical address 0\n",
00113                        image );
00114                 return -ENOEXEC;
00115         }
00116         end = ( dest + phdr->p_memsz );
00117 
00118         /* Update maximum used address, if applicable */
00119         if ( end > *max )
00120                 *max = end;
00121 
00122         /* Process segment */
00123         if ( ( rc = process ( image, phdr, dest ) ) != 0 )
00124                 return rc;
00125 
00126         /* Set execution address, if it lies within this segment */
00127         if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
00128                 *entry = ehdr->e_entry;
00129                 DBGC ( image, "ELF %p found physical entry point at %lx\n",
00130                        image, *entry );
00131         } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
00132                     < phdr->p_filesz ) {
00133                 if ( ! *entry ) {
00134                         *entry = ( dest + e_offset );
00135                         DBGC ( image, "ELF %p found virtual entry point at %lx"
00136                                " (virt %lx)\n", image, *entry,
00137                                ( ( unsigned long ) ehdr->e_entry ) );
00138                 }
00139         }
00140 
00141         return 0;
00142 }
00143 
00144 /**
00145  * Process ELF segments
00146  *
00147  * @v image             ELF file
00148  * @v ehdr              ELF executable header
00149  * @v process           Segment processor
00150  * @ret entry           Entry point, if found
00151  * @ret max             Maximum used address
00152  * @ret rc              Return status code
00153  */
00154 int elf_segments ( struct image *image, Elf_Ehdr *ehdr,
00155                    int ( * process ) ( struct image *image, Elf_Phdr *phdr,
00156                                        physaddr_t dest ),
00157                    physaddr_t *entry, physaddr_t *max ) {
00158         Elf_Phdr phdr;
00159         Elf_Off phoff;
00160         unsigned int phnum;
00161         int rc;
00162 
00163         /* Initialise maximum used address */
00164         *max = 0;
00165 
00166         /* Invalidate entry point */
00167         *entry = 0;
00168 
00169         /* Read and process ELF program headers */
00170         for ( phoff = ehdr->e_phoff , phnum = ehdr->e_phnum ; phnum ;
00171               phoff += ehdr->e_phentsize, phnum-- ) {
00172                 if ( phoff > image->len ) {
00173                         DBGC ( image, "ELF %p program header %d outside "
00174                                "image\n", image, phnum );
00175                         return -ENOEXEC;
00176                 }
00177                 copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
00178                 if ( ( rc = elf_segment ( image, ehdr, &phdr, process,
00179                                           entry, max ) ) != 0 )
00180                         return rc;
00181         }
00182 
00183         /* Check for a valid execution address */
00184         if ( ! *entry ) {
00185                 DBGC ( image, "ELF %p entry point %lx outside image\n",
00186                        image, ( ( unsigned long ) ehdr->e_entry ) );
00187                 return -ENOEXEC;
00188         }
00189 
00190         return 0;
00191 }
00192 
00193 /**
00194  * Load ELF image into memory
00195  *
00196  * @v image             ELF file
00197  * @ret entry           Entry point
00198  * @ret max             Maximum used address
00199  * @ret rc              Return status code
00200  */
00201 int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
00202         static const uint8_t e_ident[] = {
00203                 [EI_MAG0]       = ELFMAG0,
00204                 [EI_MAG1]       = ELFMAG1,
00205                 [EI_MAG2]       = ELFMAG2,
00206                 [EI_MAG3]       = ELFMAG3,
00207                 [EI_CLASS]      = ELFCLASS,
00208         };
00209         Elf_Ehdr ehdr;
00210         int rc;
00211 
00212         /* Read ELF header */
00213         copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
00214         if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident,
00215                       sizeof ( e_ident ) ) != 0 ) {
00216                 DBGC ( image, "ELF %p has invalid signature\n", image );
00217                 return -ENOEXEC;
00218         }
00219 
00220         /* Load ELF segments into memory */
00221         if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment,
00222                                    entry, max ) ) != 0 )
00223                 return rc;
00224 
00225         return 0;
00226 }