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 { |     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); | ||||||
|  | |||||||
| @ -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); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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[] = { |  | ||||||
|         {".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 { |  | ||||||
|         section_p->sec_idx = section_idx; |         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) { |     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, §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) { | 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, §ion_header)) |         return elf_relocate(elf, section); | ||||||
|             return elf_relocate(elf, §ion_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) { | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sergey Gavrilov
						Sergey Gavrilov