4 * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
5 * 2009 coresystems GmbH
6 * written by Patrick Georgi <patrick.georgi@coresystems.de>
7 * Copyright (C) 2012 Google, Inc.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
28 #include "elfparsing.h"
32 /* Checks if program segment contains the ignored section */
33 static int is_phdr_ignored(Elf64_Phdr
*phdr
, Elf64_Shdr
*shdr
)
35 /* If no ignored section, return false. */
39 Elf64_Addr sh_start
= shdr
->sh_addr
;
40 Elf64_Addr sh_end
= shdr
->sh_addr
+ shdr
->sh_size
;
41 Elf64_Addr ph_start
= phdr
->p_vaddr
;
42 Elf64_Addr ph_end
= phdr
->p_vaddr
+ phdr
->p_memsz
;
44 /* Return true only if section occupies whole of segment. */
45 if ((sh_start
== ph_start
) && (sh_end
== ph_end
)) {
46 DEBUG("Ignoring program segment at 0x%" PRIx64
"\n", ph_start
);
50 /* If shdr intersects phdr at all, its a conflict */
51 if (((sh_start
>= ph_start
) && (sh_start
<= ph_end
)) ||
52 ((sh_end
>= ph_start
) && (sh_end
<= ph_end
))) {
53 ERROR("Conflicting sections in segment\n");
57 /* Program header doesn't need to be ignored. */
61 /* Find section header based on ignored section name */
62 static Elf64_Shdr
*find_ignored_section_header(struct parsed_elf
*pelf
,
63 const char *ignore_section
)
68 /* No section needs to be ignored */
69 if (ignore_section
== NULL
)
72 DEBUG("Section to be ignored: %s\n", ignore_section
);
74 /* Get pointer to string table */
75 shstrtab
= buffer_get(pelf
->strtabs
[pelf
->ehdr
.e_shstrndx
]);
77 for (i
= 0; i
< pelf
->ehdr
.e_shnum
; i
++) {
79 const char *section_name
;
81 shdr
= &pelf
->shdr
[i
];
82 section_name
= &shstrtab
[shdr
->sh_name
];
84 /* If section name matches ignored string, return shdr */
85 if (strcmp(section_name
, ignore_section
) == 0)
89 /* No section matches ignore string */
93 /* returns size of result, or -1 if error.
94 * Note that, with the new code, this function
95 * works for all elf files, not just the restricted set.
97 int parse_elf_to_stage(const struct buffer
*input
, struct buffer
*output
,
98 uint32_t arch
, comp_algo algo
, uint32_t *location
,
99 const char *ignore_section
)
101 struct parsed_elf pelf
;
104 Elf64_Shdr
*shdr_ignored
;
106 struct buffer outheader
;
111 uint32_t data_start
, data_end
, mem_end
;
113 comp_func_ptr compress
= compression_function(algo
);
117 DEBUG("start: parse_elf_to_stage(location=0x%x)\n", *location
);
119 int flags
= ELF_PARSE_PHDR
| ELF_PARSE_SHDR
| ELF_PARSE_STRTAB
;
121 if (parse_elf(input
, &pelf
, flags
)) {
122 ERROR("Couldn't parse ELF\n");
127 phdr
= &pelf
.phdr
[0];
129 /* Find the section header corresponding to ignored-section */
130 shdr_ignored
= find_ignored_section_header(&pelf
, ignore_section
);
132 if (ignore_section
&& (shdr_ignored
== NULL
))
133 WARN("Ignore section not found\n");
135 headers
= ehdr
->e_phnum
;
137 /* Ignore the program header containing ignored section */
138 for (i
= 0; i
< headers
; i
++) {
139 if (is_phdr_ignored(&phdr
[i
], shdr_ignored
))
140 phdr
[i
].p_type
= PT_NULL
;
147 for (i
= 0; i
< headers
; i
++) {
148 unsigned int start
, mend
, rend
;
150 if (phdr
[i
].p_type
!= PT_LOAD
)
153 /* Empty segments are never interesting */
154 if (phdr
[i
].p_memsz
== 0)
159 start
= phdr
[i
].p_paddr
;
161 mend
= start
+ phdr
[i
].p_memsz
;
162 rend
= start
+ phdr
[i
].p_filesz
;
164 if (start
< data_start
)
174 if (data_start
< *location
) {
175 data_start
= *location
;
178 if (data_end
<= data_start
) {
179 ERROR("data ends (%08lx) before it starts (%08lx). Make sure "
180 "the ELF file is correct and resides in ROM space.\n",
181 (unsigned long)data_end
, (unsigned long)data_start
);
185 /* allocate an intermediate buffer for the data */
186 buffer
= calloc(data_end
- data_start
, 1);
188 if (buffer
== NULL
) {
189 ERROR("Unable to allocate memory: %m\n");
193 /* Copy the file data into the buffer */
195 for (i
= 0; i
< headers
; i
++) {
196 unsigned int l_start
, l_offset
= 0;
198 if (phdr
[i
].p_type
!= PT_LOAD
)
201 if (phdr
[i
].p_memsz
== 0)
204 l_start
= phdr
[i
].p_paddr
;
205 if (l_start
< *location
) {
206 l_offset
= *location
- l_start
;
210 /* A legal ELF file can have a program header with
211 * non-zero length but zero-length file size and a
212 * non-zero offset which, added together, are > than
213 * input->size (i.e. the total file size). So we need
214 * to not even test in the case that p_filesz is zero.
216 if (! phdr
[i
].p_filesz
)
218 if (input
->size
< (phdr
[i
].p_offset
+ phdr
[i
].p_filesz
)){
219 ERROR("Underflow copying out the segment."
220 "File has %zu bytes left, segment end is %zu\n",
221 input
->size
, (size_t)(phdr
[i
].p_offset
+ phdr
[i
].p_filesz
));
225 memcpy(buffer
+ (l_start
- data_start
),
226 &input
->data
[phdr
[i
].p_offset
+ l_offset
],
227 phdr
[i
].p_filesz
- l_offset
);
230 /* Now make the output buffer */
231 if (buffer_create(output
, sizeof(struct cbfs_stage
) + data_end
- data_start
,
233 ERROR("Unable to allocate memory: %m\n");
237 memset(output
->data
, 0, output
->size
);
239 /* Compress the data, at which point we'll know information
240 * to fill out the header. This seems backward but it works because
241 * - the output header is a known size (not always true in many xdr's)
242 * - we do need to know the compressed output size first
243 * If compression fails or makes the data bigger, we'll warn about it
244 * and use the original data.
246 if (compress(buffer
, data_end
- data_start
,
247 (output
->data
+ sizeof(struct cbfs_stage
)),
248 &outlen
) < 0 || outlen
> data_end
- data_start
) {
249 WARN("Compression failed or would make the data bigger "
251 memcpy(output
->data
+ sizeof(struct cbfs_stage
),
252 buffer
, data_end
- data_start
);
253 algo
= CBFS_COMPRESS_NONE
;
257 /* Set up for output marshaling. */
258 outheader
.data
= output
->data
;
260 /* N.B. The original plan was that SELF data was B.E.
261 * but: this is all L.E.
262 * Maybe we should just change the spec.
264 xdr_le
.put32(&outheader
, algo
);
265 xdr_le
.put64(&outheader
, ehdr
->e_entry
);
266 xdr_le
.put64(&outheader
, data_start
);
267 xdr_le
.put32(&outheader
, outlen
);
268 xdr_le
.put32(&outheader
, mem_end
- data_start
);
271 *location
-= sizeof(struct cbfs_stage
);
272 output
->size
= sizeof(struct cbfs_stage
) + outlen
;
276 parsed_elf_destroy(&pelf
);