| /* |
| * Copyright (c) 2017, Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> |
| * Keyon Jie <yang.jie@linux.intel.com> |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include "rimage.h" |
| #include "cse.h" |
| #include "manifest.h" |
| |
| static int elf_read_sections(struct image *image, struct module *module) |
| { |
| Elf32_Ehdr *hdr = &module->hdr; |
| Elf32_Shdr *section = module->section; |
| size_t count; |
| int i, ret; |
| uint32_t valid = (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR); |
| int man_section_idx; |
| |
| /* read in section header */ |
| ret = fseek(module->fd, hdr->e_shoff, SEEK_SET); |
| if (ret < 0) { |
| fprintf(stderr, "error: can't seek to %s section header %d\n", |
| module->elf_file, ret); |
| return ret; |
| } |
| |
| /* allocate space for each section header */ |
| section = calloc(sizeof(Elf32_Shdr), hdr->e_shnum); |
| if (section == NULL) |
| return -ENOMEM; |
| module->section = section; |
| |
| /* read in sections */ |
| count = fread(section, sizeof(Elf32_Shdr), hdr->e_shnum, module->fd); |
| if (count != hdr->e_shnum) { |
| fprintf(stderr, "error: failed to read %s section header %d\n", |
| module->elf_file, -errno); |
| return -errno; |
| } |
| |
| /* read in strings */ |
| module->strings = calloc(1, section[hdr->e_shstrndx].sh_size); |
| if (!module->strings) { |
| fprintf(stderr, "error: failed %s to read ELF strings for %d\n", |
| module->elf_file, -errno); |
| return -errno; |
| } |
| |
| ret = fseek(module->fd, section[hdr->e_shstrndx].sh_offset, SEEK_SET); |
| if (ret < 0) { |
| fprintf(stderr, "error: can't seek to %s stringss %d\n", |
| module->elf_file, ret); |
| return ret; |
| } |
| |
| count = fread(module->strings, 1, section[hdr->e_shstrndx].sh_size, |
| module->fd); |
| if (count != section[hdr->e_shstrndx].sh_size) { |
| fprintf(stderr, "error: failed to read %s strings %d\n", |
| module->elf_file, -errno); |
| return -errno; |
| } |
| |
| /* find manifest module data */ |
| man_section_idx = elf_find_section(image, module, ".bss"); |
| if (man_section_idx < 0) { |
| /* no bss - it is OK for boot_ldr */ |
| module->bss_start = 0; |
| module->bss_end = 0; |
| } else { |
| module->bss_index = man_section_idx; |
| } |
| |
| fprintf(stdout, " BSS module metadata section at index %d\n", |
| man_section_idx); |
| |
| /* parse each section */ |
| for (i = 0; i < hdr->e_shnum; i++) { |
| |
| /* only write valid sections */ |
| if (!(section[i].sh_flags & valid)) |
| continue; |
| |
| switch (section[i].sh_type) { |
| case SHT_NOBITS: |
| /* bss */ |
| module->bss_size += section[i].sh_size; |
| module->num_bss++; |
| break; |
| case SHT_PROGBITS: |
| /* text or data */ |
| module->fw_size += section[i].sh_size; |
| |
| if (section[i].sh_flags & SHF_EXECINSTR) |
| module->text_size += section[i].sh_size; |
| else |
| module->data_size += section[i].sh_size; |
| break; |
| default: |
| continue; |
| } |
| |
| module->num_sections++; |
| |
| if (!image->verbose) |
| continue; |
| |
| fprintf(stdout, " %s section-%d: \ttype\t 0x%8.8x\n", module->elf_file, |
| i, section[i].sh_type); |
| fprintf(stdout, " %s section-%d: \tflags\t 0x%8.8x\n", module->elf_file, |
| i, section[i].sh_flags); |
| fprintf(stdout, " %s section-%d: \taddr\t 0x%8.8x\n", module->elf_file, |
| i, section[i].sh_addr); |
| fprintf(stdout, " %s section-%d: \toffset\t 0x%8.8x\n", module->elf_file, |
| i, section[i].sh_offset); |
| fprintf(stdout, " %s section-%d: \tsize\t 0x%8.8x\n", module->elf_file, |
| i, section[i].sh_size); |
| fprintf(stdout, " %s section-%d: \tlink\t 0x%8.8x\n", module->elf_file, |
| i, section[i].sh_link); |
| fprintf(stdout, " %s section-%d: \tinfo\t 0x%8.8x\n\n", module->elf_file, |
| i, section[i].sh_info); |
| } |
| |
| return 0; |
| } |
| |
| static int elf_read_programs(struct image *image, struct module *module) |
| { |
| Elf32_Ehdr *hdr = &module->hdr; |
| Elf32_Phdr *prg = module->prg; |
| size_t count; |
| int i, ret; |
| |
| /* read in program header */ |
| ret = fseek(module->fd, hdr->e_phoff, SEEK_SET); |
| if (ret < 0) { |
| fprintf(stderr, "error: cant seek to %s program header %d\n", |
| module->elf_file, ret); |
| return ret; |
| } |
| |
| /* allocate space for programs */ |
| prg = calloc(sizeof(Elf32_Phdr), hdr->e_phnum); |
| if (prg == NULL) |
| return -ENOMEM; |
| module->prg = prg; |
| |
| /* read in programs */ |
| count = fread(prg, sizeof(Elf32_Phdr), hdr->e_phnum, module->fd); |
| if (count != hdr->e_phnum) { |
| fprintf(stderr, "error: failed to read %s program header %d\n", |
| module->elf_file, -errno); |
| return -errno; |
| } |
| |
| /* check each program */ |
| for (i = 0; i < hdr->e_phnum; i++) { |
| |
| if (prg[i].p_filesz == 0) |
| continue; |
| |
| if (!image->verbose) |
| continue; |
| |
| fprintf(stdout, "%s program-%d: \ttype\t 0x%8.8x\n", |
| module->elf_file, i, prg[i].p_type); |
| fprintf(stdout, "%s program-%d: \toffset\t 0x%8.8x\n", |
| module->elf_file, i, prg[i].p_offset); |
| fprintf(stdout, "%s program-%d: \tvaddr\t 0x%8.8x\n", |
| module->elf_file, i, prg[i].p_vaddr); |
| fprintf(stdout, "%s program-%d: \tpaddr\t 0x%8.8x\n", |
| module->elf_file, i, prg[i].p_paddr); |
| fprintf(stdout, "%s program-%d: \tfsize\t 0x%8.8x\n", |
| module->elf_file, i, prg[i].p_filesz); |
| fprintf(stdout, "%s program-%d: \tmsize\t 0x%8.8x\n", |
| module->elf_file, i, prg[i].p_memsz); |
| fprintf(stdout, "%s program-%d: \tflags\t 0x%8.8x\n\n", |
| module->elf_file, i, prg[i].p_flags); |
| } |
| |
| return 0; |
| } |
| |
| static int elf_read_hdr(struct image *image, struct module *module) |
| { |
| Elf32_Ehdr *hdr = &module->hdr; |
| size_t count; |
| |
| /* read in elf header */ |
| count = fread(hdr, sizeof(*hdr), 1, module->fd); |
| if (count != 1) { |
| fprintf(stderr, "error: failed to read %s elf header %d\n", |
| module->elf_file, -errno); |
| return -errno; |
| } |
| |
| if (!image->verbose) |
| return 0; |
| |
| fprintf(stdout, "%s elf: \tentry point\t 0x%8.8x\n", |
| module->elf_file, hdr->e_entry); |
| fprintf(stdout, "%s elf: \tprogram offset\t 0x%8.8x\n", |
| module->elf_file, hdr->e_phoff); |
| fprintf(stdout, "%s elf: \tsection offset\t 0x%8.8x\n", |
| module->elf_file, hdr->e_shoff); |
| fprintf(stdout, "%s elf: \tprogram size\t 0x%8.8x\n", |
| module->elf_file, hdr->e_phentsize); |
| fprintf(stdout, "%s elf: \tprogram count\t 0x%8.8x\n", |
| module->elf_file, hdr->e_phnum); |
| fprintf(stdout, "%s elf: \tsection size\t 0x%8.8x\n", |
| module->elf_file, hdr->e_shentsize); |
| fprintf(stdout, "%s elf: \tsection count\t 0x%8.8x\n", |
| module->elf_file, hdr->e_shnum); |
| fprintf(stdout, "%s elf: \tstring index\t 0x%8.8x\n\n", |
| module->elf_file, hdr->e_shstrndx); |
| |
| return 0; |
| } |
| |
| int elf_is_rom(struct image *image, Elf32_Shdr *section) |
| { |
| uint32_t start, end; |
| |
| start = section->sh_addr; |
| end = section->sh_addr + section->sh_size; |
| |
| if (start < image->adsp->rom_base || |
| start > image->adsp->rom_base + image->adsp->rom_size) |
| return 0; |
| if (end < image->adsp->rom_base || |
| end > image->adsp->rom_base + image->adsp->rom_size) |
| return 0; |
| return 1; |
| } |
| |
| static void elf_module_size(struct image *image, struct module *module, |
| Elf32_Shdr *section, int index) |
| { |
| switch (section->sh_type) { |
| case SHT_PROGBITS: |
| /* text or data */ |
| if (section->sh_flags & SHF_EXECINSTR) { |
| /* text */ |
| if (module->text_start > section->sh_addr) |
| module->text_start = section->sh_addr; |
| if (module->text_end < section->sh_addr + section->sh_size) |
| module->text_end = section->sh_addr + section->sh_size; |
| |
| fprintf(stdout, "\tTEXT\t"); |
| } else { |
| /* initialized data, also calc the writable sections */ |
| if (module->data_start > section->sh_addr) |
| module->data_start = section->sh_addr; |
| if (module->data_end < section->sh_addr + section->sh_size) |
| module->data_end = section->sh_addr + section->sh_size; |
| |
| fprintf(stdout, "\tDATA\t"); |
| } |
| break; |
| case SHT_NOBITS: |
| /* bss */ |
| if (index == module->bss_index) { |
| /* updated the .bss segment */ |
| module->bss_start = section->sh_addr; |
| module->bss_end = section->sh_addr + section->sh_size; |
| fprintf(stdout, "\tBSS\t"); |
| } else { |
| fprintf(stdout, "\tHEAP\t"); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void elf_module_size_reloc(struct image *image, struct module *module, |
| Elf32_Shdr *section, int index) |
| { |
| switch (section->sh_type) { |
| case SHT_PROGBITS: |
| /* text or data */ |
| if (section->sh_flags & SHF_EXECINSTR) { |
| /* text */ |
| module->text_start = 0; |
| module->text_end += section->sh_size; |
| |
| fprintf(stdout, "\tTEXT\t"); |
| } else { |
| /* initialized data, also calc the writable sections */ |
| module->data_start = 0; |
| module->data_end += section->sh_size; |
| |
| fprintf(stdout, "\tDATA\t"); |
| } |
| break; |
| case SHT_NOBITS: |
| /* bss */ |
| if (index == module->bss_index) { |
| /* updated the .bss segment */ |
| module->bss_start = section->sh_addr; |
| module->bss_end = section->sh_addr + section->sh_size; |
| fprintf(stdout, "\tBSS\t"); |
| } else { |
| fprintf(stdout, "\tHEAP\t"); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void elf_module_limits(struct image *image, struct module *module) |
| { |
| Elf32_Shdr *section; |
| uint32_t valid = (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR); |
| int i; |
| |
| module->text_start = module->data_start = 0xffffffff; |
| module->bss_start = 0; |
| module->text_end = module->data_end = module->bss_end = 0; |
| |
| fprintf(stdout, " Found %d sections, listing valid sections......\n", |
| module->hdr.e_shnum); |
| |
| fprintf(stdout, "\tNo\tStart\t\tEnd\t\tBytes\tType\tName\n"); |
| |
| /* iterate all sections and get size of segments */ |
| for (i = 0; i < module->hdr.e_shnum; i++) { |
| |
| section = &module->section[i]; |
| |
| /* module bss can sometimes be missed */ |
| if (i != module->bss_index) { |
| |
| /* only check valid sections */ |
| if (!(section->sh_flags & valid)) |
| continue; |
| |
| if (section->sh_size == 0) |
| continue; |
| |
| if (elf_is_rom(image, section)) |
| continue; |
| } |
| |
| fprintf(stdout, "\t%d\t0x%8.8x\t0x%8.8x\t%d", i, |
| section->sh_addr, section->sh_addr + section->sh_size, |
| section->sh_size); |
| |
| /* text or data section */ |
| if (image->reloc) |
| elf_module_size_reloc(image, module, section, i); |
| else |
| elf_module_size(image, module, section, i); |
| |
| /* section name */ |
| fprintf(stdout, "%s\n", module->strings + section->sh_name); |
| } |
| |
| fprintf(stdout, "\n"); |
| } |
| |
| /* make sure no section overlap from any modules */ |
| int elf_validate_section(struct image *image, struct module *module, |
| Elf32_Shdr *section, int index) |
| { |
| struct module *m; |
| Elf32_Shdr *s; |
| uint32_t valid = (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR); |
| int i, j; |
| |
| /* for each module */ |
| for (i = 0; i < image->num_modules; i++) { |
| m = &image->module[i]; |
| |
| /* for each section */ |
| for (j = 0; j < m->hdr.e_shnum; j++) { |
| s = &m->section[j]; |
| |
| if (s == section) |
| continue; |
| |
| /* only check valid sections */ |
| if (!(s->sh_flags & valid)) |
| continue; |
| |
| if (s->sh_size == 0) |
| continue; |
| |
| /* is section start non overlapping ? */ |
| if (section->sh_addr >= s->sh_addr && |
| section->sh_addr < |
| s->sh_addr + s->sh_size) { |
| goto err; |
| } |
| |
| /* is section end non overlapping ? */ |
| if (section->sh_addr + section->sh_size > s->sh_addr && |
| section->sh_addr + section->sh_size <= |
| s->sh_addr + s->sh_size) { |
| goto err; |
| } |
| } |
| } |
| |
| return 0; |
| |
| err: |
| fprintf(stderr, "error: section overlap between %s:%d and %s:%d\n", |
| module->elf_file, index, m->elf_file, j); |
| fprintf(stderr, " [0x%x : 0x%x] overlaps with [0x%x :0x%x]\n", |
| section->sh_addr, section->sh_addr + section->sh_size, |
| s->sh_addr, s->sh_addr + s->sh_size); |
| return -EINVAL; |
| } |
| |
| /* make sure no section overlaps from any modules */ |
| int elf_validate_modules(struct image *image) |
| { |
| struct module *module; |
| Elf32_Shdr *section; |
| uint32_t valid = (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR); |
| int i, j, ret; |
| |
| /* relocatable modules have no physical addresses until runtime */ |
| if (image->reloc) |
| return 0; |
| |
| /* for each module */ |
| for (i = 0; i < image->num_modules; i++) { |
| module = &image->module[i]; |
| |
| /* for each section */ |
| for (j = 0; j < module->hdr.e_shnum; j++) { |
| section = &module->section[j]; |
| |
| /* only check valid sections */ |
| if (!(section->sh_flags & valid)) |
| continue; |
| |
| if (section->sh_size == 0) |
| continue; |
| |
| /* is section non overlapping ? */ |
| ret = elf_validate_section(image, module, section, j); |
| if (ret < 0) |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int elf_find_section(struct image *image, struct module *module, |
| const char *name) |
| { |
| Elf32_Ehdr *hdr = &module->hdr; |
| Elf32_Shdr *section, *s; |
| char *buffer; |
| size_t count; |
| int ret, i; |
| |
| section = &module->section[hdr->e_shstrndx]; |
| |
| /* alloc data data */ |
| buffer = calloc(1, section->sh_size); |
| if (buffer == NULL) |
| return -ENOMEM; |
| |
| /* read in section string data */ |
| ret = fseek(module->fd, section->sh_offset, SEEK_SET); |
| if (ret < 0) { |
| fprintf(stderr, "error: cant seek to string section %d\n", ret); |
| goto out; |
| } |
| |
| count = fread(buffer, 1, section->sh_size, module->fd); |
| if (count != section->sh_size) { |
| fprintf(stderr, "error: can't read string section %d\n", -errno); |
| ret = -errno; |
| goto out; |
| } |
| |
| /* find section with name */ |
| for (i = 0; i < hdr->e_shnum; i++) { |
| s = &module->section[i]; |
| if (!strcmp(name, buffer + s->sh_name)) { |
| ret = i; |
| goto out; |
| } |
| } |
| |
| fprintf(stderr, "error: can't find section %s in module %s\n", name, |
| module->elf_file); |
| ret = -EINVAL; |
| |
| out: |
| free(buffer); |
| return ret; |
| } |
| |
| int elf_parse_module(struct image *image, int module_index, const char *name) |
| { |
| struct module *module; |
| uint32_t rem; |
| int ret = 0; |
| |
| /* validate module index */ |
| if (module_index >= MAX_MODULES) { |
| fprintf(stderr, "error: too any modules\n"); |
| return -EINVAL; |
| } |
| |
| module = &image->module[module_index]; |
| |
| /* open the elf input file */ |
| module->fd = fopen(name, "r"); |
| if (module->fd == NULL) { |
| fprintf(stderr, "error: unable to open %s for reading %d\n", |
| name, errno); |
| return -EINVAL; |
| } |
| module->elf_file = name; |
| |
| /* get file size */ |
| ret = fseek(module->fd, 0, SEEK_END); |
| if (ret < 0) |
| goto hdr_err; |
| module->file_size = ftell(module->fd); |
| ret = fseek(module->fd, 0, SEEK_SET); |
| if (ret < 0) |
| goto hdr_err; |
| |
| /* read in elf header */ |
| ret = elf_read_hdr(image, module); |
| if (ret < 0) |
| goto hdr_err; |
| |
| /* read in programs */ |
| ret = elf_read_programs(image, module); |
| if (ret < 0) { |
| fprintf(stderr, "error: failed to read program sections %d\n", |
| ret); |
| goto hdr_err; |
| } |
| |
| /* read sections */ |
| ret = elf_read_sections(image, module); |
| if (ret < 0) { |
| fprintf(stderr, "error: failed to read base sections %d\n", |
| ret); |
| goto sec_err; |
| } |
| |
| /* check limits */ |
| elf_module_limits(image, module); |
| |
| elf_find_section(image, module, ""); |
| |
| fprintf(stdout, " module: input size %d (0x%x) bytes %d sections\n", |
| module->fw_size, module->fw_size, module->num_sections); |
| fprintf(stdout, " module: text %d (0x%x) bytes\n" |
| " data %d (0x%x) bytes\n" |
| " bss %d (0x%x) bytes\n\n", |
| module->text_size, module->text_size, |
| module->data_size, module->data_size, |
| module->bss_size, module->bss_size); |
| |
| /* file sizes round up to nearest page */ |
| module->text_file_size = module->text_end - module->text_start; |
| rem = module->text_file_size % MAN_PAGE_SIZE; |
| if (rem) |
| module->text_file_size += MAN_PAGE_SIZE - rem; |
| |
| /* data section */ |
| module->data_file_size = module->data_end - module->data_start; |
| rem = module->data_file_size % MAN_PAGE_SIZE; |
| if (rem) |
| module->data_file_size += MAN_PAGE_SIZE - rem; |
| |
| /* bss section */ |
| module->bss_file_size = module->bss_end - module->bss_start; |
| rem = module->bss_file_size % MAN_PAGE_SIZE; |
| if (rem) |
| module->bss_file_size += MAN_PAGE_SIZE - rem; |
| |
| return 0; |
| |
| sec_err: |
| free(module->prg); |
| hdr_err: |
| fclose(module->fd); |
| |
| return ret; |
| } |
| |
| void elf_free_module(struct image *image, int module_index) |
| { |
| struct module *module = &image->module[module_index]; |
| |
| free(module->prg); |
| free(module->section); |
| free(module->strings); |
| fclose(module->fd); |
| } |