More correct elf loader (#1839)
* ELF File: more robust section loader * ELF File: faster and smaller preinit, init and fini arrays handling * ELF File: load sections on preload stage * ELF File: naming * Furi: fix use after free in thread join Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									1a1f711897
								
							
						
					
					
						commit
						37b5e58a60
					
				| @ -70,6 +70,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { | ||||
|     do { | ||||
|         file_selected = true; | ||||
|         loader->app = flipper_application_alloc(loader->storage, &hashtable_api_interface); | ||||
|         size_t start = furi_get_tick(); | ||||
| 
 | ||||
|         FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); | ||||
| 
 | ||||
| @ -99,6 +100,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); | ||||
|         FURI_LOG_I(TAG, "FAP Loader is staring app"); | ||||
| 
 | ||||
|         FuriThread* thread = flipper_application_spawn(loader->app, NULL); | ||||
|  | ||||
| @ -103,6 +103,7 @@ static void furi_thread_body(void* context) { | ||||
|     furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL); | ||||
|     vTaskSetThreadLocalStoragePointer(NULL, 0, NULL); | ||||
| 
 | ||||
|     thread->task_handle = NULL; | ||||
|     vTaskDelete(NULL); | ||||
|     furi_thread_catch(); | ||||
| } | ||||
| @ -211,13 +212,8 @@ bool furi_thread_join(FuriThread* thread) { | ||||
| 
 | ||||
|     furi_check(furi_thread_get_current() != thread); | ||||
| 
 | ||||
|     // Check if thread was started
 | ||||
|     if(thread->task_handle == NULL) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Wait for thread to stop
 | ||||
|     while(eTaskGetState(thread->task_handle) != eDeleted) { | ||||
|     while(thread->task_handle) { | ||||
|         furi_delay_ms(10); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -57,8 +57,23 @@ static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) { | ||||
|     return ELFSectionDict_get(elf->sections, name); | ||||
| } | ||||
| 
 | ||||
| static void elf_file_put_section(ELFFile* elf, const char* name, ELFSection* section) { | ||||
|     ELFSectionDict_set_at(elf->sections, strdup(name), *section); | ||||
| static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) { | ||||
|     ELFSection* section_p = elf_file_get_section(elf, name); | ||||
|     if(!section_p) { | ||||
|         ELFSectionDict_set_at( | ||||
|             elf->sections, | ||||
|             strdup(name), | ||||
|             (ELFSection){ | ||||
|                 .data = NULL, | ||||
|                 .sec_idx = 0, | ||||
|                 .size = 0, | ||||
|                 .rel_count = 0, | ||||
|                 .rel_offset = 0, | ||||
|             }); | ||||
|         section_p = elf_file_get_section(elf, name); | ||||
|     } | ||||
| 
 | ||||
|     return section_p; | ||||
| } | ||||
| 
 | ||||
| static bool elf_read_string_from_offset(ELFFile* elf, off_t offset, FuriString* name) { | ||||
| @ -320,12 +335,12 @@ static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf3 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) { | ||||
| static bool elf_relocate(ELFFile* elf, ELFSection* s) { | ||||
|     if(s->data) { | ||||
|         Elf32_Rel rel; | ||||
|         size_t relEntries = h->sh_size / sizeof(rel); | ||||
|         size_t relEntries = s->rel_count; | ||||
|         size_t relCount; | ||||
|         (void)storage_file_seek(elf->fd, h->sh_offset, true); | ||||
|         (void)storage_file_seek(elf->fd, s->rel_offset, true); | ||||
|         FURI_LOG_D(TAG, " Offset   Info     Type             Name"); | ||||
| 
 | ||||
|         int relocate_result = true; | ||||
| @ -395,14 +410,6 @@ static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| /**************************************************************************************************/ | ||||
| /********************************************* MISC ***********************************************/ | ||||
| /**************************************************************************************************/ | ||||
| 
 | ||||
| static bool cstr_prefix(const char* prefix, const char* string) { | ||||
|     return strncmp(prefix, string, strlen(prefix)) == 0; | ||||
| } | ||||
| 
 | ||||
| /**************************************************************************************************/ | ||||
| /************************************ Internal FAP interfaces *************************************/ | ||||
| /**************************************************************************************************/ | ||||
| @ -445,6 +452,31 @@ static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) { | ||||
|                section_header->sh_size; | ||||
| } | ||||
| 
 | ||||
| static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) { | ||||
|     if(section_header->sh_size == 0) { | ||||
|         FURI_LOG_D(TAG, "No data for section"); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign); | ||||
|     section->size = section_header->sh_size; | ||||
| 
 | ||||
|     if(section_header->sh_type == SHT_NOBITS) { | ||||
|         // BSS section, no data to load
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if((!storage_file_seek(elf->fd, section_header->sh_offset, true)) || | ||||
|        (storage_file_read(elf->fd, section->data, section_header->sh_size) != | ||||
|         section_header->sh_size)) { | ||||
|         FURI_LOG_E(TAG, "    seek/read fail"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "0x%X", section->data); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static SectionType elf_preload_section( | ||||
|     ELFFile* elf, | ||||
|     size_t section_idx, | ||||
| @ -453,73 +485,63 @@ static SectionType elf_preload_section( | ||||
|     FlipperApplicationManifest* manifest) { | ||||
|     const char* name = furi_string_get_cstr(name_string); | ||||
| 
 | ||||
|     const struct { | ||||
|         const char* prefix; | ||||
|         SectionType type; | ||||
|     } lookup_sections[] = { | ||||
|         {".text", SectionTypeData}, | ||||
|         {".rodata", SectionTypeData}, | ||||
|         {".data", SectionTypeData}, | ||||
|         {".bss", SectionTypeData}, | ||||
|         {".preinit_array", SectionTypeData}, | ||||
|         {".init_array", SectionTypeData}, | ||||
|         {".fini_array", SectionTypeData}, | ||||
|         {".rel.text", SectionTypeRelData}, | ||||
|         {".rel.rodata", SectionTypeRelData}, | ||||
|         {".rel.data", SectionTypeRelData}, | ||||
|         {".rel.preinit_array", SectionTypeRelData}, | ||||
|         {".rel.init_array", SectionTypeRelData}, | ||||
|         {".rel.fini_array", SectionTypeRelData}, | ||||
|     }; | ||||
| 
 | ||||
|     for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) { | ||||
|         if(cstr_prefix(lookup_sections[i].prefix, name)) { | ||||
|             FURI_LOG_D(TAG, "Found section %s", lookup_sections[i].prefix); | ||||
| 
 | ||||
|             if(lookup_sections[i].type == SectionTypeRelData) { | ||||
|                 name = name + strlen(".rel"); | ||||
|             } | ||||
| 
 | ||||
|             ELFSection* section_p = elf_file_get_section(elf, name); | ||||
|             if(!section_p) { | ||||
|                 ELFSection section = { | ||||
|                     .data = NULL, | ||||
|                     .sec_idx = 0, | ||||
|                     .rel_sec_idx = 0, | ||||
|                     .size = 0, | ||||
|                 }; | ||||
| 
 | ||||
|                 elf_file_put_section(elf, name, §ion); | ||||
|                 section_p = elf_file_get_section(elf, name); | ||||
|             } | ||||
| 
 | ||||
|             if(lookup_sections[i].type == SectionTypeRelData) { | ||||
|                 section_p->rel_sec_idx = section_idx; | ||||
|             } else { | ||||
|     // Load allocable section
 | ||||
|     if(section_header->sh_flags & SHF_ALLOC) { | ||||
|         ELFSection* section_p = elf_file_get_or_put_section(elf, name); | ||||
|         section_p->sec_idx = section_idx; | ||||
| 
 | ||||
|         if(section_header->sh_type == SHT_PREINIT_ARRAY) { | ||||
|             elf->preinit_array = section_p; | ||||
|         } else if(section_header->sh_type == SHT_INIT_ARRAY) { | ||||
|             elf->init_array = section_p; | ||||
|         } else if(section_header->sh_type == SHT_FINI_ARRAY) { | ||||
|             elf->fini_array = section_p; | ||||
|         } | ||||
| 
 | ||||
|             return lookup_sections[i].type; | ||||
|         if(!elf_load_section_data(elf, section_p, section_header)) { | ||||
|             FURI_LOG_E(TAG, "Error loading section '%s'", name); | ||||
|             return SectionTypeERROR; | ||||
|         } else { | ||||
|             return SectionTypeData; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Load link info section
 | ||||
|     if(section_header->sh_flags & SHF_INFO_LINK) { | ||||
|         name = name + strlen(".rel"); | ||||
|         ELFSection* section_p = elf_file_get_or_put_section(elf, name); | ||||
|         section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); | ||||
|         section_p->rel_offset = section_header->sh_offset; | ||||
|         return SectionTypeRelData; | ||||
|     } | ||||
| 
 | ||||
|     // Load symbol table
 | ||||
|     if(strcmp(name, ".symtab") == 0) { | ||||
|         FURI_LOG_D(TAG, "Found .symtab section"); | ||||
|         elf->symbol_table = section_header->sh_offset; | ||||
|         elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym); | ||||
|         return SectionTypeSymTab; | ||||
|     } else if(strcmp(name, ".strtab") == 0) { | ||||
|     } | ||||
| 
 | ||||
|     // Load string table
 | ||||
|     if(strcmp(name, ".strtab") == 0) { | ||||
|         FURI_LOG_D(TAG, "Found .strtab section"); | ||||
|         elf->symbol_table_strings = section_header->sh_offset; | ||||
|         return SectionTypeStrTab; | ||||
|     } else if(strcmp(name, ".fapmeta") == 0) { | ||||
|     } | ||||
| 
 | ||||
|     // Load manifest section
 | ||||
|     if(strcmp(name, ".fapmeta") == 0) { | ||||
|         FURI_LOG_D(TAG, "Found .fapmeta section"); | ||||
|         if(elf_load_metadata(elf, section_header, manifest)) { | ||||
|             return SectionTypeManifest; | ||||
|         } else { | ||||
|             return SectionTypeERROR; | ||||
|         } | ||||
|     } else if(strcmp(name, ".gnu_debuglink") == 0) { | ||||
|     } | ||||
| 
 | ||||
|     // Load debug link section
 | ||||
|     if(strcmp(name, ".gnu_debuglink") == 0) { | ||||
|         FURI_LOG_D(TAG, "Found .gnu_debuglink section"); | ||||
|         if(elf_load_debug_link(elf, section_header)) { | ||||
|             return SectionTypeDebugLink; | ||||
| @ -531,61 +553,17 @@ static SectionType elf_preload_section( | ||||
|     return SectionTypeUnused; | ||||
| } | ||||
| 
 | ||||
| static bool elf_load_section_data(ELFFile* elf, ELFSection* section) { | ||||
|     Elf32_Shdr section_header; | ||||
|     if(section->sec_idx == 0) { | ||||
|         FURI_LOG_D(TAG, "Section is not present"); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if(!elf_read_section_header(elf, section->sec_idx, §ion_header)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(section_header.sh_size == 0) { | ||||
|         FURI_LOG_D(TAG, "No data for section"); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     section->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign); | ||||
|     section->size = section_header.sh_size; | ||||
| 
 | ||||
|     if(section_header.sh_type == SHT_NOBITS) { | ||||
|         /* section is empty (.bss?) */ | ||||
|         /* no need to memset - allocator already did that */ | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if((!storage_file_seek(elf->fd, section_header.sh_offset, true)) || | ||||
|        (storage_file_read(elf->fd, section->data, section_header.sh_size) != | ||||
|         section_header.sh_size)) { | ||||
|         FURI_LOG_E(TAG, "    seek/read fail"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "0x%X", section->data); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool elf_relocate_section(ELFFile* elf, ELFSection* section) { | ||||
|     Elf32_Shdr section_header; | ||||
|     if(section->rel_sec_idx) { | ||||
|     if(section->rel_count) { | ||||
|         FURI_LOG_D(TAG, "Relocating section"); | ||||
|         if(elf_read_section_header(elf, section->rel_sec_idx, §ion_header)) | ||||
|             return elf_relocate(elf, §ion_header, section); | ||||
|         else { | ||||
|             FURI_LOG_E(TAG, "Error reading section header"); | ||||
|             return false; | ||||
|         } | ||||
|         return elf_relocate(elf, section); | ||||
|     } else { | ||||
|         FURI_LOG_D(TAG, "No relocation index"); /* Not an error */ | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void elf_file_call_section_list(ELFFile* elf, const char* name, bool reverse_order) { | ||||
|     ELFSection* section = elf_file_get_section(elf, name); | ||||
| 
 | ||||
| static void elf_file_call_section_list(ELFSection* section, bool reverse_order) { | ||||
|     if(section && section->size) { | ||||
|         const uint32_t* start = section->data; | ||||
|         const uint32_t* end = section->data + section->size; | ||||
| @ -729,7 +707,6 @@ bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manif | ||||
|     } | ||||
| 
 | ||||
|     furi_string_free(name); | ||||
|     FURI_LOG_D(TAG, "Load symbols done"); | ||||
| 
 | ||||
|     return IS_FLAGS_SET(loaded_sections, SectionTypeValid); | ||||
| } | ||||
| @ -739,16 +716,6 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { | ||||
|     ELFSectionDict_it_t it; | ||||
| 
 | ||||
|     AddressCache_init(elf->relocation_cache); | ||||
|     size_t start = furi_get_tick(); | ||||
| 
 | ||||
|     for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { | ||||
|         ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); | ||||
|         FURI_LOG_D(TAG, "Loading section '%s'", itref->key); | ||||
|         if(!elf_load_section_data(elf, &itref->value)) { | ||||
|             FURI_LOG_E(TAG, "Error loading section '%s'", itref->key); | ||||
|             status = ELFFileLoadStatusUnspecifiedError; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(status == ELFFileLoadStatusSuccess) { | ||||
|         for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); | ||||
| @ -777,14 +744,13 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { | ||||
|     FURI_LOG_D(TAG, "Relocation cache size: %u", AddressCache_size(elf->relocation_cache)); | ||||
|     FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache)); | ||||
|     AddressCache_clear(elf->relocation_cache); | ||||
|     FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); | ||||
| 
 | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| void elf_file_pre_run(ELFFile* elf) { | ||||
|     elf_file_call_section_list(elf, ".preinit_array", false); | ||||
|     elf_file_call_section_list(elf, ".init_array", false); | ||||
|     elf_file_call_section_list(elf->preinit_array, false); | ||||
|     elf_file_call_section_list(elf->init_array, false); | ||||
| } | ||||
| 
 | ||||
| int32_t elf_file_run(ELFFile* elf, void* args) { | ||||
| @ -794,7 +760,7 @@ int32_t elf_file_run(ELFFile* elf, void* args) { | ||||
| } | ||||
| 
 | ||||
| void elf_file_post_run(ELFFile* elf) { | ||||
|     elf_file_call_section_list(elf, ".fini_array", true); | ||||
|     elf_file_call_section_list(elf->fini_array, true); | ||||
| } | ||||
| 
 | ||||
| const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) { | ||||
|  | ||||
| @ -16,8 +16,10 @@ typedef int32_t(entry_t)(void*); | ||||
| typedef struct { | ||||
|     void* data; | ||||
|     uint16_t sec_idx; | ||||
|     uint16_t rel_sec_idx; | ||||
|     Elf32_Word size; | ||||
| 
 | ||||
|     size_t rel_count; | ||||
|     Elf32_Off rel_offset; | ||||
| } ELFSection; | ||||
| 
 | ||||
| DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) | ||||
| @ -39,6 +41,10 @@ struct ELFFile { | ||||
|     File* fd; | ||||
|     const ElfApiInterface* api_interface; | ||||
|     ELFDebugLinkInfo debug_link_info; | ||||
| 
 | ||||
|     ELFSection* preinit_array; | ||||
|     ELFSection* init_array; | ||||
|     ELFSection* fini_array; | ||||
| }; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sergey Gavrilov
						Sergey Gavrilov