1 /******************************************************************************
2 * Copyright (c) 2004, 2011 IBM Corporation
3 * All rights reserved.
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
8 *
9 * Contributors:
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
12
13 /*
14 * ELF loader
15 */
16
17 #include <string.h>
18 #include <cache.h>
19 #include <libelf.h>
20 #include <byteorder.h>
21
22 /**
23 * elf_check_file tests if the file at file_addr is
24 * a correct endian, ELF PPC executable
25 * @param file_addr pointer to the start of the ELF file
26 * @return the class (1 for 32 bit, 2 for 64 bit)
27 * -1 if it is not an ELF file
28 * -2 if it has the wrong endianness
29 * -3 if it is not an ELF executable
30 * -4 if it is not for PPC
31 */
32 static int
elf_check_file(unsigned long * file_addr)33 elf_check_file(unsigned long *file_addr)
34 {
35 struct ehdr *ehdr = (struct ehdr *) file_addr;
36 uint8_t native_endian;
37
38 /* check if it is an ELF image at all */
39 if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
40 return -1;
41
42 #ifdef __BIG_ENDIAN__
43 native_endian = ELFDATA2MSB;
44 #else
45 native_endian = ELFDATA2LSB;
46 #endif
47
48 if (native_endian != ehdr->ei_data) {
49 switch (ehdr->ei_class) {
50 case 1:
51 elf_byteswap_header32(file_addr);
52 break;
53 case 2:
54 elf_byteswap_header64(file_addr);
55 break;
56 }
57 }
58
59 /* check if it is an ELF executable ... and also
60 * allow DYN files, since this is specified by ePAPR */
61 if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
62 return -3;
63
64 /* check if it is a PPC ELF executable */
65 if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15)
66 return -4;
67
68 return ehdr->ei_class;
69 }
70
71 /**
72 * load_elf_file tries to load the ELF file specified in file_addr
73 *
74 * it first checks if the file is a PPC ELF executable and then loads
75 * the segments depending if it is a 64bit or 32 bit ELF file
76 *
77 * @param file_addr pointer to the start of the elf file
78 * @param entry pointer where the ELF loader will store
79 * the entry point
80 * @param pre_load handler that is called before copying a segment
81 * @param post_load handler that is called after copying a segment
82 * @return 1 for a 32 bit file
83 * 2 for a 64 bit BE file
84 * 3 for a 64 bit LE ABIv1 file
85 * 4 for a 64 bit LE ABIv2 file
86 * 5 for a 32 bit LE ABIv1 file
87 * anything else means an error during load
88 */
89 int
elf_load_file(void * file_addr,unsigned long * entry,int (* pre_load)(void *,long),void (* post_load)(void *,long))90 elf_load_file(void *file_addr, unsigned long *entry,
91 int (*pre_load)(void*, long),
92 void (*post_load)(void*, long))
93 {
94 int type = elf_check_file(file_addr);
95 struct ehdr *ehdr = (struct ehdr *) file_addr;
96
97 switch (type) {
98 case 1:
99 *entry = elf_load_segments32(file_addr, 0, pre_load, post_load);
100 if (ehdr->ei_data != ELFDATA2MSB) {
101 type = 5; /* LE32 ABIv1 */
102 }
103 break;
104 case 2:
105 *entry = elf_load_segments64(file_addr, 0, pre_load, post_load);
106 if (ehdr->ei_data != ELFDATA2MSB) {
107 uint32_t flags = elf_get_eflags_64(file_addr);
108 if ((flags & 0x3) == 2)
109 type = 4; /* LE64 ABIv2 */
110 else
111 type = 3; /* LE64 ABIv1 */
112 }
113 break;
114 }
115 if (*entry == 0)
116 type = 0;
117
118 return type;
119 }
120
121
122 /**
123 * load_elf_file_to_addr loads an ELF file to given address.
124 * This is useful for 64-bit vmlinux images that use the virtual entry
125 * point address in their headers, and thereby need a special treatment.
126 *
127 * @param file_addr pointer to the start of the elf file
128 * @param entry pointer where the ELF loader will store
129 * the entry point
130 * @param pre_load handler that is called before copying a segment
131 * @param post_load handler that is called after copying a segment
132 * @return 1 for a 32 bit file
133 * 2 for a 64 bit file
134 * anything else means an error during load
135 */
136 int
elf_load_file_to_addr(void * file_addr,void * addr,unsigned long * entry,int (* pre_load)(void *,long),void (* post_load)(void *,long))137 elf_load_file_to_addr(void *file_addr, void *addr, unsigned long *entry,
138 int (*pre_load)(void*, long),
139 void (*post_load)(void*, long))
140 {
141 int type;
142 long offset;
143 struct ehdr *ehdr = (struct ehdr *) file_addr;
144
145 type = elf_check_file(file_addr);
146
147 switch (type) {
148 case 1:
149 /* Parse 32-bit image */
150 offset = (long)addr - elf_get_base_addr32(file_addr);
151 *entry = elf_load_segments32(file_addr, offset, pre_load,
152 post_load) + offset;
153 // TODO: elf_relocate32(...)
154 break;
155 case 2:
156 /* Parse 64-bit image */
157 offset = (long)addr - elf_get_base_addr64(file_addr);
158 *entry = elf_load_segments64(file_addr, offset, pre_load,
159 post_load) + offset;
160 elf_relocate64(file_addr, offset);
161 if (ehdr->ei_data != ELFDATA2MSB) {
162 uint32_t flags = elf_get_eflags_64(file_addr);
163 if ((flags & 0x3) == 2)
164 type = 4; /* LE64 ABIv2 */
165 else
166 type = 3; /* LE64 ABIv1 */
167 }
168 break;
169 }
170
171 return type;
172 }
173
174
175 /**
176 * Get the base load address of the ELF image
177 * @return The base address or -1 for error
178 */
179 long
elf_get_base_addr(void * file_addr)180 elf_get_base_addr(void *file_addr)
181 {
182 int type;
183
184 type = elf_check_file(file_addr);
185
186 switch (type) {
187 case 1:
188 /* Return 32-bit image base address */
189 return elf_get_base_addr32(file_addr);
190 break;
191 case 2:
192 /* Return 64-bit image base address */
193 return elf_get_base_addr64(file_addr);
194 break;
195 }
196
197 return -1;
198 }
199
200 /**
201 * Get the file size of the ELF image that has been loaded into a
202 * buffer larger than the size of the file
203 * @return The size of the ELF image or < 0 for error
204 */
elf_get_file_size(const void * buffer,const unsigned long buffer_size)205 long elf_get_file_size(const void *buffer, const unsigned long buffer_size)
206 {
207 const struct ehdr *ehdr = (const struct ehdr *)buffer;
208
209 if (buffer_size < sizeof(struct ehdr))
210 return -1;
211
212 /* check if it is an ELF image at all */
213 if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
214 return -1;
215
216 switch (ehdr->ei_class) {
217 case 1:
218 return elf_get_file_size32(buffer, buffer_size);
219 case 2:
220 return elf_get_file_size64(buffer, buffer_size);
221 }
222
223 return -1;
224 }
225