iPXE
elf.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /**
27  * @file
28  *
29  * ELF image format
30  *
31  * A "pure" ELF image is not a bootable image. There are various
32  * bootable formats based upon ELF (e.g. Multiboot), which share
33  * common ELF-related functionality.
34  */
35 
36 #include <errno.h>
37 #include <elf.h>
38 #include <ipxe/uaccess.h>
39 #include <ipxe/segment.h>
40 #include <ipxe/image.h>
41 #include <ipxe/elf.h>
42 
43 /**
44  * Load ELF segment into memory
45  *
46  * @v image ELF file
47  * @v phdr ELF program header
48  * @v dest Destination address
49  * @ret rc Return status code
50  */
51 static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
52  physaddr_t dest ) {
54  int rc;
55 
56  DBGC ( image, "ELF %p loading segment [%x,%x) to [%lx,%lx,%lx)\n",
57  image, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
58  dest, ( dest + phdr->p_filesz ), ( dest + phdr->p_memsz ) );
59 
60  /* Verify and prepare segment */
61  if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
62  phdr->p_memsz ) ) != 0 ) {
63  DBGC ( image, "ELF %p could not prepare segment: %s\n",
64  image, strerror ( rc ) );
65  return rc;
66  }
67 
68  /* Copy image to segment */
69  memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
70 
71  return 0;
72 }
73 
74 /**
75  * Process ELF segment
76  *
77  * @v image ELF file
78  * @v ehdr ELF executable header
79  * @v phdr ELF program header
80  * @v process Segment processor
81  * @ret entry Entry point, if found
82  * @ret max Maximum used address
83  * @ret rc Return status code
84  */
85 static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
86  int ( * process ) ( struct image *image,
87  Elf_Phdr *phdr, physaddr_t dest ),
88  physaddr_t *entry, physaddr_t *max ) {
91  unsigned long e_offset;
92  int rc;
93 
94  /* Do nothing for non-PT_LOAD segments */
95  if ( phdr->p_type != PT_LOAD )
96  return 0;
97 
98  /* Check segment lies within image */
99  if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
100  DBGC ( image, "ELF %p segment outside image\n", image );
101  return -ENOEXEC;
102  }
103 
104  /* Find start address: use physical address for preference,
105  * fall back to virtual address if no physical address
106  * supplied.
107  */
108  dest = phdr->p_paddr;
109  if ( ! dest )
110  dest = phdr->p_vaddr;
111  if ( ! dest ) {
112  DBGC ( image, "ELF %p segment loads to physical address 0\n",
113  image );
114  return -ENOEXEC;
115  }
116  end = ( dest + phdr->p_memsz );
117 
118  /* Update maximum used address, if applicable */
119  if ( end > *max )
120  *max = end;
121 
122  /* Process segment */
123  if ( ( rc = process ( image, phdr, dest ) ) != 0 )
124  return rc;
125 
126  /* Set execution address, if it lies within this segment */
127  if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
128  *entry = ehdr->e_entry;
129  DBGC ( image, "ELF %p found physical entry point at %lx\n",
130  image, *entry );
131  } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
132  < phdr->p_filesz ) {
133  if ( ! *entry ) {
134  *entry = ( dest + e_offset );
135  DBGC ( image, "ELF %p found virtual entry point at %lx"
136  " (virt %lx)\n", image, *entry,
137  ( ( unsigned long ) ehdr->e_entry ) );
138  }
139  }
140 
141  return 0;
142 }
143 
144 /**
145  * Process ELF segments
146  *
147  * @v image ELF file
148  * @v ehdr ELF executable header
149  * @v process Segment processor
150  * @ret entry Entry point, if found
151  * @ret max Maximum used address
152  * @ret rc Return status code
153  */
154 int elf_segments ( struct image *image, Elf_Ehdr *ehdr,
155  int ( * process ) ( struct image *image, Elf_Phdr *phdr,
156  physaddr_t dest ),
157  physaddr_t *entry, physaddr_t *max ) {
158  Elf_Phdr phdr;
159  Elf_Off phoff;
160  unsigned int phnum;
161  int rc;
162 
163  /* Initialise maximum used address */
164  *max = 0;
165 
166  /* Invalidate entry point */
167  *entry = 0;
168 
169  /* Read and process ELF program headers */
170  for ( phoff = ehdr->e_phoff , phnum = ehdr->e_phnum ; phnum ;
171  phoff += ehdr->e_phentsize, phnum-- ) {
172  if ( phoff > image->len ) {
173  DBGC ( image, "ELF %p program header %d outside "
174  "image\n", image, phnum );
175  return -ENOEXEC;
176  }
177  copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
178  if ( ( rc = elf_segment ( image, ehdr, &phdr, process,
179  entry, max ) ) != 0 )
180  return rc;
181  }
182 
183  /* Check for a valid execution address */
184  if ( ! *entry ) {
185  DBGC ( image, "ELF %p entry point %lx outside image\n",
186  image, ( ( unsigned long ) ehdr->e_entry ) );
187  return -ENOEXEC;
188  }
189 
190  return 0;
191 }
192 
193 /**
194  * Load ELF image into memory
195  *
196  * @v image ELF file
197  * @ret entry Entry point
198  * @ret max Maximum used address
199  * @ret rc Return status code
200  */
201 int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
202  static const uint8_t e_ident[] = {
203  [EI_MAG0] = ELFMAG0,
204  [EI_MAG1] = ELFMAG1,
205  [EI_MAG2] = ELFMAG2,
206  [EI_MAG3] = ELFMAG3,
207  [EI_CLASS] = ELFCLASS,
208  };
209  Elf_Ehdr ehdr;
210  int rc;
211 
212  /* Read ELF header */
213  copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
214  if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident,
215  sizeof ( e_ident ) ) != 0 ) {
216  DBGC ( image, "ELF %p has invalid signature\n", image );
217  return -ENOEXEC;
218  }
219 
220  /* Load ELF segments into memory */
221  if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment,
222  entry, max ) ) != 0 )
223  return rc;
224 
225  return 0;
226 }
A process.
Definition: process.h:17
Elf32_Addr p_paddr
Definition: elf.h:71
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
ELF header.
Definition: elf.h:25
userptr_t data
Raw file image.
Definition: image.h:41
#define max(x, y)
Definition: ath.h:39
#define PT_LOAD
Definition: elf.h:79
Error codes.
#define ELFMAG1
Definition: elf.h:53
#define ENOEXEC
Exec format error.
Definition: errno.h:519
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
static int elf_load_segment(struct image *image, Elf_Phdr *phdr, physaddr_t dest)
Load ELF segment into memory.
Definition: elf.c:51
#define DBGC(...)
Definition: compiler.h:505
Elf32_Half e_phentsize
Definition: elf.h:35
userptr_t phys_to_user(unsigned long phys_addr)
Convert physical address to user pointer.
uint32_t buffer
Buffer index (or NETVSC_RNDIS_NO_BUFFER)
Definition: netvsc.h:16
An executable image.
Definition: image.h:24
Access to external ("user") memory.
ELF image format.
Executable image segments.
#define ELFMAG2
Definition: elf.h:54
Elf32_Word p_memsz
Definition: elf.h:73
uint32_t userptr_t
A pointer to a user buffer.
Definition: libkir.h:159
int prep_segment(userptr_t segment, size_t filesz, size_t memsz)
Prepare segment for loading.
Definition: segment.c:60
int elf_load(struct image *image, physaddr_t *entry, physaddr_t *max)
Load ELF image into memory.
Definition: elf.c:201
Executable images.
Elf32_Addr p_vaddr
Definition: elf.h:70
Elf32_Word p_filesz
Definition: elf.h:72
Elf32_Off e_phoff
Definition: elf.h:31
unsigned char e_ident[EI_NIDENT]
Definition: elf.h:26
ELF headers.
#define EI_MAG2
Definition: elf.h:45
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
size_t len
Length of raw file image.
Definition: image.h:43
Elf32_Addr e_entry
Definition: elf.h:30
#define ELFCLASS
Definition: elf.h:20
unsigned char uint8_t
Definition: stdint.h:10
Elf32_Word p_type
Definition: elf.h:68
int elf_segments(struct image *image, Elf_Ehdr *ehdr, int(*process)(struct image *image, Elf_Phdr *phdr, physaddr_t dest), physaddr_t *entry, physaddr_t *max)
Process ELF segments.
Definition: elf.c:154
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
#define EI_CLASS
Definition: elf.h:47
Elf32_Off p_offset
Definition: elf.h:69
unsigned long physaddr_t
Definition: stdint.h:20
#define ELFMAG3
Definition: elf.h:55
static int elf_segment(struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, int(*process)(struct image *image, Elf_Phdr *phdr, physaddr_t dest), physaddr_t *entry, physaddr_t *max)
Process ELF segment.
Definition: elf.c:85
Elf32_Half e_phnum
Definition: elf.h:36
Elf32_Off Elf_Off
Definition: elf.h:19
if(len >=6 *4) __asm__ __volatile__("movsl" if(len >=5 *4) __asm__ __volatile__("movsl" if(len >=4 *4) __asm__ __volatile__("movsl" if(len >=3 *4) __asm__ __volatile__("movsl" if(len >=2 *4) __asm__ __volatile__("movsl" if(len >=1 *4) __asm__ __volatile__("movsl" if((len % 4) >=2) __asm__ __volatile__("movsw" if((len % 2) >=1) __asm__ __volatile__("movsb" return dest
Definition: string.h:150
uint32_t end
Ending offset.
Definition: netvsc.h:18
#define EI_MAG3
Definition: elf.h:46
#define ELFMAG0
Definition: elf.h:52
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:114
ELF program header.
Definition: elf.h:67
#define EI_MAG1
Definition: elf.h:44
#define EI_MAG0
Definition: elf.h:43
void memcpy_user(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len)
Copy data between user buffers.