From a8a133ded34d82a7baa9a439969eae780a501992 Mon Sep 17 00:00:00 2001 From: "Ronald G. Minnich" Date: Mon, 30 Dec 2013 13:16:18 -0800 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/181272 Reviewed-by: Ronald Minnich Commit-Queue: Ronald Minnich Tested-by: Ronald Minnich Signed-off-by: Ronald G. Minnich Reviewed-on: http://review.coreboot.org/5098 Tested-by: build bot (Jenkins) Reviewed-by: Alexandru Gagniuc --- util/cbfstool/cbfs-mkpayload.c | 166 ++++++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 77 deletions(-) (limited to 'util/cbfstool/cbfs-mkpayload.c') diff --git a/util/cbfstool/cbfs-mkpayload.c b/util/cbfstool/cbfs-mkpayload.c index 78988a0182..b1dd1c027c 100644 --- a/util/cbfstool/cbfs-mkpayload.c +++ b/util/cbfstool/cbfs-mkpayload.c @@ -23,18 +23,39 @@ #include #include +#include "elf.h" #include "common.h" #include "cbfs.h" -#include "elf.h" #include "fv.h" #include "coff.h" +/* serialize the seg array into the buffer. + * The buffer is assumed to be large enough. + */ +static void xdr_segs(struct buffer *output, + struct cbfs_payload_segment *segs, int nseg) +{ + struct buffer outheader; + int i; + + outheader.data = output->data; + outheader.size = 0; + + for(i = 0; i < nseg; i++){ + xdr_be.put32(&outheader, segs[i].type); + xdr_be.put32(&outheader, segs[i].compression); + xdr_be.put32(&outheader, segs[i].offset); + xdr_be.put64(&outheader, segs[i].load_addr); + xdr_be.put32(&outheader, segs[i].len); + xdr_be.put32(&outheader, segs[i].mem_len); + } +} int parse_elf_to_payload(const struct buffer *input, struct buffer *output, comp_algo algo) { - Elf32_Phdr *phdr; - Elf32_Ehdr *ehdr = (Elf32_Ehdr *)input->data; - Elf32_Shdr *shdr; + Elf64_Phdr *phdr; + Elf64_Ehdr ehdr; + Elf64_Shdr *shdr; char *header; char *strtab; int headers; @@ -44,39 +65,26 @@ int parse_elf_to_payload(const struct buffer *input, struct cbfs_payload_segment *segs; int i; - if(!iself((unsigned char *)input->data)){ - INFO("The payload file is not in ELF format!\n"); - return -1; - } - - // 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 payload file has the wrong architecture\n"); - return -1; - } - comp_func_ptr compress = compression_function(algo); if (!compress) return -1; - DEBUG("start: parse_elf_to_payload\n"); - headers = ehdr->e_phnum; - header = (char *)ehdr; + if (elf_headers(input, &ehdr, &phdr, &shdr) < 0) + return -1; - phdr = (Elf32_Phdr *) & (header[ehdr->e_phoff]); - shdr = (Elf32_Shdr *) & (header[ehdr->e_shoff]); + DEBUG("start: parse_elf_to_payload\n"); + headers = ehdr.e_phnum; + header = input->data; - strtab = &header[shdr[ehdr->e_shstrndx].sh_offset]; + strtab = &header[shdr[ehdr.e_shstrndx].sh_offset]; /* Count the number of headers - look for the .notes.pinfo * section */ - for (i = 0; i < ehdr->e_shnum; i++) { + for (i = 0; i < ehdr.e_shnum; i++) { char *name; - if (i == ehdr->e_shstrndx) + if (i == ehdr.e_shstrndx) continue; if (shdr[i].sh_size == 0) @@ -106,29 +114,39 @@ int parse_elf_to_payload(const struct buffer *input, segments++; } - + /* allocate the segment header array */ + segs = calloc(segments, sizeof(*segs)); + if (segs == NULL) + return -1; /* Allocate a block of memory to store the data in */ if (buffer_create(output, (segments * sizeof(*segs)) + isize, input->name) != 0) return -1; memset(output->data, 0, output->size); - doffset = (segments * sizeof(struct cbfs_payload_segment)); - - segs = (struct cbfs_payload_segment *)output->data; + doffset = (segments * sizeof(*segs)); + + /* set up for output marshaling. This is a bit + * tricky as we are marshaling the headers at the front, + * and the data starting after the headers. We need to convert + * the headers to the right format but the data + * passes through unchanged. Unlike most XDR code, + * we are doing these two concurrently. The doffset is + * used to compute the address for the raw data, and the + * outheader is used to marshal the headers. To make it simpler + * for The Reader, we set up the headers in a separate array, + * then marshal them all at once to the output. + */ segments = 0; - for (i = 0; i < ehdr->e_shnum; i++) { + for (i = 0; i < ehdr.e_shnum; i++) { char *name; - - if (i == ehdr->e_shstrndx) + if (i == ehdr.e_shstrndx) continue; if (shdr[i].sh_size == 0) continue; - name = (char *)(strtab + shdr[i].sh_name); - if (!strcmp(name, ".note.pinfo")) { segs[segments].type = PAYLOAD_SEGMENT_PARAMS; segs[segments].load_addr = 0; @@ -148,17 +166,13 @@ int parse_elf_to_payload(const struct buffer *input, for (i = 0; i < headers; i++) { if (phdr[i].p_type != PT_LOAD) continue; - if (phdr[i].p_memsz == 0) continue; - if (phdr[i].p_filesz == 0) { segs[segments].type = PAYLOAD_SEGMENT_BSS; - segs[segments].load_addr = - (uint64_t)htonll(phdr[i].p_paddr); - segs[segments].mem_len = - (uint32_t)htonl(phdr[i].p_memsz); - segs[segments].offset = htonl(doffset); + segs[segments].load_addr = phdr[i].p_paddr; + segs[segments].mem_len = phdr[i].p_memsz; + segs[segments].offset = doffset; segments++; continue; @@ -168,37 +182,37 @@ int parse_elf_to_payload(const struct buffer *input, segs[segments].type = PAYLOAD_SEGMENT_CODE; else segs[segments].type = PAYLOAD_SEGMENT_DATA; - segs[segments].load_addr = (uint64_t)htonll(phdr[i].p_paddr); - segs[segments].mem_len = (uint32_t)htonl(phdr[i].p_memsz); - segs[segments].compression = htonl(algo); - segs[segments].offset = htonl(doffset); + segs[segments].load_addr = phdr[i].p_paddr; + segs[segments].mem_len = phdr[i].p_memsz; + segs[segments].compression = algo; + segs[segments].offset = doffset; int len; compress((char *)&header[phdr[i].p_offset], phdr[i].p_filesz, output->data + doffset, &len); - segs[segments].len = htonl(len); + segs[segments].len = len; /* If the compressed section is larger, then use the original stuff */ if ((unsigned int)len > phdr[i].p_filesz) { segs[segments].compression = 0; - segs[segments].len = htonl(phdr[i].p_filesz); - + segs[segments].len = phdr[i].p_filesz; memcpy(output->data + doffset, &header[phdr[i].p_offset], phdr[i].p_filesz); } - doffset += ntohl(segs[segments].len); - osize += ntohl(segs[segments].len); + doffset += segs[segments].len; + osize += segs[segments].len; segments++; } segs[segments].type = PAYLOAD_SEGMENT_ENTRY; - segs[segments++].load_addr = htonll(ehdr->e_entry); + segs[segments++].load_addr = ehdr.e_entry; - output->size = (segments * sizeof(struct cbfs_payload_segment)) + osize; + output->size = (segments * sizeof(*segs)) + osize; + xdr_segs(output, segs, segments); return 0; } @@ -209,7 +223,7 @@ int parse_flat_binary_to_payload(const struct buffer *input, comp_algo algo) { comp_func_ptr compress; - struct cbfs_payload_segment *segs; + struct cbfs_payload_segment segs[2]; int doffset, len = 0; compress = compression_function(algo); @@ -217,36 +231,35 @@ int parse_flat_binary_to_payload(const struct buffer *input, return -1; DEBUG("start: parse_flat_binary_to_payload\n"); - if (buffer_create(output, (2 * sizeof(*segs) + input->size), + if (buffer_create(output, (sizeof(segs) + input->size), input->name) != 0) return -1; memset(output->data, 0, output->size); - segs = (struct cbfs_payload_segment *)output->data; doffset = (2 * sizeof(*segs)); /* Prepare code segment */ segs[0].type = PAYLOAD_SEGMENT_CODE; - segs[0].load_addr = htonll(loadaddress); - segs[0].mem_len = htonl(input->size); - segs[0].offset = htonl(doffset); + segs[0].load_addr = loadaddress; + segs[0].mem_len = input->size; + segs[0].offset = doffset; compress(input->data, input->size, output->data + doffset, &len); - segs[0].compression = htonl(algo); - segs[0].len = htonl(len); + segs[0].compression = algo; + segs[0].len = len; if ((unsigned int)len >= input->size) { WARN("Compressing data would make it bigger - disabled.\n"); segs[0].compression = 0; - segs[0].len = htonl(input->size); + segs[0].len = input->size; memcpy(output->data + doffset, input->data, input->size); } /* prepare entry point segment */ segs[1].type = PAYLOAD_SEGMENT_ENTRY; - segs[1].load_addr = htonll(entrypoint); - output->size = doffset + ntohl(segs[0].len); - + segs[1].load_addr = entrypoint; + output->size = doffset + segs[0].len; + xdr_segs(output, segs, 2); return 0; } @@ -254,7 +267,7 @@ int parse_fv_to_payload(const struct buffer *input, struct buffer *output, comp_algo algo) { comp_func_ptr compress; - struct cbfs_payload_segment *segs; + struct cbfs_payload_segment segs[2]; int doffset, len = 0; firmware_volume_header_t *fv; ffs_file_header_t *fh; @@ -343,37 +356,36 @@ int parse_fv_to_payload(const struct buffer *input, return -1; } - if (buffer_create(output, (2 * sizeof(*segs) + input->size), + if (buffer_create(output, (sizeof(segs) + input->size), input->name) != 0) return -1; memset(output->data, 0, output->size); - segs = (struct cbfs_payload_segment *)output->data; - doffset = (2 * sizeof(*segs)); + doffset = (sizeof(segs)); /* Prepare code segment */ segs[0].type = PAYLOAD_SEGMENT_CODE; - segs[0].load_addr = htonll(loadaddress); - segs[0].mem_len = htonl(input->size); - segs[0].offset = htonl(doffset); + segs[0].load_addr = loadaddress; + segs[0].mem_len = input->size; + segs[0].offset = doffset; compress(input->data, input->size, output->data + doffset, &len); - segs[0].compression = htonl(algo); - segs[0].len = htonl(len); + segs[0].compression = algo; + segs[0].len = len; if ((unsigned int)len >= input->size) { WARN("Compressing data would make it bigger - disabled.\n"); segs[0].compression = 0; - segs[0].len = htonl(input->size); + segs[0].len = input->size; memcpy(output->data + doffset, input->data, input->size); } /* prepare entry point segment */ segs[1].type = PAYLOAD_SEGMENT_ENTRY; - segs[1].load_addr = htonll(entrypoint); - output->size = doffset + ntohl(segs[0].len); - + segs[1].load_addr = entrypoint; + output->size = doffset + segs[0].len; + xdr_segs(output, segs, 2); return 0; } -- cgit v1.2.3