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:
Sergey Gavrilov 2022-10-08 03:06:29 +10:00 committed by GitHub
parent 1a1f711897
commit 37b5e58a60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 130 deletions

View File

@ -70,6 +70,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
do { do {
file_selected = true; file_selected = true;
loader->app = flipper_application_alloc(loader->storage, &hashtable_api_interface); 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)); 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; break;
} }
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
FURI_LOG_I(TAG, "FAP Loader is staring app"); FURI_LOG_I(TAG, "FAP Loader is staring app");
FuriThread* thread = flipper_application_spawn(loader->app, NULL); FuriThread* thread = flipper_application_spawn(loader->app, NULL);

View File

@ -103,6 +103,7 @@ static void furi_thread_body(void* context) {
furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL); furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL);
vTaskSetThreadLocalStoragePointer(NULL, 0, NULL); vTaskSetThreadLocalStoragePointer(NULL, 0, NULL);
thread->task_handle = NULL;
vTaskDelete(NULL); vTaskDelete(NULL);
furi_thread_catch(); furi_thread_catch();
} }
@ -211,13 +212,8 @@ bool furi_thread_join(FuriThread* thread) {
furi_check(furi_thread_get_current() != 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 // Wait for thread to stop
while(eTaskGetState(thread->task_handle) != eDeleted) { while(thread->task_handle) {
furi_delay_ms(10); furi_delay_ms(10);
} }

View File

@ -57,8 +57,23 @@ static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) {
return ELFSectionDict_get(elf->sections, name); return ELFSectionDict_get(elf->sections, name);
} }
static void elf_file_put_section(ELFFile* elf, const char* name, ELFSection* section) { static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) {
ELFSectionDict_set_at(elf->sections, strdup(name), *section); 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) { 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; return true;
} }
static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) { static bool elf_relocate(ELFFile* elf, ELFSection* s) {
if(s->data) { if(s->data) {
Elf32_Rel rel; Elf32_Rel rel;
size_t relEntries = h->sh_size / sizeof(rel); size_t relEntries = s->rel_count;
size_t relCount; 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"); FURI_LOG_D(TAG, " Offset Info Type Name");
int relocate_result = true; int relocate_result = true;
@ -395,14 +410,6 @@ static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) {
return false; return false;
} }
/**************************************************************************************************/
/********************************************* MISC ***********************************************/
/**************************************************************************************************/
static bool cstr_prefix(const char* prefix, const char* string) {
return strncmp(prefix, string, strlen(prefix)) == 0;
}
/**************************************************************************************************/ /**************************************************************************************************/
/************************************ Internal FAP interfaces *************************************/ /************************************ Internal FAP interfaces *************************************/
/**************************************************************************************************/ /**************************************************************************************************/
@ -445,6 +452,31 @@ static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
section_header->sh_size; 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( static SectionType elf_preload_section(
ELFFile* elf, ELFFile* elf,
size_t section_idx, size_t section_idx,
@ -453,73 +485,63 @@ static SectionType elf_preload_section(
FlipperApplicationManifest* manifest) { FlipperApplicationManifest* manifest) {
const char* name = furi_string_get_cstr(name_string); const char* name = furi_string_get_cstr(name_string);
const struct { // Load allocable section
const char* prefix; if(section_header->sh_flags & SHF_ALLOC) {
SectionType type; ELFSection* section_p = elf_file_get_or_put_section(elf, name);
} lookup_sections[] = { section_p->sec_idx = section_idx;
{".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(section_header->sh_type == SHT_PREINIT_ARRAY) {
if(cstr_prefix(lookup_sections[i].prefix, name)) { elf->preinit_array = section_p;
FURI_LOG_D(TAG, "Found section %s", lookup_sections[i].prefix); } 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;
}
if(lookup_sections[i].type == SectionTypeRelData) { if(!elf_load_section_data(elf, section_p, section_header)) {
name = name + strlen(".rel"); FURI_LOG_E(TAG, "Error loading section '%s'", name);
} return SectionTypeERROR;
} else {
ELFSection* section_p = elf_file_get_section(elf, name); return SectionTypeData;
if(!section_p) {
ELFSection section = {
.data = NULL,
.sec_idx = 0,
.rel_sec_idx = 0,
.size = 0,
};
elf_file_put_section(elf, name, &section);
section_p = elf_file_get_section(elf, name);
}
if(lookup_sections[i].type == SectionTypeRelData) {
section_p->rel_sec_idx = section_idx;
} else {
section_p->sec_idx = section_idx;
}
return lookup_sections[i].type;
} }
} }
// 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) { if(strcmp(name, ".symtab") == 0) {
FURI_LOG_D(TAG, "Found .symtab section"); FURI_LOG_D(TAG, "Found .symtab section");
elf->symbol_table = section_header->sh_offset; elf->symbol_table = section_header->sh_offset;
elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym); elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym);
return SectionTypeSymTab; return SectionTypeSymTab;
} else if(strcmp(name, ".strtab") == 0) { }
// Load string table
if(strcmp(name, ".strtab") == 0) {
FURI_LOG_D(TAG, "Found .strtab section"); FURI_LOG_D(TAG, "Found .strtab section");
elf->symbol_table_strings = section_header->sh_offset; elf->symbol_table_strings = section_header->sh_offset;
return SectionTypeStrTab; return SectionTypeStrTab;
} else if(strcmp(name, ".fapmeta") == 0) { }
// Load manifest section
if(strcmp(name, ".fapmeta") == 0) {
FURI_LOG_D(TAG, "Found .fapmeta section"); FURI_LOG_D(TAG, "Found .fapmeta section");
if(elf_load_metadata(elf, section_header, manifest)) { if(elf_load_metadata(elf, section_header, manifest)) {
return SectionTypeManifest; return SectionTypeManifest;
} else { } else {
return SectionTypeERROR; 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"); FURI_LOG_D(TAG, "Found .gnu_debuglink section");
if(elf_load_debug_link(elf, section_header)) { if(elf_load_debug_link(elf, section_header)) {
return SectionTypeDebugLink; return SectionTypeDebugLink;
@ -531,61 +553,17 @@ static SectionType elf_preload_section(
return SectionTypeUnused; 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, &section_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) { static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
Elf32_Shdr section_header; if(section->rel_count) {
if(section->rel_sec_idx) {
FURI_LOG_D(TAG, "Relocating section"); FURI_LOG_D(TAG, "Relocating section");
if(elf_read_section_header(elf, section->rel_sec_idx, &section_header)) return elf_relocate(elf, section);
return elf_relocate(elf, &section_header, section);
else {
FURI_LOG_E(TAG, "Error reading section header");
return false;
}
} else { } else {
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */ FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
} }
return true; return true;
} }
static void elf_file_call_section_list(ELFFile* elf, const char* name, bool reverse_order) { static void elf_file_call_section_list(ELFSection* section, bool reverse_order) {
ELFSection* section = elf_file_get_section(elf, name);
if(section && section->size) { if(section && section->size) {
const uint32_t* start = section->data; const uint32_t* start = section->data;
const uint32_t* end = section->data + section->size; 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_string_free(name);
FURI_LOG_D(TAG, "Load symbols done");
return IS_FLAGS_SET(loaded_sections, SectionTypeValid); return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
} }
@ -739,16 +716,6 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
ELFSectionDict_it_t it; ELFSectionDict_it_t it;
AddressCache_init(elf->relocation_cache); 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) { if(status == ELFFileLoadStatusSuccess) {
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); 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, "Relocation cache size: %u", AddressCache_size(elf->relocation_cache));
FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache)); FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache));
AddressCache_clear(elf->relocation_cache); AddressCache_clear(elf->relocation_cache);
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
return status; return status;
} }
void elf_file_pre_run(ELFFile* elf) { void elf_file_pre_run(ELFFile* elf) {
elf_file_call_section_list(elf, ".preinit_array", false); elf_file_call_section_list(elf->preinit_array, false);
elf_file_call_section_list(elf, ".init_array", false); elf_file_call_section_list(elf->init_array, false);
} }
int32_t elf_file_run(ELFFile* elf, void* args) { 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) { 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) { const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) {

View File

@ -16,8 +16,10 @@ typedef int32_t(entry_t)(void*);
typedef struct { typedef struct {
void* data; void* data;
uint16_t sec_idx; uint16_t sec_idx;
uint16_t rel_sec_idx;
Elf32_Word size; Elf32_Word size;
size_t rel_count;
Elf32_Off rel_offset;
} ELFSection; } ELFSection;
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
@ -39,6 +41,10 @@ struct ELFFile {
File* fd; File* fd;
const ElfApiInterface* api_interface; const ElfApiInterface* api_interface;
ELFDebugLinkInfo debug_link_info; ELFDebugLinkInfo debug_link_info;
ELFSection* preinit_array;
ELFSection* init_array;
ELFSection* fini_array;
}; };
#ifdef __cplusplus #ifdef __cplusplus