diff options
author | Ronald G. Minnich <rminnich@google.com> | 2013-12-30 13:16:18 -0800 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2014-02-02 20:18:55 +0100 |
commit | a8a133ded34d82a7baa9a439969eae780a501992 (patch) | |
tree | ef64f06102d378795ea3b84920e6fba9f4a424eb /util/cbfstool/cbfs-mkstage.c | |
parent | 25fc8d181fde674fb35ad56c841ffb3b4b0489e9 (diff) | |
download | coreboot-a8a133ded34d82a7baa9a439969eae780a501992.tar.gz coreboot-a8a133ded34d82a7baa9a439969eae780a501992.tar.bz2 coreboot-a8a133ded34d82a7baa9a439969eae780a501992.zip |
Add section header parsing and use it in the mk-payload step
This completes the improvements to the ELF file parsing code. We can
now parse section headers too, across all 4 combinations of word size
and endianness. I had hoped to completely remove the use of htonl
until I found it in cbfs_image.c. That's a battle for another day.
There's now a handy macro to create magic numbers in host byte order.
I'm using it for all the PAYLOAD_SEGMENT_* constants and maybe
we can use it for the others too, but this is sensitive code and
I'd rather change one thing at a time.
To maximize the ease of use for users, elf parsing is accomplished with
just one function:
int
elf_headers(const struct buffer *pinput,
Elf64_Ehdr *ehdr,
Elf64_Phdr **pphdr,
Elf64_Shdr **pshdr)
which requires the ehdr and pphdr pointers to be non-NULL, but allows
the pshdr to be NULL. If pshdr is NULL, the code will not try to read
in section headers.
To satisfy our powerful scripts, I had to remove the ^M from an unrelated
microcode file.
BUG=None
TEST=Build a peppy image (known to boot) with old and new versions and verify they are bit-for-bit the same. This was also fully tested across all chromebooks for building and booting and running chromeos.
BRANCH=None
Change-Id: I54dad887d922428b6175fdb6a9cdfadd8a6bb889
Signed-off-by: Ronald G. Minnich <rminnich@google.com>
Reviewed-on: https://chromium-review.googlesource.com/181272
Reviewed-by: Ronald Minnich <rminnich@chromium.org>
Commit-Queue: Ronald Minnich <rminnich@chromium.org>
Tested-by: Ronald Minnich <rminnich@chromium.org>
Signed-off-by: Ronald G. Minnich <rminnich@google.com>
Reviewed-on: http://review.coreboot.org/5098
Tested-by: build bot (Jenkins)
Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Diffstat (limited to 'util/cbfstool/cbfs-mkstage.c')
-rw-r--r-- | util/cbfstool/cbfs-mkstage.c | 241 |
1 files changed, 2 insertions, 239 deletions
diff --git a/util/cbfstool/cbfs-mkstage.c b/util/cbfstool/cbfs-mkstage.c index 5c8014f072a4..6a5f6f74ca6e 100644 --- a/util/cbfstool/cbfs-mkstage.c +++ b/util/cbfstool/cbfs-mkstage.c @@ -24,246 +24,9 @@ #include <stdlib.h> #include <string.h> +#include "elf.h" #include "common.h" #include "cbfs.h" -#include "elf.h" - -/* - * Short form: this is complicated, but we've tried making it simple - * and we keep hitting problems with our ELF parsing. - * - * The ELF parsing situation has always been a bit tricky. In fact, - * we (and most others) have been getting it wrong in small ways for - * years. Recently this has caused real trouble for the ARM V8 build. - * In this file we attempt to finally get it right for all variations - * of endian-ness and word size and target architectures and - * architectures we might get run on. Phew!. To do this we borrow a - * page from the FreeBSD NFS xdr model (see elf_ehdr and elf_phdr), - * the Plan 9 endianness functions (see xdr.c), and Go interfaces (see - * how we use buffer structs in this file). This ends up being a bit - * wordy at the lowest level, but greatly simplifies the elf parsing - * code and removes a common source of bugs, namely, forgetting to - * flip type endianness when referencing a struct member. - * - * ELF files can have four combinations of data layout: 32/64, and - * big/little endian. Further, to add to the fun, depending on the - * word size, the size of the ELF structs varies. The coreboot SELF - * format is simpler in theory: it's supposed to be always BE, and the - * various struct members allow room for growth: the entry point is - * always 64 bits, for example, so the size of a SELF struct is - * constant, regardless of target architecture word size. Hence, we - * need to do some transformation of the ELF files. - * - * A given architecture, realistically, only supports one of the four - * combinations at a time as the 'native' format. Hence, our code has - * been sprinkled with every variation of [nh]to[hn][sll] over the - * years. We've never quite gotten it all right, however, and a quick - * pass over this code revealed another bug. It's all worked because, - * until now, all the working platforms that had CBFS were 32 LE. Even then, - * however, bugs crept in: we recently realized that we're not - * transforming the entry point to big format when we store into the - * SELF image. - * - * The problem is essentially an XDR operation: - * we have something in a foreign format and need to transform it. - * It's most like XDR because: - * 1) the byte order can be wrong - * 2) the word size can be wrong - * 3) the size of elements in the stream depends on the value - * of other elements in the stream - * it's not like XDR because: - * 1) the byte order can be right - * 2) the word size can be right - * 3) the struct members are all on a natural alignment - * - * Hence, this new approach. To cover word size issues, we *always* - * transform the two structs we care about, the file header and - * program header, into a native struct in the 64 bit format: - * - * [32,little] -> [Elf64_Ehdr, Elf64_Phdr] - * [64,little] -> [Elf64_Ehdr, Elf64_Phdr] - * [32,big] -> [Elf64_Ehdr, Elf64_Phdr] - * [64,big] -> [Elf64_Ehdr, Elf64_Phdr] - * Then we just use those structs, and all the need for inline ntoh* goes away, - * as well as all the chances for error. - * This works because all the SELF structs have fields large enough for - * the largest ELF 64 struct members, and all the Elf64 struct members - * are at least large enough for all ELF 32 struct members. - * We end up with one function to do all our ELF parsing, and two functions - * to transform the headers. For the put case, we also have - * XDR functions, and hopefully we'll never again spend 5 years with the - * wrong endian-ness on an output value :-) - * This should work for all word sizes and endianness we hope to target. - * I *really* don't want to be here for 128 bit addresses. - * - * The parse functions are called with a pointer to an input buffer - * struct. One might ask: are there enough bytes in the input buffer? - * We know there need to be at *least* sizeof(Elf32_Ehdr) + - * sizeof(Elf32_Phdr) bytes. Realistically, there has to be some data - * too. If we start to worry, though we have not in the past, we - * might apply the simple test: the input buffer needs to be at least - * sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) bytes because, even if it's - * ELF 32, there's got to be *some* data! This is not theoretically - * accurate but it is actually good enough in practice. It allows the - * header transformation code to ignore the possibility of underrun. - * - * We also must accomodate different ELF files, and hence formats, - * in the same cbfs invocation. We might load a 64-bit payload - * on a 32-bit machine; we might even have a mixed armv7/armv8 - * SOC or even a system with an x86/ARM! - * - * A possibly problematic (though unlikely to be so) assumption - * is that we expect the BIOS to remain in the lowest 32 bits - * of the physical address space. Since ARMV8 has standardized - * on that, and x86_64 also has, this seems a safe assumption. - * - * To repeat, ELF structs are different sizes because ELF struct - * members are different sizes, depending on values in the ELF file - * header. For this we use the functions defined in xdr.c, which - * consume bytes, convert the endianness, and advance the data pointer - * in the buffer struct. - */ - -/* Get the ident array, so we can figure out - * endian-ness, word size, and in future other useful - * parameters - */ -static void -elf_eident(struct buffer *input, Elf64_Ehdr *ehdr) -{ - memmove(ehdr->e_ident, input->data, sizeof(ehdr->e_ident)); - input->data += sizeof(ehdr->e_ident); - input->size -= sizeof(ehdr->e_ident); -} - - -static void -elf_ehdr(struct buffer *input, Elf64_Ehdr *ehdr, struct xdr *xdr, int bit64) -{ - ehdr->e_type = xdr->get16(input); - ehdr->e_machine = xdr->get16(input); - ehdr->e_version = xdr->get32(input); - if (bit64){ - ehdr->e_entry = xdr->get64(input); - ehdr->e_phoff = xdr->get64(input); - ehdr->e_shoff = xdr->get64(input); - } else { - ehdr->e_entry = xdr->get32(input); - ehdr->e_phoff = xdr->get32(input); - ehdr->e_shoff = xdr->get32(input); - } - ehdr->e_flags = xdr->get32(input); - ehdr->e_ehsize = xdr->get16(input); - ehdr->e_phentsize = xdr->get16(input); - ehdr->e_phnum = xdr->get16(input); - ehdr->e_shentsize = xdr->get16(input); - ehdr->e_shnum = xdr->get16(input); - ehdr->e_shstrndx = xdr->get16(input); -} - -static void -elf_phdr(struct buffer *pinput, Elf64_Phdr *phdr, - int entsize, struct xdr *xdr, int bit64) -{ - /* - * The entsize need not be sizeof(*phdr). - * Hence, it is easier to keep a copy of the input, - * as the xdr functions may not advance the input - * pointer the full entsize; rather than get tricky - * we just advance it below. - */ - struct buffer input = *pinput; - if (bit64){ - phdr->p_type = xdr->get32(&input); - phdr->p_flags = xdr->get32(&input); - phdr->p_offset = xdr->get64(&input); - phdr->p_vaddr = xdr->get64(&input); - phdr->p_paddr = xdr->get64(&input); - phdr->p_filesz = xdr->get64(&input); - phdr->p_memsz = xdr->get64(&input); - phdr->p_align = xdr->get64(&input); - } else { - phdr->p_type = xdr->get32(&input); - phdr->p_offset = xdr->get32(&input); - phdr->p_vaddr = xdr->get32(&input); - phdr->p_paddr = xdr->get32(&input); - phdr->p_filesz = xdr->get32(&input); - phdr->p_memsz = xdr->get32(&input); - phdr->p_flags = xdr->get32(&input); - phdr->p_align = xdr->get32(&input); - } - pinput->size -= entsize; - pinput->data += entsize; -} - -/* Get the headers from the buffer. - * Return -1 in the event of an error. - */ -static int -elf_headers(const struct buffer *pinput, Elf64_Ehdr *ehdr, Elf64_Phdr **pphdr) -{ - int i; - struct xdr *xdr = &xdr_le; - int bit64 = 0; - struct buffer input = *(struct buffer *)pinput; - struct buffer phdr_buf; - Elf64_Phdr *phdr; - - if (!iself((unsigned char *)pinput->data)) { - ERROR("The stage file is not in ELF format!\n"); - return -1; - } - - elf_eident(&input, ehdr); - bit64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64; - /* Assume LE unless we are sure otherwise. - * We're not going to take on the task of - * fully validating the ELF file. That way - * lies madness. - */ - if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) - xdr = &xdr_be; - - elf_ehdr(&input, ehdr, xdr, bit64); - - // The tool may work in architecture-independent way. - if (arch != CBFS_ARCHITECTURE_UNKNOWN && - !((ehdr->e_machine == EM_ARM) && (arch == CBFS_ARCHITECTURE_ARMV7)) && - !((ehdr->e_machine == EM_386) && (arch == CBFS_ARCHITECTURE_X86))) { - ERROR("The stage file has the wrong architecture\n"); - return -1; - } - - if (pinput->size < ehdr->e_phoff){ - ERROR("The program header offset is greater than " - "the remaining file size." - "%ld bytes left, program header offset is %ld \n", - pinput->size, ehdr->e_phoff); - return -1; - } - /* cons up an input buffer for the headers. - * Note that the program headers can be anywhere, - * per the ELF spec, You'd be surprised how many ELF - * readers miss this little detail. - */ - phdr_buf.data = &pinput->data[ehdr->e_phoff]; - phdr_buf.size = ehdr->e_phentsize * ehdr->e_phnum; - if (phdr_buf.size > (pinput->size - ehdr->e_phoff)){ - ERROR("The file is not large enough for the program headers." - "%ld bytes left, %ld bytes of headers\n", - pinput->size - ehdr->e_phoff, phdr_buf.size); - return -1; - } - /* gather up all the phdrs. - * We do them all at once because there is more - * than one loop over all the phdrs. - */ - phdr = calloc(sizeof(*phdr), ehdr->e_phnum); - for (i = 0; i < ehdr->e_phnum; i++) - elf_phdr(&phdr_buf, &phdr[i], ehdr->e_phentsize, xdr, bit64); - *pphdr = phdr; - return 0; -} /* returns size of result, or -1 if error. * Note that, with the new code, this function @@ -287,7 +50,7 @@ int parse_elf_to_stage(const struct buffer *input, struct buffer *output, DEBUG("start: parse_elf_to_stage(location=0x%x)\n", *location); - if (elf_headers(input, &ehdr, &phdr) < 0) + if (elf_headers(input, &ehdr, &phdr, NULL) < 0) return -1; headers = ehdr.e_phnum; |