int fiasco_write_to_file(struct fiasco * fiasco, const char * file) { int fd = -1; int i; int device_count; uint32_t size; uint32_t length; uint16_t hash; uint8_t length8; char ** device_hwrevs_bufs; const char * str; const char * type; struct image_list * image_list; struct image * image; unsigned char buf[4096]; if ( ! fiasco ) return -1; printf("Generating Fiasco image %s...\n", file); if ( ! fiasco->first ) FIASCO_WRITE_ERROR(file, fd, "Nothing to write"); if ( fiasco->name && strlen(fiasco->name)+1 > UINT8_MAX ) FIASCO_WRITE_ERROR(file, fd, "Fiasco name string is too long"); if ( fiasco->swver && strlen(fiasco->swver)+1 > UINT8_MAX ) FIASCO_WRITE_ERROR(file, fd, "SW version string is too long"); if ( ! simulate ) { fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0644); if ( fd < 0 ) { ERROR_INFO("Cannot create file"); return -1; } } printf("Writing Fiasco header...\n"); WRITE_OR_FAIL(file, fd, "\xb4", 1); /* signature */ if ( fiasco->name[0] ) str = fiasco->name; else str = "OSSO UART+USB"; length = 4 + strlen(str) + 3; if ( fiasco->swver[0] ) length += strlen(fiasco->swver) + 3; length = htonl(length); WRITE_OR_FAIL(file, fd, &length, 4); /* FW header length */ if ( fiasco->swver[0] ) length = htonl(2); else length = htonl(1); WRITE_OR_FAIL(file, fd, &length, 4); /* FW header blocks count */ /* Fiasco name */ length8 = strlen(str)+1; WRITE_OR_FAIL(file, fd, "\xe8", 1); WRITE_OR_FAIL(file, fd, &length8, 1); WRITE_OR_FAIL(file, fd, str, length8); /* SW version */ if ( fiasco->swver[0] ) { printf("Writing SW version: %s\n", fiasco->swver); length8 = strlen(fiasco->swver)+1; WRITE_OR_FAIL(file, fd, "\x31", 1); WRITE_OR_FAIL(file, fd, &length8, 1); WRITE_OR_FAIL(file, fd, fiasco->swver, length8); }; printf("\n"); image_list = fiasco->first; while ( image_list ) { image = image_list->image; if ( ! image ) FIASCO_WRITE_ERROR(file, fd, "Empty image"); printf("Writing image...\n"); image_print_info(image); type = image_type_to_string(image->type); device_hwrevs_bufs = device_list_alloc_to_bufs(image->devices); device_count = 0; if ( device_hwrevs_bufs && device_hwrevs_bufs[0] ) for ( ; device_hwrevs_bufs[device_count]; ++device_count ); if ( ! type ) FIASCO_WRITE_ERROR(file, fd, "Unknown image type"); if ( image->version && strlen(image->version) > UINT8_MAX ) FIASCO_WRITE_ERROR(file, fd, "Image version string is too long"); if ( image->layout && strlen(image->layout) > UINT8_MAX ) FIASCO_WRITE_ERROR(file, fd, "Image layout is too long"); printf("Writing image header...\n"); /* signature */ WRITE_OR_FAIL(file, fd, "T", 1); /* number of subsections */ length8 = device_count+1; if ( image->version ) ++length8; if ( image->layout ) ++length8; WRITE_OR_FAIL(file, fd, &length8, 1); /* unknown */ WRITE_OR_FAIL(file, fd, "\x2e\x19\x01\x01\x00", 5); /* checksum */ hash = htons(image->hash); WRITE_OR_FAIL(file, fd, &hash, 2); /* image type name */ memset(buf, 0, 12); strncpy((char *)buf, type, 12); WRITE_OR_FAIL(file, fd, buf, 12); /* image size */ size = htonl(image->size); WRITE_OR_FAIL(file, fd, &size, 4); /* unknown */ WRITE_OR_FAIL(file, fd, "\x00\x00\x00\x00", 4); /* append version subsection */ if ( image->version ) { WRITE_OR_FAIL(file, fd, "1", 1); /* 1 - version */ length8 = strlen(image->version)+1; WRITE_OR_FAIL(file, fd, &length8, 1); WRITE_OR_FAIL(file, fd, image->version, length8); } /* append device & hwrevs subsection */ for ( i = 0; i < device_count; ++i ) { WRITE_OR_FAIL(file, fd, "2", 1); /* 2 - device & hwrevs */ WRITE_OR_FAIL(file, fd, &device_hwrevs_bufs[i][0], 1); WRITE_OR_FAIL(file, fd, device_hwrevs_bufs[i]+1, ((uint8_t *)(device_hwrevs_bufs[i]))[0]); } free(device_hwrevs_bufs); /* append layout subsection */ if ( image->layout ) { length8 = strlen(image->layout); WRITE_OR_FAIL(file, fd, "3", 1); /* 3 - layout */ WRITE_OR_FAIL(file, fd, &length8, 1); WRITE_OR_FAIL(file, fd, image->layout, length8); } /* dummy byte - end of all subsections */ WRITE_OR_FAIL(file, fd, "\x00", 1); printf("Writing image data...\n"); image_seek(image, 0); while ( 1 ) { size = image_read(image, buf, sizeof(buf)); if ( size == 0 ) break; WRITE_OR_FAIL(file, fd, buf, size); } image_list = image_list->next; if ( image_list ) printf("\n"); } close(fd); printf("\nDone\n\n"); return 0; }
int fiasco_unpack(struct fiasco * fiasco, const char * dir) { int fd = -1; char * name = NULL; char * layout_name = NULL; struct image * image; struct image_list * image_list; uint32_t size; char cwd[256]; unsigned char buf[4096]; if ( dir ) { memset(cwd, 0, sizeof(cwd)); if ( ! getcwd(cwd, sizeof(cwd)) ) { ERROR_INFO("Cannot store current directory"); return -1; } if ( chdir(dir) < 0 ) { ERROR_INFO("Cannot change current directory to %s", dir); return -1; } } fiasco_print_info(fiasco); image_list = fiasco->first; while ( image_list ) { image = image_list->image; name = image_name_alloc_from_values(image); if ( ! name ) return -1; printf("\n"); printf("Unpacking image...\n"); image_print_info(image); if ( image->layout ) { layout_name = calloc(1, strlen(name) + strlen(".layout") + 1); if ( ! layout_name ) ALLOC_ERROR_RETURN(-1); sprintf(layout_name, "%s.layout", name); printf(" Layout file: %s\n", layout_name); } printf(" Output file: %s\n", name); if ( ! simulate ) { fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644); if ( fd < 0 ) { ERROR_INFO("Cannot create output file %s", name); return -1; } } free(name); image_seek(image, 0); while ( 1 ) { size = image_read(image, buf, sizeof(buf)); if ( size == 0 ) break; WRITE_OR_FAIL(name, fd, buf, size); } close(fd); if ( image->layout ) { if ( ! simulate ) { fd = open(layout_name, O_RDWR|O_CREAT|O_TRUNC, 0644); if ( fd < 0 ) { ERROR_INFO("Cannot create layout file %s", layout_name); return -1; } } free(layout_name); WRITE_OR_FAIL(layout_name, fd, image->layout, (int)strlen(image->layout)); close(fd); } image_list = image_list->next; } if ( dir ) { if ( chdir(cwd) < 0 ) { ERROR_INFO("Cannot change current directory back to %s", cwd); return -1; } } printf("\nDone\n\n"); return 0; }
/* * * The implementation assumes that the loadable segments are present * in the PHT sorted by their offsets, so that only forward seeks would * be necessary. */ int load_segments(struct elf_module *module, Elf_Ehdr *elf_hdr) { int i; int res = 0; char *pht = NULL; char *sht = NULL; Elf32_Phdr *cr_pht; Elf32_Shdr *cr_sht; Elf32_Addr min_addr = 0x00000000; // Min. ELF vaddr Elf32_Addr max_addr = 0x00000000; // Max. ELF vaddr Elf32_Word max_align = sizeof(void*); // Min. align of posix_memalign() Elf32_Addr min_alloc, max_alloc; // Min. and max. aligned allocables Elf32_Addr dyn_addr = 0x00000000; // Get to the PHT image_seek(elf_hdr->e_phoff, module); // Load the PHT pht = malloc(elf_hdr->e_phnum * elf_hdr->e_phentsize); if (!pht) return -1; image_read(pht, elf_hdr->e_phnum * elf_hdr->e_phentsize, module); // Compute the memory needings of the module for (i=0; i < elf_hdr->e_phnum; i++) { cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize); switch (cr_pht->p_type) { case PT_LOAD: if (i == 0) { min_addr = cr_pht->p_vaddr; } else { min_addr = MIN(min_addr, cr_pht->p_vaddr); } max_addr = MAX(max_addr, cr_pht->p_vaddr + cr_pht->p_memsz); max_align = MAX(max_align, cr_pht->p_align); break; case PT_DYNAMIC: dyn_addr = cr_pht->p_vaddr; break; default: // Unsupported - ignore break; } } if (max_addr - min_addr == 0) { // No loadable segments DBG_PRINT("No loadable segments found\n"); goto out; } if (dyn_addr == 0) { DBG_PRINT("No dynamic information segment found\n"); goto out; } // The minimum address that should be allocated min_alloc = min_addr - (min_addr % max_align); // The maximum address that should be allocated max_alloc = max_addr - (max_addr % max_align); if (max_addr % max_align > 0) max_alloc += max_align; if (elf_malloc(&module->module_addr, max_align, max_alloc-min_alloc) != 0) { DBG_PRINT("Could not allocate segments\n"); goto out; } module->base_addr = (Elf32_Addr)(module->module_addr) - min_alloc; module->module_size = max_alloc - min_alloc; // Zero-initialize the memory memset(module->module_addr, 0, module->module_size); for (i = 0; i < elf_hdr->e_phnum; i++) { cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize); if (cr_pht->p_type == PT_LOAD) { // Copy the segment at its destination if (cr_pht->p_offset < module->u.l._cr_offset) { // The segment contains data before the current offset // It can be discarded without worry - it would contain only // headers Elf32_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset; if (image_read((char *)module_get_absolute(cr_pht->p_vaddr, module) + aux_off, cr_pht->p_filesz - aux_off, module) < 0) { res = -1; goto out; } } else { if (image_seek(cr_pht->p_offset, module) < 0) { res = -1; goto out; } if (image_read(module_get_absolute(cr_pht->p_vaddr, module), cr_pht->p_filesz, module) < 0) { res = -1; goto out; } } /* DBG_PRINT("Loadable segment of size 0x%08x copied from vaddr 0x%08x at 0x%08x\n", cr_pht->p_filesz, cr_pht->p_vaddr, (Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module)); */ } } // Get to the SHT image_seek(elf_hdr->e_shoff, module); // Load the SHT sht = malloc(elf_hdr->e_shnum * elf_hdr->e_shentsize); if (!sht) { res = -1; goto out; } image_read(sht, elf_hdr->e_shnum * elf_hdr->e_shentsize, module); // Setup the symtable size for (i = 0; i < elf_hdr->e_shnum; i++) { cr_sht = (Elf32_Shdr*)(sht + i * elf_hdr->e_shentsize); if (cr_sht->sh_type == SHT_DYNSYM) { module->symtable_size = cr_sht->sh_size; break; } } free(sht); // Setup dynamic segment location module->dyn_table = module_get_absolute(dyn_addr, module); /* DBG_PRINT("Base address: 0x%08x, aligned at 0x%08x\n", module->base_addr, max_align); DBG_PRINT("Module size: 0x%08x\n", module->module_size); */ out: // Free up allocated memory if (pht != NULL) free(pht); return res; }