Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libelf / elf.c
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
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
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
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
144         type = elf_check_file(file_addr);
145
146         switch (type) {
147         case 1:
148                 /* Parse 32-bit image */
149                 offset = (long)addr - elf_get_base_addr32(file_addr);
150                 *entry = elf_load_segments32(file_addr, offset, pre_load,
151                                              post_load) + offset;
152                 // TODO: elf_relocate32(...)
153                 break;
154         case 2:
155                 /* Parse 64-bit image */
156                 offset = (long)addr - elf_get_base_addr64(file_addr);
157                 *entry = elf_load_segments64(file_addr, offset, pre_load,
158                                              post_load) + offset;
159                 elf_relocate64(file_addr, offset);
160                 break;
161         }
162
163         return type;
164 }
165
166
167 /**
168  * Get the base load address of the ELF image
169  * @return  The base address or -1 for error
170  */
171 long
172 elf_get_base_addr(void *file_addr)
173 {
174         int type;
175
176         type = elf_check_file(file_addr);
177
178         switch (type) {
179         case 1:
180                 /* Return 32-bit image base address */
181                 return elf_get_base_addr32(file_addr);
182                 break;
183         case 2:
184                 /* Return 64-bit image base address */
185                 return elf_get_base_addr64(file_addr);
186                 break;
187         }
188
189         return -1;
190 }