diff --git a/simavr/sim/sim_elf.c b/simavr/sim/sim_elf.c index ee3b5c5ad..0fd8e6561 100644 --- a/simavr/sim/sim_elf.c +++ b/simavr/sim/sim_elf.c @@ -360,24 +360,45 @@ elf_parse_mmcu_section( } static int -elf_copy_section( - const char *name, - Elf_Data *data, - uint8_t **dest) +elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **dest) { - *dest = malloc(data->d_size); + int rv; + + if (*dest == NULL) + *dest = malloc(php->p_filesz); if (!*dest) return -1; - memcpy(*dest, data->d_buf, data->d_size); - AVR_LOG(NULL, LOG_DEBUG, "Loaded %zu %s\n", data->d_size, name); - + lseek(fd, php->p_offset, SEEK_SET); + rv = read(fd, *dest, php->p_filesz); + if (rv != php->p_filesz) { + AVR_LOG(NULL, LOG_ERROR, + "Got %d when reading %d bytes for %x at offset %d " + "from ELF file\n", + rv, php->p_filesz, php->p_vaddr, php->p_offset); + return -1; + } + AVR_LOG(NULL, LOG_DEBUG, "Loaded %d bytes at %x\n", + php->p_filesz, php->p_vaddr); return 0; } +static int +elf_handle_segment(int fd, Elf32_Phdr *php, uint8_t **dest, const char *name) +{ + if (*dest) { + AVR_LOG(NULL, LOG_ERROR, + "Unexpected extra %s data: %d bytes at %x.\n", + name, php->p_filesz, php->p_vaddr); + return -1; + } else { + elf_copy_segment(fd, php, dest); + return 0; + } +} /* The structure *firmware must be pre-initialised to zero, then optionally - * with tracing and VCD information. + * tracing and VCD information may be added. */ int @@ -385,9 +406,12 @@ elf_read_firmware( const char * file, elf_firmware_t * firmware) { - Elf32_Ehdr elf_header; /* ELF header */ - Elf *elf = NULL; /* Our Elf pointer for libelf */ - int fd; // File Descriptor + Elf32_Ehdr elf_header; /* ELF header */ + Elf *elf = NULL; /* Our Elf pointer for libelf */ + Elf32_Phdr *php; /* Program header. */ + Elf_Scn *scn = NULL; /* Section Descriptor */ + size_t ph_count; /* Program Header entry count. */ + int fd, i; /* File Descriptor */ if ((fd = open(file, O_RDONLY | O_BINARY)) == -1 || (read(fd, &elf_header, sizeof(elf_header))) < sizeof(elf_header)) { @@ -397,12 +421,6 @@ elf_read_firmware( return -1; } - Elf_Data *data_data = NULL, - *data_text = NULL, - *data_ee = NULL; /* Data Descriptor */ - Elf_Data *data_fuse = NULL; - Elf_Data *data_lockbits = NULL; - #if ELF_SYMBOLS firmware->symbolcount = 0; firmware->symbol = NULL; @@ -410,7 +428,8 @@ elf_read_firmware( /* this is actually mandatory !! otherwise elf_begin() fails */ if (elf_version(EV_CURRENT) == EV_NONE) { - /* library out of date - recover from error */ + /* library out of date - recover from error */ + return -1; } // Iterate through section headers again this time well stop when we find symbols elf = elf_begin(fd, ELF_C_READ, NULL); @@ -418,37 +437,100 @@ elf_read_firmware( if (!elf) return -1; + if (elf_kind(elf) != ELF_K_ELF) { + AVR_LOG(NULL, LOG_ERROR, "Unexpected ELF file type\n"); + return -1; + } if (!firmware->dwarf_file) firmware->dwarf_file = strdup(file); // Parse later. - Elf_Scn *scn = NULL; /* Section Descriptor */ + /* Scan the Program Header Table. */ + + if (elf_getphdrnum(elf, &ph_count) != 0 || ph_count == 0 || + (php = elf32_getphdr(elf)) == NULL) { + AVR_LOG(NULL, LOG_ERROR, "No ELF Program Headers\n"); + return -1; + } + + for (i = 0; i < (int)ph_count; ++i, ++php) { +#if 0 + printf("Header %d type %d addr %x/%x size %d/%d flags %x\n", + i, php->p_type, php->p_vaddr, php->p_paddr, + php->p_filesz, php->p_memsz, php->p_flags); +#endif + if (php->p_type != PT_LOAD || php->p_filesz == 0) + continue; + if (php->p_vaddr < 0x800000) { + /* Explicit flash section. Load it. */ + + if (elf_handle_segment(fd, php, &firmware->flash, "Flash")) + continue; + firmware->flashsize = php->p_filesz; + firmware->flashbase = php->p_vaddr; + } else if (php->p_vaddr < 0x810000) { + /* Data space. If there are initialised variables, treat + * them as extra initialised flash. The C startup function + * understands that and will copy them to RAM. + */ + + if (firmware->flash) { + uint8_t *where; + + firmware->flash = realloc(firmware->flash, + firmware->flashsize + php->p_filesz); + if (!firmware->flash) + return -1; + where = firmware->flash + firmware->flashsize; + elf_copy_segment(fd, php, &where); + firmware->flashsize += php->p_filesz; + } else { + /* If this ever happens, add a second pass. */ + + AVR_LOG(NULL, LOG_ERROR, + "Initialialised data but no flash (%d bytes at %x)!\n", + php->p_filesz, php->p_vaddr); + return -1; + } + } else if (php->p_vaddr < 0x820000) { + /* EEPROM. */ + + if (elf_handle_segment(fd, php, &firmware->eeprom, "EEPROM")) + continue; + firmware->eesize = php->p_filesz; + } else if (php->p_vaddr < 0x830000) { + /* Fuses. */ + + if (elf_handle_segment(fd, php, &firmware->fuse, "Fuses")) + continue; + firmware->fusesize = php->p_filesz; + } else if (php->p_vaddr < 0x840000) { + /* Lock bits. */ + + elf_handle_segment(fd, php, &firmware->lockbits, "Lock bits"); + } + } + + /* Scan the section table for .mmcu magic and symbols. */ while ((scn = elf_nextscn(elf, scn)) != NULL) { GElf_Shdr shdr; /* Section Header */ gelf_getshdr(scn, &shdr); char * name = elf_strptr(elf, elf_header.e_shstrndx, shdr.sh_name); - // printf("Walking elf section '%s'\n", name); - - if (!strcmp(name, ".text")) - data_text = elf_getdata(scn, NULL); - else if (!strcmp(name, ".data")) - data_data = elf_getdata(scn, NULL); - else if (!strcmp(name, ".eeprom")) - data_ee = elf_getdata(scn, NULL); - else if (!strcmp(name, ".fuse")) - data_fuse = elf_getdata(scn, NULL); - else if (!strcmp(name, ".lock")) - data_lockbits = elf_getdata(scn, NULL); - else if (!strcmp(name, ".bss")) { - Elf_Data *s = elf_getdata(scn, NULL); - firmware->bsssize = s->d_size; - } else if (!strcmp(name, ".mmcu")) { + // printf("Walking elf section '%s'\n", name); + + if (!strcmp(name, ".mmcu")) { Elf_Data *s = elf_getdata(scn, NULL); + elf_parse_mmcu_section(firmware, s->d_buf, s->d_size); + if (shdr.sh_addr < 0x860000) + AVR_LOG(NULL, LOG_WARNING, + "Warning: ELF .mmcu section at %x may be loaded.\n", + shdr.sh_addr); // printf("%s: size %ld\n", __FUNCTION__, s->d_size); // avr->frequency = f_cpu; } + #if ELF_SYMBOLS // When we find a section header marked SHT_SYMTAB stop and get symbols if (shdr.sh_type == SHT_SYMTAB) { @@ -537,46 +619,9 @@ elf_read_firmware( } firmware->highest_data_symbol = highest_data; } -#endif - } - uint32_t offset = 0; - firmware->flashsize = - (data_text ? data_text->d_size : 0) + - (data_data ? data_data->d_size : 0); - firmware->flash = malloc(firmware->flashsize); - - // using unsigned int for output, since there is no AVR with 4GB - if (data_text) { - // hdump("code", data_text->d_buf, data_text->d_size); - memcpy(firmware->flash + offset, data_text->d_buf, data_text->d_size); - AVR_LOG(NULL, LOG_DEBUG, "Loaded %zu .text at address 0x%x\n", - (unsigned int)data_text->d_size, firmware->flashbase); - offset += data_text->d_size; - } - if (data_data) { - // hdump("data", data_data->d_buf, data_data->d_size); - memcpy(firmware->flash + offset, data_data->d_buf, data_data->d_size); - AVR_LOG(NULL, LOG_DEBUG, "Loaded %zu .data\n", data_data->d_size); - offset += data_data->d_size; - firmware->datasize = data_data->d_size; +#endif // ELF_SYMBOLS } - if (data_ee) { - if (elf_copy_section(".eeprom", data_ee, &firmware->eeprom)) - return -1; - firmware->eesize = data_ee->d_size; - } - if (data_fuse) { - if (elf_copy_section(".fuse", data_fuse, &firmware->fuse)) - return -1; - firmware->fusesize = data_fuse->d_size; - } - if (data_lockbits) { - if (elf_copy_section(".lock", data_lockbits, &firmware->lockbits)) - return -1; - } -// hdump("flash", avr->flash, offset); elf_end(elf); close(fd); return 0; } -