ELF-Loader: C++ plugin support, loader overhaul. (#1744)
* fap-loader: load all code and data sections * fap-loader: relocate all code and data sections * fap-loader: remove old elf loader * fap-loader: new jmp call relocation * openocd: resume on detach * fap-loader: trampoline for big jumps * fap-loader: rename cache * fap-loader: init_array support * fap-loader: untangled flipper_application into separate entities * fap-loader: fix debug * fap-loader: optimize section container * fap-loader: optimize key for section container * fap-loader: disable debug log * documentation * F7: bump api symbols version * Lib: cleanup elf_file.c Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									7e2008095e
								
							
						
					
					
						commit
						e6d22ed147
					
				| @ -25,7 +25,7 @@ static bool | |||||||
|     FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface); |     FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface); | ||||||
| 
 | 
 | ||||||
|     FlipperApplicationPreloadStatus preload_res = |     FlipperApplicationPreloadStatus preload_res = | ||||||
|         flipper_application_preload(app, string_get_cstr(path)); |         flipper_application_preload_manifest(app, string_get_cstr(path)); | ||||||
| 
 | 
 | ||||||
|     bool load_success = false; |     bool load_success = false; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -64,7 +64,7 @@ class AppState: | |||||||
| 
 | 
 | ||||||
|     def is_loaded_in_gdb(self, gdb_app) -> bool: |     def is_loaded_in_gdb(self, gdb_app) -> bool: | ||||||
|         # Avoid constructing full app wrapper for comparison |         # Avoid constructing full app wrapper for comparison | ||||||
|         return self.entry_address == int(gdb_app["entry"]) |         return self.entry_address == int(gdb_app["state"]["entry"]) | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]: |     def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]: | ||||||
| @ -78,13 +78,13 @@ class AppState: | |||||||
|     @staticmethod |     @staticmethod | ||||||
|     def from_gdb(gdb_app: "AppState") -> "AppState": |     def from_gdb(gdb_app: "AppState") -> "AppState": | ||||||
|         state = AppState(str(gdb_app["manifest"]["name"].string())) |         state = AppState(str(gdb_app["manifest"]["name"].string())) | ||||||
|         state.entry_address = int(gdb_app["entry"]) |         state.entry_address = int(gdb_app["state"]["entry"]) | ||||||
| 
 | 
 | ||||||
|         app_state = gdb_app["state"] |         app_state = gdb_app["state"] | ||||||
|         if debug_link_size := int(app_state["debug_link_size"]): |         if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): | ||||||
|             debug_link_data = ( |             debug_link_data = ( | ||||||
|                 gdb.selected_inferior() |                 gdb.selected_inferior() | ||||||
|                 .read_memory(int(app_state["debug_link"]), debug_link_size) |                 .read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size) | ||||||
|                 .tobytes() |                 .tobytes() | ||||||
|             ) |             ) | ||||||
|             state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( |             state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( | ||||||
|  | |||||||
| @ -101,3 +101,7 @@ $_TARGETNAME configure -event trace-config { | |||||||
|     # assignment |     # assignment | ||||||
|     mmw 0xE0042004 0x00000020 0 |     mmw 0xE0042004 0x00000020 0 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | $_TARGETNAME configure -event gdb-detach { | ||||||
|  |     resume | ||||||
|  | } | ||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,1.9,, | Version,+,1.10,, | ||||||
| Header,+,applications/services/bt/bt_service/bt.h,, | Header,+,applications/services/bt/bt_service/bt.h,, | ||||||
| Header,+,applications/services/cli/cli.h,, | Header,+,applications/services/cli/cli.h,, | ||||||
| Header,+,applications/services/cli/cli_vcp.h,, | Header,+,applications/services/cli/cli_vcp.h,, | ||||||
| @ -779,13 +779,13 @@ Function,-,fiprintf,int,"FILE*, const char*, ..." | |||||||
| Function,-,fiscanf,int,"FILE*, const char*, ..." | Function,-,fiscanf,int,"FILE*, const char*, ..." | ||||||
| Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" | Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" | ||||||
| Function,+,flipper_application_free,void,FlipperApplication* | Function,+,flipper_application_free,void,FlipperApplication* | ||||||
| Function,-,flipper_application_get_entry_address,const void*,FlipperApplication* |  | ||||||
| Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* | Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* | ||||||
| Function,-,flipper_application_get_state,const FlipperApplicationState*,FlipperApplication* |  | ||||||
| Function,-,flipper_application_get_thread,FuriThread*,FlipperApplication* |  | ||||||
| Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus | Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus | ||||||
|  | Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" | ||||||
|  | Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* | ||||||
| Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* | Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* | ||||||
| Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" | Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" | ||||||
|  | Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" | ||||||
| Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus | Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus | ||||||
| Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" | Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" | ||||||
| Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* | Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* | ||||||
|  | |||||||
| 
 | 
							
								
								
									
										21
									
								
								lib/flipper_application/application_manifest.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/flipper_application/application_manifest.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | #include "application_manifest.h" | ||||||
|  | 
 | ||||||
|  | bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) { | ||||||
|  |     if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) || | ||||||
|  |        (manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_application_manifest_is_compatible( | ||||||
|  |     const FlipperApplicationManifest* manifest, | ||||||
|  |     const ElfApiInterface* api_interface) { | ||||||
|  |     if(manifest->base.api_version.major != api_interface->api_version_major /* ||
 | ||||||
|  |        manifest->base.api_version.minor > app->api_interface->api_version_minor */) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
| @ -1,6 +1,12 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file application_manifest.h | ||||||
|  |  * Flipper application manifest | ||||||
|  |  */ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include "elf/elf_api_interface.h" | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -40,6 +46,25 @@ typedef FlipperApplicationManifestV1 FlipperApplicationManifest; | |||||||
| 
 | 
 | ||||||
| #pragma pack(pop) | #pragma pack(pop) | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Check if manifest is valid | ||||||
|  |  *  | ||||||
|  |  * @param manifest  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Check if manifest is compatible with current ELF API interface | ||||||
|  |  *  | ||||||
|  |  * @param manifest  | ||||||
|  |  * @param api_interface  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool flipper_application_manifest_is_compatible( | ||||||
|  |     const FlipperApplicationManifest* manifest, | ||||||
|  |     const ElfApiInterface* api_interface); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <flipper_application/elf/elf.h> | #include <elf.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| #define ELF_INVALID_ADDRESS 0xFFFFFFFF | #define ELF_INVALID_ADDRESS 0xFFFFFFFF | ||||||
|  | |||||||
							
								
								
									
										794
									
								
								lib/flipper_application/elf/elf_file.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										794
									
								
								lib/flipper_application/elf/elf_file.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,794 @@ | |||||||
|  | #include <elf.h> | ||||||
|  | #include "elf_file.h" | ||||||
|  | #include "elf_file_i.h" | ||||||
|  | #include "elf_api_interface.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "elf" | ||||||
|  | 
 | ||||||
|  | #define ELF_NAME_BUFFER_LEN 32 | ||||||
|  | #define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr)) | ||||||
|  | #define IS_FLAGS_SET(v, m) ((v & m) == m) | ||||||
|  | #define RESOLVER_THREAD_YIELD_STEP 30 | ||||||
|  | 
 | ||||||
|  | // #define ELF_DEBUG_LOG 1
 | ||||||
|  | 
 | ||||||
|  | #ifndef ELF_DEBUG_LOG | ||||||
|  | #undef FURI_LOG_D | ||||||
|  | #define FURI_LOG_D(...) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define TRAMPOLINE_CODE_SIZE 6 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  | ldr r12, [pc, #2] | ||||||
|  | bx r12 | ||||||
|  | */ | ||||||
|  | const uint8_t trampoline_code_little_endian[TRAMPOLINE_CODE_SIZE] = | ||||||
|  |     {0xdf, 0xf8, 0x02, 0xc0, 0x60, 0x47}; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t code[TRAMPOLINE_CODE_SIZE]; | ||||||
|  |     uint32_t addr; | ||||||
|  | } __attribute__((packed)) JMPTrampoline; | ||||||
|  | 
 | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | /********************************************* Caches *********************************************/ | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | 
 | ||||||
|  | static bool address_cache_get(AddressCache_t cache, int symEntry, Elf32_Addr* symAddr) { | ||||||
|  |     Elf32_Addr* addr = AddressCache_get(cache, symEntry); | ||||||
|  |     if(addr) { | ||||||
|  |         *symAddr = *addr; | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr symAddr) { | ||||||
|  |     AddressCache_set_at(cache, symEntry, symAddr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | /********************************************** ELF ***********************************************/ | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | 
 | ||||||
|  | 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 bool elf_read_string_from_offset(ELFFile* elf, off_t offset, string_t name) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     off_t old = storage_file_tell(elf->fd); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!storage_file_seek(elf->fd, offset, true)) break; | ||||||
|  | 
 | ||||||
|  |         char buffer[ELF_NAME_BUFFER_LEN + 1]; | ||||||
|  |         buffer[ELF_NAME_BUFFER_LEN] = 0; | ||||||
|  | 
 | ||||||
|  |         while(true) { | ||||||
|  |             uint16_t read = storage_file_read(elf->fd, buffer, ELF_NAME_BUFFER_LEN); | ||||||
|  |             string_cat_str(name, buffer); | ||||||
|  |             if(strlen(buffer) < ELF_NAME_BUFFER_LEN) { | ||||||
|  |                 result = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(storage_file_get_error(elf->fd) != FSE_OK || read == 0) break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } while(false); | ||||||
|  |     storage_file_seek(elf->fd, old, true); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_read_section_name(ELFFile* elf, off_t offset, string_t name) { | ||||||
|  |     return elf_read_string_from_offset(elf, elf->section_table_strings + offset, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_read_symbol_name(ELFFile* elf, off_t offset, string_t name) { | ||||||
|  |     return elf_read_string_from_offset(elf, elf->symbol_table_strings + offset, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_read_section_header(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header) { | ||||||
|  |     off_t offset = SECTION_OFFSET(elf, section_idx); | ||||||
|  |     return storage_file_seek(elf->fd, offset, true) && | ||||||
|  |            storage_file_read(elf->fd, section_header, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  |     elf_read_section(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header, string_t name) { | ||||||
|  |     if(!elf_read_section_header(elf, section_idx, section_header)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(section_header->sh_name && !elf_read_section_name(elf, section_header->sh_name, name)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_read_symbol(ELFFile* elf, int n, Elf32_Sym* sym, string_t name) { | ||||||
|  |     bool success = false; | ||||||
|  |     off_t old = storage_file_tell(elf->fd); | ||||||
|  |     off_t pos = elf->symbol_table + n * sizeof(Elf32_Sym); | ||||||
|  |     if(storage_file_seek(elf->fd, pos, true) && | ||||||
|  |        storage_file_read(elf->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) { | ||||||
|  |         if(sym->st_name) | ||||||
|  |             success = elf_read_symbol_name(elf, sym->st_name, name); | ||||||
|  |         else { | ||||||
|  |             Elf32_Shdr shdr; | ||||||
|  |             success = elf_read_section(elf, sym->st_shndx, &shdr, name); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     storage_file_seek(elf->fd, old, true); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ELFSection* elf_section_of(ELFFile* elf, int index) { | ||||||
|  |     ELFSectionDict_it_t it; | ||||||
|  |     for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { | ||||||
|  |         ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); | ||||||
|  |         if(itref->value.sec_idx == index) { | ||||||
|  |             return &itref->value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { | ||||||
|  |     if(sym->st_shndx == SHN_UNDEF) { | ||||||
|  |         Elf32_Addr addr = 0; | ||||||
|  |         if(elf->api_interface->resolver_callback(sName, &addr)) { | ||||||
|  |             return addr; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         ELFSection* symSec = elf_section_of(elf, sym->st_shndx); | ||||||
|  |         if(symSec) { | ||||||
|  |             return ((Elf32_Addr)symSec->data) + sym->st_value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "  Can not find address for symbol %s", sName); | ||||||
|  |     return ELF_INVALID_ADDRESS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | __attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) { | ||||||
|  | #define STRCASE(name) \ | ||||||
|  |     case name:        \ | ||||||
|  |         return #name; | ||||||
|  |     switch(symt) { | ||||||
|  |         STRCASE(R_ARM_NONE) | ||||||
|  |         STRCASE(R_ARM_TARGET1) | ||||||
|  |         STRCASE(R_ARM_ABS32) | ||||||
|  |         STRCASE(R_ARM_THM_PC22) | ||||||
|  |         STRCASE(R_ARM_THM_JUMP24) | ||||||
|  |     default: | ||||||
|  |         return "R_<unknow>"; | ||||||
|  |     } | ||||||
|  | #undef STRCASE | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static JMPTrampoline* elf_create_trampoline(Elf32_Addr addr) { | ||||||
|  |     JMPTrampoline* trampoline = malloc(sizeof(JMPTrampoline)); | ||||||
|  |     memcpy(trampoline->code, trampoline_code_little_endian, TRAMPOLINE_CODE_SIZE); | ||||||
|  |     trampoline->addr = addr; | ||||||
|  |     return trampoline; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { | ||||||
|  |     int offset, hi, lo, s, j1, j2, i1, i2, imm10, imm11; | ||||||
|  |     int to_thumb, is_call, blx_bit = 1 << 12; | ||||||
|  | 
 | ||||||
|  |     /* Get initial offset */ | ||||||
|  |     hi = ((uint16_t*)relAddr)[0]; | ||||||
|  |     lo = ((uint16_t*)relAddr)[1]; | ||||||
|  |     s = (hi >> 10) & 1; | ||||||
|  |     j1 = (lo >> 13) & 1; | ||||||
|  |     j2 = (lo >> 11) & 1; | ||||||
|  |     i1 = (j1 ^ s) ^ 1; | ||||||
|  |     i2 = (j2 ^ s) ^ 1; | ||||||
|  |     imm10 = hi & 0x3ff; | ||||||
|  |     imm11 = lo & 0x7ff; | ||||||
|  |     offset = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); | ||||||
|  |     if(offset & 0x01000000) offset -= 0x02000000; | ||||||
|  | 
 | ||||||
|  |     to_thumb = symAddr & 1; | ||||||
|  |     is_call = (type == R_ARM_THM_PC22); | ||||||
|  | 
 | ||||||
|  |     /* Store offset */ | ||||||
|  |     int offset_copy = offset; | ||||||
|  | 
 | ||||||
|  |     /* Compute final offset */ | ||||||
|  |     offset += symAddr - relAddr; | ||||||
|  |     if(!to_thumb && is_call) { | ||||||
|  |         blx_bit = 0; /* bl -> blx */ | ||||||
|  |         offset = (offset + 3) & -4; /* Compute offset from aligned PC */ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Check that relocation is possible
 | ||||||
|  |     * offset must not be out of range | ||||||
|  |     * if target is to be entered in arm mode: | ||||||
|  |         - bit 1 must not set | ||||||
|  |         - instruction must be a call (bl) or a jump to PLT */ | ||||||
|  |     if(!to_thumb || offset >= 0x1000000 || offset < -0x1000000) { | ||||||
|  |         if(to_thumb || (symAddr & 2) || (!is_call)) { | ||||||
|  |             FURI_LOG_D( | ||||||
|  |                 TAG, | ||||||
|  |                 "can't relocate value at %x, %s, doing trampoline", | ||||||
|  |                 relAddr, | ||||||
|  |                 elf_reloc_type_to_str(type)); | ||||||
|  | 
 | ||||||
|  |             Elf32_Addr addr; | ||||||
|  |             if(!address_cache_get(elf->trampoline_cache, symAddr, &addr)) { | ||||||
|  |                 addr = (Elf32_Addr)elf_create_trampoline(symAddr); | ||||||
|  |                 address_cache_put(elf->trampoline_cache, symAddr, addr); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             offset = offset_copy; | ||||||
|  |             offset += (int)addr - relAddr; | ||||||
|  |             if(!to_thumb && is_call) { | ||||||
|  |                 blx_bit = 0; /* bl -> blx */ | ||||||
|  |                 offset = (offset + 3) & -4; /* Compute offset from aligned PC */ | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Compute and store final offset */ | ||||||
|  |     s = (offset >> 24) & 1; | ||||||
|  |     i1 = (offset >> 23) & 1; | ||||||
|  |     i2 = (offset >> 22) & 1; | ||||||
|  |     j1 = s ^ (i1 ^ 1); | ||||||
|  |     j2 = s ^ (i2 ^ 1); | ||||||
|  |     imm10 = (offset >> 12) & 0x3ff; | ||||||
|  |     imm11 = (offset >> 1) & 0x7ff; | ||||||
|  |     (*(uint16_t*)relAddr) = (uint16_t)((hi & 0xf800) | (s << 10) | imm10); | ||||||
|  |     (*(uint16_t*)(relAddr + 2)) = | ||||||
|  |         (uint16_t)((lo & 0xc000) | (j1 << 13) | blx_bit | (j2 << 11) | imm11); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { | ||||||
|  |     switch(type) { | ||||||
|  |     case R_ARM_TARGET1: | ||||||
|  |     case R_ARM_ABS32: | ||||||
|  |         *((uint32_t*)relAddr) += symAddr; | ||||||
|  |         FURI_LOG_D(TAG, "  R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); | ||||||
|  |         break; | ||||||
|  |     case R_ARM_THM_PC22: | ||||||
|  |     case R_ARM_THM_JUMP24: | ||||||
|  |         elf_relocate_jmp_call(elf, relAddr, type, symAddr); | ||||||
|  |         FURI_LOG_D( | ||||||
|  |             TAG, "  R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         FURI_LOG_E(TAG, "  Undefined relocation %d", type); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) { | ||||||
|  |     if(s->data) { | ||||||
|  |         Elf32_Rel rel; | ||||||
|  |         size_t relEntries = h->sh_size / sizeof(rel); | ||||||
|  |         size_t relCount; | ||||||
|  |         (void)storage_file_seek(elf->fd, h->sh_offset, true); | ||||||
|  |         FURI_LOG_D(TAG, " Offset   Info     Type             Name"); | ||||||
|  | 
 | ||||||
|  |         int relocate_result = true; | ||||||
|  |         string_t symbol_name; | ||||||
|  |         string_init(symbol_name); | ||||||
|  | 
 | ||||||
|  |         for(relCount = 0; relCount < relEntries; relCount++) { | ||||||
|  |             if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) { | ||||||
|  |                 FURI_LOG_D(TAG, "  reloc YIELD"); | ||||||
|  |                 furi_delay_tick(1); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(storage_file_read(elf->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) { | ||||||
|  |                 FURI_LOG_E(TAG, "  reloc read fail"); | ||||||
|  |                 string_clear(symbol_name); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Elf32_Addr symAddr; | ||||||
|  | 
 | ||||||
|  |             int symEntry = ELF32_R_SYM(rel.r_info); | ||||||
|  |             int relType = ELF32_R_TYPE(rel.r_info); | ||||||
|  |             Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset; | ||||||
|  | 
 | ||||||
|  |             if(!address_cache_get(elf->relocation_cache, symEntry, &symAddr)) { | ||||||
|  |                 Elf32_Sym sym; | ||||||
|  |                 string_reset(symbol_name); | ||||||
|  |                 if(!elf_read_symbol(elf, symEntry, &sym, symbol_name)) { | ||||||
|  |                     FURI_LOG_E(TAG, "  symbol read fail"); | ||||||
|  |                     string_clear(symbol_name); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 FURI_LOG_D( | ||||||
|  |                     TAG, | ||||||
|  |                     " %08X %08X %-16s %s", | ||||||
|  |                     (unsigned int)rel.r_offset, | ||||||
|  |                     (unsigned int)rel.r_info, | ||||||
|  |                     elf_reloc_type_to_str(relType), | ||||||
|  |                     string_get_cstr(symbol_name)); | ||||||
|  | 
 | ||||||
|  |                 symAddr = elf_address_of(elf, &sym, string_get_cstr(symbol_name)); | ||||||
|  |                 address_cache_put(elf->relocation_cache, symEntry, symAddr); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(symAddr != ELF_INVALID_ADDRESS) { | ||||||
|  |                 FURI_LOG_D( | ||||||
|  |                     TAG, | ||||||
|  |                     "  symAddr=%08X relAddr=%08X", | ||||||
|  |                     (unsigned int)symAddr, | ||||||
|  |                     (unsigned int)relAddr); | ||||||
|  |                 if(!elf_relocate_symbol(elf, relAddr, relType, symAddr)) { | ||||||
|  |                     relocate_result = false; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_E(TAG, "  No symbol address of %s", string_get_cstr(symbol_name)); | ||||||
|  |                 relocate_result = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         string_clear(symbol_name); | ||||||
|  | 
 | ||||||
|  |         return relocate_result; | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_D(TAG, "Section not loaded"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | /********************************************* MISC ***********************************************/ | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | 
 | ||||||
|  | static bool cstr_prefix(const char* prefix, const char* string) { | ||||||
|  |     return strncmp(prefix, string, strlen(prefix)) == 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | /************************************ Internal FAP interfaces *************************************/ | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | typedef enum { | ||||||
|  |     SectionTypeERROR = 0, | ||||||
|  |     SectionTypeUnused = 1 << 0, | ||||||
|  |     SectionTypeData = 1 << 1, | ||||||
|  |     SectionTypeRelData = 1 << 2, | ||||||
|  |     SectionTypeSymTab = 1 << 3, | ||||||
|  |     SectionTypeStrTab = 1 << 4, | ||||||
|  |     SectionTypeManifest = 1 << 5, | ||||||
|  |     SectionTypeDebugLink = 1 << 6, | ||||||
|  | 
 | ||||||
|  |     SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab | SectionTypeManifest, | ||||||
|  | } SectionType; | ||||||
|  | 
 | ||||||
|  | static bool elf_load_metadata( | ||||||
|  |     ELFFile* elf, | ||||||
|  |     Elf32_Shdr* section_header, | ||||||
|  |     FlipperApplicationManifest* manifest) { | ||||||
|  |     if(section_header->sh_size < sizeof(FlipperApplicationManifest)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(manifest == NULL) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return storage_file_seek(elf->fd, section_header->sh_offset, true) && | ||||||
|  |            storage_file_read(elf->fd, manifest, section_header->sh_size) == | ||||||
|  |                section_header->sh_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) { | ||||||
|  |     elf->debug_link_info.debug_link_size = section_header->sh_size; | ||||||
|  |     elf->debug_link_info.debug_link = malloc(section_header->sh_size); | ||||||
|  | 
 | ||||||
|  |     return storage_file_seek(elf->fd, section_header->sh_offset, true) && | ||||||
|  |            storage_file_read(elf->fd, elf->debug_link_info.debug_link, section_header->sh_size) == | ||||||
|  |                section_header->sh_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SectionType elf_preload_section( | ||||||
|  |     ELFFile* elf, | ||||||
|  |     size_t section_idx, | ||||||
|  |     Elf32_Shdr* section_header, | ||||||
|  |     string_t name_string, | ||||||
|  |     FlipperApplicationManifest* manifest) { | ||||||
|  |     const char* name = 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 { | ||||||
|  |                 section_p->sec_idx = section_idx; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return lookup_sections[i].type; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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) { | ||||||
|  |         FURI_LOG_D(TAG, "Found .strtab section"); | ||||||
|  |         elf->symbol_table_strings = section_header->sh_offset; | ||||||
|  |         return SectionTypeStrTab; | ||||||
|  |     } else 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) { | ||||||
|  |         FURI_LOG_D(TAG, "Found .gnu_debuglink section"); | ||||||
|  |         if(elf_load_debug_link(elf, section_header)) { | ||||||
|  |             return SectionTypeDebugLink; | ||||||
|  |         } else { | ||||||
|  |             return SectionTypeERROR; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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) { | ||||||
|  |         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; | ||||||
|  |         } | ||||||
|  |     } 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); | ||||||
|  | 
 | ||||||
|  |     if(section && section->size) { | ||||||
|  |         const uint32_t* start = section->data; | ||||||
|  |         const uint32_t* end = section->data + section->size; | ||||||
|  | 
 | ||||||
|  |         if(reverse_order) { | ||||||
|  |             while(end > start) { | ||||||
|  |                 end--; | ||||||
|  |                 ((void (*)(void))(*end))(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             while(start < end) { | ||||||
|  |                 ((void (*)(void))(*start))(); | ||||||
|  |                 start++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | /********************************************* Public *********************************************/ | ||||||
|  | /**************************************************************************************************/ | ||||||
|  | 
 | ||||||
|  | ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) { | ||||||
|  |     ELFFile* elf = malloc(sizeof(ELFFile)); | ||||||
|  |     elf->fd = storage_file_alloc(storage); | ||||||
|  |     elf->api_interface = api_interface; | ||||||
|  |     ELFSectionDict_init(elf->sections); | ||||||
|  |     AddressCache_init(elf->trampoline_cache); | ||||||
|  |     return elf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void elf_file_free(ELFFile* elf) { | ||||||
|  |     // free sections data
 | ||||||
|  |     { | ||||||
|  |         ELFSectionDict_it_t it; | ||||||
|  |         for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); | ||||||
|  |             ELFSectionDict_next(it)) { | ||||||
|  |             const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); | ||||||
|  |             if(itref->value.data) { | ||||||
|  |                 aligned_free(itref->value.data); | ||||||
|  |             } | ||||||
|  |             free((void*)itref->key); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ELFSectionDict_clear(elf->sections); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // free trampoline data
 | ||||||
|  |     { | ||||||
|  |         AddressCache_it_t it; | ||||||
|  |         for(AddressCache_it(it, elf->trampoline_cache); !AddressCache_end_p(it); | ||||||
|  |             AddressCache_next(it)) { | ||||||
|  |             const AddressCache_itref_t* itref = AddressCache_cref(it); | ||||||
|  |             free((void*)itref->value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         AddressCache_clear(elf->trampoline_cache); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(elf->debug_link_info.debug_link) { | ||||||
|  |         free(elf->debug_link_info.debug_link); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     storage_file_free(elf->fd); | ||||||
|  |     free(elf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool elf_file_open(ELFFile* elf, const char* path) { | ||||||
|  |     Elf32_Ehdr h; | ||||||
|  |     Elf32_Shdr sH; | ||||||
|  | 
 | ||||||
|  |     if(!storage_file_open(elf->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) || | ||||||
|  |        !storage_file_seek(elf->fd, 0, true) || | ||||||
|  |        storage_file_read(elf->fd, &h, sizeof(h)) != sizeof(h) || | ||||||
|  |        !storage_file_seek(elf->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) || | ||||||
|  |        storage_file_read(elf->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     elf->entry = h.e_entry; | ||||||
|  |     elf->sections_count = h.e_shnum; | ||||||
|  |     elf->section_table = h.e_shoff; | ||||||
|  |     elf->section_table_strings = sH.sh_offset; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest) { | ||||||
|  |     bool result = false; | ||||||
|  |     string_t name; | ||||||
|  |     string_init(name); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Looking for manifest section"); | ||||||
|  |     for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) { | ||||||
|  |         Elf32_Shdr section_header; | ||||||
|  | 
 | ||||||
|  |         string_reset(name); | ||||||
|  |         if(!elf_read_section(elf, section_idx, §ion_header, name)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp(name, ".fapmeta") == 0) { | ||||||
|  |             if(elf_load_metadata(elf, §ion_header, manifest)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Load manifest done"); | ||||||
|  |                 result = true; | ||||||
|  |                 break; | ||||||
|  |             } else { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(name); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manifest) { | ||||||
|  |     SectionType loaded_sections = SectionTypeERROR; | ||||||
|  |     string_t name; | ||||||
|  |     string_init(name); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Scan ELF indexs..."); | ||||||
|  |     for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) { | ||||||
|  |         Elf32_Shdr section_header; | ||||||
|  | 
 | ||||||
|  |         string_reset(name); | ||||||
|  |         if(!elf_read_section(elf, section_idx, §ion_header, name)) { | ||||||
|  |             loaded_sections = SectionTypeERROR; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FURI_LOG_D(TAG, "Preloading data for section #%d %s", section_idx, string_get_cstr(name)); | ||||||
|  |         SectionType section_type = | ||||||
|  |             elf_preload_section(elf, section_idx, §ion_header, name, manifest); | ||||||
|  |         loaded_sections |= section_type; | ||||||
|  | 
 | ||||||
|  |         if(section_type == SectionTypeERROR) { | ||||||
|  |             loaded_sections = SectionTypeERROR; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(name); | ||||||
|  |     FURI_LOG_D(TAG, "Load symbols done"); | ||||||
|  | 
 | ||||||
|  |     return IS_FLAGS_SET(loaded_sections, SectionTypeValid); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { | ||||||
|  |     ELFFileLoadStatus status = ELFFileLoadStatusSuccess; | ||||||
|  |     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); | ||||||
|  |             ELFSectionDict_next(it)) { | ||||||
|  |             ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); | ||||||
|  |             FURI_LOG_D(TAG, "Relocating section '%s'", itref->key); | ||||||
|  |             if(!elf_relocate_section(elf, &itref->value)) { | ||||||
|  |                 FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key); | ||||||
|  |                 status = ELFFileLoadStatusMissingImports; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Fixing up entry point */ | ||||||
|  |     if(status == ELFFileLoadStatusSuccess) { | ||||||
|  |         ELFSection* text_section = elf_file_get_section(elf, ".text"); | ||||||
|  | 
 | ||||||
|  |         if(text_section == NULL) { | ||||||
|  |             FURI_LOG_E(TAG, "No .text section found"); | ||||||
|  |             status = ELFFileLoadStatusUnspecifiedError; | ||||||
|  |         } else { | ||||||
|  |             elf->entry += (uint32_t)text_section->data; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t elf_file_run(ELFFile* elf, void* args) { | ||||||
|  |     int32_t result; | ||||||
|  |     result = ((int32_t(*)(void*))elf->entry)(args); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void elf_file_post_run(ELFFile* elf) { | ||||||
|  |     elf_file_call_section_list(elf, ".fini_array", true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) { | ||||||
|  |     return elf_file->api_interface; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) { | ||||||
|  |     // set entry
 | ||||||
|  |     debug_info->entry = elf->entry; | ||||||
|  | 
 | ||||||
|  |     // copy debug info
 | ||||||
|  |     memcpy(&debug_info->debug_link_info, &elf->debug_link_info, sizeof(ELFDebugLinkInfo)); | ||||||
|  | 
 | ||||||
|  |     // init mmap
 | ||||||
|  |     debug_info->mmap_entry_count = ELFSectionDict_size(elf->sections); | ||||||
|  |     debug_info->mmap_entries = malloc(sizeof(ELFMemoryMapEntry) * debug_info->mmap_entry_count); | ||||||
|  |     uint32_t mmap_entry_idx = 0; | ||||||
|  | 
 | ||||||
|  |     ELFSectionDict_it_t it; | ||||||
|  |     for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { | ||||||
|  |         const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); | ||||||
|  | 
 | ||||||
|  |         const void* data_ptr = itref->value.data; | ||||||
|  |         if(data_ptr) { | ||||||
|  |             debug_info->mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr; | ||||||
|  |             debug_info->mmap_entries[mmap_entry_idx].name = itref->key; | ||||||
|  |             mmap_entry_idx++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void elf_file_clear_debug_info(ELFDebugInfo* debug_info) { | ||||||
|  |     // clear debug info
 | ||||||
|  |     memset(&debug_info->debug_link_info, 0, sizeof(ELFDebugLinkInfo)); | ||||||
|  | 
 | ||||||
|  |     // clear mmap
 | ||||||
|  |     if(debug_info->mmap_entries) { | ||||||
|  |         free(debug_info->mmap_entries); | ||||||
|  |         debug_info->mmap_entries = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     debug_info->mmap_entry_count = 0; | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								lib/flipper_application/elf/elf_file.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								lib/flipper_application/elf/elf_file.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file elf_file.h | ||||||
|  |  * ELF file loader | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include "../application_manifest.h" | ||||||
|  | #include "elf_api_interface.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct ELFFile ELFFile; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const char* name; | ||||||
|  |     uint32_t address; | ||||||
|  | } ELFMemoryMapEntry; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t debug_link_size; | ||||||
|  |     uint8_t* debug_link; | ||||||
|  | } ELFDebugLinkInfo; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t mmap_entry_count; | ||||||
|  |     ELFMemoryMapEntry* mmap_entries; | ||||||
|  |     ELFDebugLinkInfo debug_link_info; | ||||||
|  |     off_t entry; | ||||||
|  | } ELFDebugInfo; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     ELFFileLoadStatusSuccess = 0, | ||||||
|  |     ELFFileLoadStatusUnspecifiedError, | ||||||
|  |     ELFFileLoadStatusNoFreeMemory, | ||||||
|  |     ELFFileLoadStatusMissingImports, | ||||||
|  | } ELFFileLoadStatus; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Allocate ELFFile instance | ||||||
|  |  * @param storage  | ||||||
|  |  * @param api_interface  | ||||||
|  |  * @return ELFFile*  | ||||||
|  |  */ | ||||||
|  | ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Free ELFFile instance | ||||||
|  |  * @param elf_file  | ||||||
|  |  */ | ||||||
|  | void elf_file_free(ELFFile* elf_file); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Open ELF file | ||||||
|  |  * @param elf_file  | ||||||
|  |  * @param path  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool elf_file_open(ELFFile* elf_file, const char* path); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Load ELF file manifest | ||||||
|  |  * @param elf  | ||||||
|  |  * @param manifest  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Load ELF file section table (load stage #1) | ||||||
|  |  * @param elf_file  | ||||||
|  |  * @param manifest  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool elf_file_load_section_table(ELFFile* elf_file, FlipperApplicationManifest* manifest); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Load and relocate ELF file sections (load stage #2) | ||||||
|  |  * @param elf_file  | ||||||
|  |  * @return ELFFileLoadStatus  | ||||||
|  |  */ | ||||||
|  | ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3) | ||||||
|  |  * @param elf  | ||||||
|  |  */ | ||||||
|  | void elf_file_pre_run(ELFFile* elf); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Run ELF file (load stage #4) | ||||||
|  |  * @param elf_file  | ||||||
|  |  * @param args  | ||||||
|  |  * @return int32_t  | ||||||
|  |  */ | ||||||
|  | int32_t elf_file_run(ELFFile* elf_file, void* args); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Execute ELF file post-run stage, call static destructors for example (load stage #5) | ||||||
|  |  * @param elf  | ||||||
|  |  */ | ||||||
|  | void elf_file_post_run(ELFFile* elf); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get ELF file API interface | ||||||
|  |  * @param elf_file  | ||||||
|  |  * @return const ElfApiInterface*  | ||||||
|  |  */ | ||||||
|  | const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get ELF file debug info | ||||||
|  |  * @param elf_file  | ||||||
|  |  * @param debug_info  | ||||||
|  |  */ | ||||||
|  | void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Clear ELF file debug info generated by elf_file_init_debug_info | ||||||
|  |  * @param debug_info  | ||||||
|  |  */ | ||||||
|  | void elf_file_clear_debug_info(ELFDebugInfo* debug_info); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										46
									
								
								lib/flipper_application/elf/elf_file_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/flipper_application/elf/elf_file_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "elf_file.h" | ||||||
|  | #include <m-dict.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Callable elf entry type | ||||||
|  |  */ | ||||||
|  | typedef int32_t(entry_t)(void*); | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     void* data; | ||||||
|  |     uint16_t sec_idx; | ||||||
|  |     uint16_t rel_sec_idx; | ||||||
|  |     Elf32_Word size; | ||||||
|  | } ELFSection; | ||||||
|  | 
 | ||||||
|  | DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) | ||||||
|  | 
 | ||||||
|  | struct ELFFile { | ||||||
|  |     size_t sections_count; | ||||||
|  |     off_t section_table; | ||||||
|  |     off_t section_table_strings; | ||||||
|  | 
 | ||||||
|  |     size_t symbol_count; | ||||||
|  |     off_t symbol_table; | ||||||
|  |     off_t symbol_table_strings; | ||||||
|  |     off_t entry; | ||||||
|  |     ELFSectionDict_t sections; | ||||||
|  | 
 | ||||||
|  |     AddressCache_t relocation_cache; | ||||||
|  |     AddressCache_t trampoline_cache; | ||||||
|  | 
 | ||||||
|  |     File* fd; | ||||||
|  |     const ElfApiInterface* api_interface; | ||||||
|  |     ELFDebugLinkInfo debug_link_info; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,477 +0,0 @@ | |||||||
| #include "flipper_application_i.h" |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| #define TAG "fapp-i" |  | ||||||
| 
 |  | ||||||
| #define RESOLVER_THREAD_YIELD_STEP 30 |  | ||||||
| 
 |  | ||||||
| #define IS_FLAGS_SET(v, m) ((v & m) == m) |  | ||||||
| #define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr)) |  | ||||||
| #define SYMBOL_OFFSET(e, n) (e->_table + n * sizeof(Elf32_Shdr)) |  | ||||||
| 
 |  | ||||||
| bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path) { |  | ||||||
|     Elf32_Ehdr h; |  | ||||||
|     Elf32_Shdr sH; |  | ||||||
| 
 |  | ||||||
|     if(!storage_file_open(e->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) || |  | ||||||
|        !storage_file_seek(e->fd, 0, true) || |  | ||||||
|        storage_file_read(e->fd, &h, sizeof(h)) != sizeof(h) || |  | ||||||
|        !storage_file_seek(e->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) || |  | ||||||
|        storage_file_read(e->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     e->entry = h.e_entry; |  | ||||||
|     e->sections = h.e_shnum; |  | ||||||
|     e->section_table = h.e_shoff; |  | ||||||
|     e->section_table_strings = sH.sh_offset; |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool flipper_application_load_metadata(FlipperApplication* e, Elf32_Shdr* sh) { |  | ||||||
|     if(sh->sh_size < sizeof(e->manifest)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return storage_file_seek(e->fd, sh->sh_offset, true) && |  | ||||||
|            storage_file_read(e->fd, &e->manifest, sh->sh_size) == sh->sh_size; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool flipper_application_load_debug_link(FlipperApplication* e, Elf32_Shdr* sh) { |  | ||||||
|     e->state.debug_link_size = sh->sh_size; |  | ||||||
|     e->state.debug_link = malloc(sh->sh_size); |  | ||||||
| 
 |  | ||||||
|     return storage_file_seek(e->fd, sh->sh_offset, true) && |  | ||||||
|            storage_file_read(e->fd, e->state.debug_link, sh->sh_size) == sh->sh_size; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static FindFlags_t flipper_application_preload_section( |  | ||||||
|     FlipperApplication* e, |  | ||||||
|     Elf32_Shdr* sh, |  | ||||||
|     const char* name, |  | ||||||
|     int n) { |  | ||||||
|     FURI_LOG_D(TAG, "Processing: %s", name); |  | ||||||
| 
 |  | ||||||
|     const struct { |  | ||||||
|         const char* name; |  | ||||||
|         uint16_t* ptr_section_idx; |  | ||||||
|         FindFlags_t flags; |  | ||||||
|     } lookup_sections[] = { |  | ||||||
|         {".text", &e->text.sec_idx, FoundText}, |  | ||||||
|         {".rodata", &e->rodata.sec_idx, FoundRodata}, |  | ||||||
|         {".data", &e->data.sec_idx, FoundData}, |  | ||||||
|         {".bss", &e->bss.sec_idx, FoundBss}, |  | ||||||
|         {".rel.text", &e->text.rel_sec_idx, FoundRelText}, |  | ||||||
|         {".rel.rodata", &e->rodata.rel_sec_idx, FoundRelRodata}, |  | ||||||
|         {".rel.data", &e->data.rel_sec_idx, FoundRelData}, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) { |  | ||||||
|         if(strcmp(name, lookup_sections[i].name) == 0) { |  | ||||||
|             *lookup_sections[i].ptr_section_idx = n; |  | ||||||
|             return lookup_sections[i].flags; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(strcmp(name, ".symtab") == 0) { |  | ||||||
|         e->symbol_table = sh->sh_offset; |  | ||||||
|         e->symbol_count = sh->sh_size / sizeof(Elf32_Sym); |  | ||||||
|         return FoundSymTab; |  | ||||||
|     } else if(strcmp(name, ".strtab") == 0) { |  | ||||||
|         e->symbol_table_strings = sh->sh_offset; |  | ||||||
|         return FoundStrTab; |  | ||||||
|     } else if(strcmp(name, ".fapmeta") == 0) { |  | ||||||
|         // Load metadata immediately
 |  | ||||||
|         if(flipper_application_load_metadata(e, sh)) { |  | ||||||
|             return FoundFappManifest; |  | ||||||
|         } |  | ||||||
|     } else if(strcmp(name, ".gnu_debuglink") == 0) { |  | ||||||
|         if(flipper_application_load_debug_link(e, sh)) { |  | ||||||
|             return FoundDebugLink; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return FoundERROR; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool |  | ||||||
|     read_string_from_offset(FlipperApplication* e, off_t offset, char* buffer, size_t buffer_size) { |  | ||||||
|     bool success = false; |  | ||||||
| 
 |  | ||||||
|     off_t old = storage_file_tell(e->fd); |  | ||||||
|     if(storage_file_seek(e->fd, offset, true) && |  | ||||||
|        (storage_file_read(e->fd, buffer, buffer_size) == buffer_size)) { |  | ||||||
|         success = true; |  | ||||||
|     } |  | ||||||
|     storage_file_seek(e->fd, old, true); |  | ||||||
| 
 |  | ||||||
|     return success; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool read_section_name(FlipperApplication* e, off_t off, char* buf, size_t max) { |  | ||||||
|     return read_string_from_offset(e, e->section_table_strings + off, buf, max); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool read_symbol_name(FlipperApplication* e, off_t off, char* buf, size_t max) { |  | ||||||
|     return read_string_from_offset(e, e->symbol_table_strings + off, buf, max); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool read_section_header(FlipperApplication* e, int n, Elf32_Shdr* h) { |  | ||||||
|     off_t offset = SECTION_OFFSET(e, n); |  | ||||||
|     return storage_file_seek(e->fd, offset, true) && |  | ||||||
|            storage_file_read(e->fd, h, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool read_section(FlipperApplication* e, int n, Elf32_Shdr* h, char* name, size_t nlen) { |  | ||||||
|     if(!read_section_header(e, n, h)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     if(!h->sh_name) { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|     return read_section_name(e, h->sh_name, name, nlen); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool flipper_application_load_section_table(FlipperApplication* e) { |  | ||||||
|     furi_check(e->state.mmap_entry_count == 0); |  | ||||||
| 
 |  | ||||||
|     size_t n; |  | ||||||
|     FindFlags_t found = FoundERROR; |  | ||||||
|     FURI_LOG_D(TAG, "Scan ELF indexs..."); |  | ||||||
|     for(n = 1; n < e->sections; n++) { |  | ||||||
|         Elf32_Shdr section_header; |  | ||||||
|         char name[33] = {0}; |  | ||||||
|         if(!read_section_header(e, n, §ion_header)) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         if(section_header.sh_name && |  | ||||||
|            !read_section_name(e, section_header.sh_name, name, sizeof(name))) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         FURI_LOG_T(TAG, "Examining section %d %s", n, name); |  | ||||||
|         FindFlags_t section_flags = |  | ||||||
|             flipper_application_preload_section(e, §ion_header, name, n); |  | ||||||
|         found |= section_flags; |  | ||||||
|         if((section_flags & FoundGdbSection) != 0) { |  | ||||||
|             e->state.mmap_entry_count++; |  | ||||||
|         } |  | ||||||
|         if(IS_FLAGS_SET(found, FoundAll)) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_D(TAG, "Load symbols done"); |  | ||||||
|     return IS_FLAGS_SET(found, FoundValid); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const char* type_to_str(int symt) { |  | ||||||
| #define STRCASE(name) \ |  | ||||||
|     case name:        \ |  | ||||||
|         return #name; |  | ||||||
|     switch(symt) { |  | ||||||
|         STRCASE(R_ARM_NONE) |  | ||||||
|         STRCASE(R_ARM_ABS32) |  | ||||||
|         STRCASE(R_ARM_THM_PC22) |  | ||||||
|         STRCASE(R_ARM_THM_JUMP24) |  | ||||||
|     default: |  | ||||||
|         return "R_<unknow>"; |  | ||||||
|     } |  | ||||||
| #undef STRCASE |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void relocate_jmp_call(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { |  | ||||||
|     UNUSED(type); |  | ||||||
|     uint16_t upper_insn = ((uint16_t*)relAddr)[0]; |  | ||||||
|     uint16_t lower_insn = ((uint16_t*)relAddr)[1]; |  | ||||||
|     uint32_t S = (upper_insn >> 10) & 1; |  | ||||||
|     uint32_t J1 = (lower_insn >> 13) & 1; |  | ||||||
|     uint32_t J2 = (lower_insn >> 11) & 1; |  | ||||||
| 
 |  | ||||||
|     int32_t offset = (S << 24) | /* S     -> offset[24] */ |  | ||||||
|                      ((~(J1 ^ S) & 1) << 23) | /* J1    -> offset[23] */ |  | ||||||
|                      ((~(J2 ^ S) & 1) << 22) | /* J2    -> offset[22] */ |  | ||||||
|                      ((upper_insn & 0x03ff) << 12) | /* imm10 -> offset[12:21] */ |  | ||||||
|                      ((lower_insn & 0x07ff) << 1); /* imm11 -> offset[1:11] */ |  | ||||||
|     if(offset & 0x01000000) offset -= 0x02000000; |  | ||||||
| 
 |  | ||||||
|     offset += symAddr - relAddr; |  | ||||||
| 
 |  | ||||||
|     S = (offset >> 24) & 1; |  | ||||||
|     J1 = S ^ (~(offset >> 23) & 1); |  | ||||||
|     J2 = S ^ (~(offset >> 22) & 1); |  | ||||||
| 
 |  | ||||||
|     upper_insn = ((upper_insn & 0xf800) | (S << 10) | ((offset >> 12) & 0x03ff)); |  | ||||||
|     ((uint16_t*)relAddr)[0] = upper_insn; |  | ||||||
| 
 |  | ||||||
|     lower_insn = ((lower_insn & 0xd000) | (J1 << 13) | (J2 << 11) | ((offset >> 1) & 0x07ff)); |  | ||||||
|     ((uint16_t*)relAddr)[1] = lower_insn; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool relocate_symbol(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { |  | ||||||
|     switch(type) { |  | ||||||
|     case R_ARM_ABS32: |  | ||||||
|         *((uint32_t*)relAddr) += symAddr; |  | ||||||
|         FURI_LOG_D(TAG, "  R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); |  | ||||||
|         break; |  | ||||||
|     case R_ARM_THM_PC22: |  | ||||||
|     case R_ARM_THM_JUMP24: |  | ||||||
|         relocate_jmp_call(relAddr, type, symAddr); |  | ||||||
|         FURI_LOG_D( |  | ||||||
|             TAG, "  R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         FURI_LOG_D(TAG, "  Undefined relocation %d", type); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static ELFSection_t* section_of(FlipperApplication* e, int index) { |  | ||||||
|     if(e->text.sec_idx == index) { |  | ||||||
|         return &e->text; |  | ||||||
|     } else if(e->data.sec_idx == index) { |  | ||||||
|         return &e->data; |  | ||||||
|     } else if(e->bss.sec_idx == index) { |  | ||||||
|         return &e->bss; |  | ||||||
|     } else if(e->rodata.sec_idx == index) { |  | ||||||
|         return &e->rodata; |  | ||||||
|     } |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static Elf32_Addr address_of(FlipperApplication* e, Elf32_Sym* sym, const char* sName) { |  | ||||||
|     if(sym->st_shndx == SHN_UNDEF) { |  | ||||||
|         Elf32_Addr addr = 0; |  | ||||||
|         if(e->api_interface->resolver_callback(sName, &addr)) { |  | ||||||
|             return addr; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         ELFSection_t* symSec = section_of(e, sym->st_shndx); |  | ||||||
|         if(symSec) { |  | ||||||
|             return ((Elf32_Addr)symSec->data) + sym->st_value; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     FURI_LOG_D(TAG, "  Can not find address for symbol %s", sName); |  | ||||||
|     return ELF_INVALID_ADDRESS; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool read_symbol(FlipperApplication* e, int n, Elf32_Sym* sym, char* name, size_t nlen) { |  | ||||||
|     bool success = false; |  | ||||||
|     off_t old = storage_file_tell(e->fd); |  | ||||||
|     off_t pos = e->symbol_table + n * sizeof(Elf32_Sym); |  | ||||||
|     if(storage_file_seek(e->fd, pos, true) && |  | ||||||
|        storage_file_read(e->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) { |  | ||||||
|         if(sym->st_name) |  | ||||||
|             success = read_symbol_name(e, sym->st_name, name, nlen); |  | ||||||
|         else { |  | ||||||
|             Elf32_Shdr shdr; |  | ||||||
|             success = read_section(e, sym->st_shndx, &shdr, name, nlen); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     storage_file_seek(e->fd, old, true); |  | ||||||
|     return success; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool |  | ||||||
|     relocation_cache_get(RelocationAddressCache_t cache, int symEntry, Elf32_Addr* symAddr) { |  | ||||||
|     Elf32_Addr* addr = RelocationAddressCache_get(cache, symEntry); |  | ||||||
|     if(addr) { |  | ||||||
|         *symAddr = *addr; |  | ||||||
|         return true; |  | ||||||
|     } else { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void |  | ||||||
|     relocation_cache_put(RelocationAddressCache_t cache, int symEntry, Elf32_Addr symAddr) { |  | ||||||
|     RelocationAddressCache_set_at(cache, symEntry, symAddr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define MAX_SYMBOL_NAME_LEN 128u |  | ||||||
| 
 |  | ||||||
| static bool relocate(FlipperApplication* e, Elf32_Shdr* h, ELFSection_t* s) { |  | ||||||
|     if(s->data) { |  | ||||||
|         Elf32_Rel rel; |  | ||||||
|         size_t relEntries = h->sh_size / sizeof(rel); |  | ||||||
|         size_t relCount; |  | ||||||
|         (void)storage_file_seek(e->fd, h->sh_offset, true); |  | ||||||
|         FURI_LOG_D(TAG, " Offset   Info     Type             Name"); |  | ||||||
| 
 |  | ||||||
|         int relocate_result = true; |  | ||||||
|         char symbol_name[MAX_SYMBOL_NAME_LEN + 1] = {0}; |  | ||||||
| 
 |  | ||||||
|         for(relCount = 0; relCount < relEntries; relCount++) { |  | ||||||
|             if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) { |  | ||||||
|                 FURI_LOG_D(TAG, "  reloc YIELD"); |  | ||||||
|                 furi_delay_tick(1); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(storage_file_read(e->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) { |  | ||||||
|                 FURI_LOG_E(TAG, "  reloc read fail"); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Elf32_Addr symAddr; |  | ||||||
| 
 |  | ||||||
|             int symEntry = ELF32_R_SYM(rel.r_info); |  | ||||||
|             int relType = ELF32_R_TYPE(rel.r_info); |  | ||||||
|             Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset; |  | ||||||
| 
 |  | ||||||
|             if(!relocation_cache_get(e->relocation_cache, symEntry, &symAddr)) { |  | ||||||
|                 Elf32_Sym sym; |  | ||||||
|                 if(!read_symbol(e, symEntry, &sym, symbol_name, MAX_SYMBOL_NAME_LEN)) { |  | ||||||
|                     FURI_LOG_E(TAG, "  symbol read fail"); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 FURI_LOG_D( |  | ||||||
|                     TAG, |  | ||||||
|                     " %08X %08X %-16s %s", |  | ||||||
|                     (unsigned int)rel.r_offset, |  | ||||||
|                     (unsigned int)rel.r_info, |  | ||||||
|                     type_to_str(relType), |  | ||||||
|                     symbol_name); |  | ||||||
| 
 |  | ||||||
|                 symAddr = address_of(e, &sym, symbol_name); |  | ||||||
|                 relocation_cache_put(e->relocation_cache, symEntry, symAddr); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(symAddr != ELF_INVALID_ADDRESS) { |  | ||||||
|                 FURI_LOG_D( |  | ||||||
|                     TAG, |  | ||||||
|                     "  symAddr=%08X relAddr=%08X", |  | ||||||
|                     (unsigned int)symAddr, |  | ||||||
|                     (unsigned int)relAddr); |  | ||||||
|                 if(!relocate_symbol(relAddr, relType, symAddr)) { |  | ||||||
|                     relocate_result = false; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 FURI_LOG_D(TAG, "  No symbol address of %s", symbol_name); |  | ||||||
|                 relocate_result = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return relocate_result; |  | ||||||
|     } else |  | ||||||
|         FURI_LOG_I(TAG, "Section not loaded"); |  | ||||||
| 
 |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool flipper_application_load_section_data(FlipperApplication* e, ELFSection_t* s) { |  | ||||||
|     Elf32_Shdr section_header; |  | ||||||
|     if(s->sec_idx == 0) { |  | ||||||
|         FURI_LOG_I(TAG, "Section is not present"); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!read_section_header(e, s->sec_idx, §ion_header)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(section_header.sh_size == 0) { |  | ||||||
|         FURI_LOG_I(TAG, "No data for section"); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     s->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign); |  | ||||||
|     // e->state.mmap_entry_count++;
 |  | ||||||
| 
 |  | ||||||
|     if(section_header.sh_type == SHT_NOBITS) { |  | ||||||
|         /* section is empty (.bss?) */ |  | ||||||
|         /* no need to memset - allocator already did that */ |  | ||||||
|         /* memset(s->data, 0, h->sh_size); */ |  | ||||||
|         FURI_LOG_D(TAG, "0x%X", s->data); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if((!storage_file_seek(e->fd, section_header.sh_offset, true)) || |  | ||||||
|        (storage_file_read(e->fd, s->data, section_header.sh_size) != section_header.sh_size)) { |  | ||||||
|         FURI_LOG_E(TAG, "    seek/read fail"); |  | ||||||
|         flipper_application_free_section(s); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_D(TAG, "0x%X", s->data); |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool flipper_application_relocate_section(FlipperApplication* e, ELFSection_t* s) { |  | ||||||
|     Elf32_Shdr section_header; |  | ||||||
|     if(s->rel_sec_idx) { |  | ||||||
|         FURI_LOG_D(TAG, "Relocating section"); |  | ||||||
|         if(read_section_header(e, s->rel_sec_idx, §ion_header)) |  | ||||||
|             return relocate(e, §ion_header, s); |  | ||||||
|         else { |  | ||||||
|             FURI_LOG_E(TAG, "Error reading section header"); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } else |  | ||||||
|         FURI_LOG_D(TAG, "No relocation index"); /* Not an error */ |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e) { |  | ||||||
|     FlipperApplicationLoadStatus status = FlipperApplicationLoadStatusSuccess; |  | ||||||
|     RelocationAddressCache_init(e->relocation_cache); |  | ||||||
|     size_t start = furi_get_tick(); |  | ||||||
| 
 |  | ||||||
|     struct { |  | ||||||
|         ELFSection_t* section; |  | ||||||
|         const char* name; |  | ||||||
|     } sections[] = { |  | ||||||
|         {&e->text, ".text"}, |  | ||||||
|         {&e->rodata, ".rodata"}, |  | ||||||
|         {&e->data, ".data"}, |  | ||||||
|         {&e->bss, ".bss"}, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     for(size_t i = 0; i < COUNT_OF(sections); i++) { |  | ||||||
|         if(!flipper_application_load_section_data(e, sections[i].section)) { |  | ||||||
|             FURI_LOG_E(TAG, "Error loading section '%s'", sections[i].name); |  | ||||||
|             status = FlipperApplicationLoadStatusUnspecifiedError; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(status == FlipperApplicationLoadStatusSuccess) { |  | ||||||
|         for(size_t i = 0; i < COUNT_OF(sections); i++) { |  | ||||||
|             if(!flipper_application_relocate_section(e, sections[i].section)) { |  | ||||||
|                 FURI_LOG_E(TAG, "Error relocating section '%s'", sections[i].name); |  | ||||||
|                 status = FlipperApplicationLoadStatusMissingImports; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(status == FlipperApplicationLoadStatusSuccess) { |  | ||||||
|         e->state.mmap_entries = |  | ||||||
|             malloc(sizeof(FlipperApplicationMemoryMapEntry) * e->state.mmap_entry_count); |  | ||||||
|         uint32_t mmap_entry_idx = 0; |  | ||||||
|         for(size_t i = 0; i < COUNT_OF(sections); i++) { |  | ||||||
|             const void* data_ptr = sections[i].section->data; |  | ||||||
|             if(data_ptr) { |  | ||||||
|                 FURI_LOG_I(TAG, "0x%X %s", (uint32_t)data_ptr, sections[i].name); |  | ||||||
|                 e->state.mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr; |  | ||||||
|                 e->state.mmap_entries[mmap_entry_idx].name = sections[i].name; |  | ||||||
|                 mmap_entry_idx++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         furi_check(mmap_entry_idx == e->state.mmap_entry_count); |  | ||||||
| 
 |  | ||||||
|         /* Fixing up entry point */ |  | ||||||
|         e->entry += (uint32_t)e->text.data; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_D(TAG, "Relocation cache size: %u", RelocationAddressCache_size(e->relocation_cache)); |  | ||||||
|     RelocationAddressCache_clear(e->relocation_cache); |  | ||||||
|     FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); |  | ||||||
| 
 |  | ||||||
|     return status; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void flipper_application_free_section(ELFSection_t* s) { |  | ||||||
|     if(s->data) { |  | ||||||
|         aligned_free(s->data); |  | ||||||
|     } |  | ||||||
|     s->data = NULL; |  | ||||||
| } |  | ||||||
| @ -1,16 +1,22 @@ | |||||||
| #include "flipper_application.h" | #include "flipper_application.h" | ||||||
| #include "flipper_application_i.h" | #include "elf/elf_file.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "fapp" | #define TAG "fapp" | ||||||
| 
 | 
 | ||||||
|  | struct FlipperApplication { | ||||||
|  |     ELFDebugInfo state; | ||||||
|  |     FlipperApplicationManifest manifest; | ||||||
|  |     ELFFile* elf; | ||||||
|  |     FuriThread* thread; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /* For debugger access to app state */ | /* For debugger access to app state */ | ||||||
| FlipperApplication* last_loaded_app = NULL; | FlipperApplication* last_loaded_app = NULL; | ||||||
| 
 | 
 | ||||||
| FlipperApplication* | FlipperApplication* | ||||||
|     flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) { |     flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) { | ||||||
|     FlipperApplication* app = malloc(sizeof(FlipperApplication)); |     FlipperApplication* app = malloc(sizeof(FlipperApplication)); | ||||||
|     app->api_interface = api_interface; |     app->elf = elf_file_alloc(storage, api_interface); | ||||||
|     app->fd = storage_file_alloc(storage); |  | ||||||
|     app->thread = NULL; |     app->thread = NULL; | ||||||
|     return app; |     return app; | ||||||
| } | } | ||||||
| @ -25,56 +31,71 @@ void flipper_application_free(FlipperApplication* app) { | |||||||
| 
 | 
 | ||||||
|     last_loaded_app = NULL; |     last_loaded_app = NULL; | ||||||
| 
 | 
 | ||||||
|     if(app->state.debug_link_size) { |     elf_file_clear_debug_info(&app->state); | ||||||
|         free(app->state.debug_link); |     elf_file_free(app->elf); | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(app->state.mmap_entries) { |  | ||||||
|         free(app->state.mmap_entries); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ELFSection_t* sections[] = {&app->text, &app->rodata, &app->data, &app->bss}; |  | ||||||
|     for(size_t i = 0; i < COUNT_OF(sections); i++) { |  | ||||||
|         flipper_application_free_section(sections[i]); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     storage_file_free(app->fd); |  | ||||||
| 
 |  | ||||||
|     free(app); |     free(app); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Parse headers, load manifest */ | static FlipperApplicationPreloadStatus | ||||||
| FlipperApplicationPreloadStatus |     flipper_application_validate_manifest(FlipperApplication* app) { | ||||||
|     flipper_application_preload(FlipperApplication* app, const char* path) { |     if(!flipper_application_manifest_is_valid(&app->manifest)) { | ||||||
|     if(!flipper_application_load_elf_headers(app, path) || |  | ||||||
|        !flipper_application_load_section_table(app)) { |  | ||||||
|         return FlipperApplicationPreloadStatusInvalidFile; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if((app->manifest.base.manifest_magic != FAP_MANIFEST_MAGIC) && |  | ||||||
|        (app->manifest.base.manifest_version == FAP_MANIFEST_SUPPORTED_VERSION)) { |  | ||||||
|         return FlipperApplicationPreloadStatusInvalidManifest; |         return FlipperApplicationPreloadStatusInvalidManifest; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(app->manifest.base.api_version.major != app->api_interface->api_version_major /* ||
 |     if(!flipper_application_manifest_is_compatible( | ||||||
|        app->manifest.base.api_version.minor > app->api_interface->api_version_minor */) { |            &app->manifest, elf_file_get_api_interface(app->elf))) { | ||||||
|         return FlipperApplicationPreloadStatusApiMismatch; |         return FlipperApplicationPreloadStatusApiMismatch; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return FlipperApplicationPreloadStatusSuccess; |     return FlipperApplicationPreloadStatusSuccess; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Parse headers, load manifest */ | ||||||
|  | FlipperApplicationPreloadStatus | ||||||
|  |     flipper_application_preload_manifest(FlipperApplication* app, const char* path) { | ||||||
|  |     if(!elf_file_open(app->elf, path) || !elf_file_load_manifest(app->elf, &app->manifest)) { | ||||||
|  |         return FlipperApplicationPreloadStatusInvalidFile; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return flipper_application_validate_manifest(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Parse headers, load full file */ | ||||||
|  | FlipperApplicationPreloadStatus | ||||||
|  |     flipper_application_preload(FlipperApplication* app, const char* path) { | ||||||
|  |     if(!elf_file_open(app->elf, path) || !elf_file_load_section_table(app->elf, &app->manifest)) { | ||||||
|  |         return FlipperApplicationPreloadStatusInvalidFile; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return flipper_application_validate_manifest(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) { | const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) { | ||||||
|     return &app->manifest; |     return &app->manifest; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) { | FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) { | ||||||
|     last_loaded_app = app; |     last_loaded_app = app; | ||||||
|     return flipper_application_load_sections(app); |     ELFFileLoadStatus status = elf_file_load_sections(app->elf); | ||||||
|  | 
 | ||||||
|  |     switch(status) { | ||||||
|  |     case ELFFileLoadStatusSuccess: | ||||||
|  |         elf_file_init_debug_info(app->elf, &app->state); | ||||||
|  |         return FlipperApplicationLoadStatusSuccess; | ||||||
|  |     case ELFFileLoadStatusNoFreeMemory: | ||||||
|  |         return FlipperApplicationLoadStatusNoFreeMemory; | ||||||
|  |     case ELFFileLoadStatusMissingImports: | ||||||
|  |         return FlipperApplicationLoadStatusMissingImports; | ||||||
|  |     default: | ||||||
|  |         return FlipperApplicationLoadStatusUnspecifiedError; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app) { | static int32_t flipper_application_thread(void* context) { | ||||||
|     return &app->state; |     elf_file_pre_run(last_loaded_app->elf); | ||||||
|  |     int32_t result = elf_file_run(last_loaded_app->elf, context); | ||||||
|  |     elf_file_post_run(last_loaded_app->elf); | ||||||
|  |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { | FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { | ||||||
| @ -86,20 +107,12 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { | |||||||
|     app->thread = furi_thread_alloc(); |     app->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_stack_size(app->thread, manifest->stack_size); |     furi_thread_set_stack_size(app->thread, manifest->stack_size); | ||||||
|     furi_thread_set_name(app->thread, manifest->name); |     furi_thread_set_name(app->thread, manifest->name); | ||||||
|     furi_thread_set_callback(app->thread, (entry_t*)app->entry); |     furi_thread_set_callback(app->thread, flipper_application_thread); | ||||||
|     furi_thread_set_context(app->thread, args); |     furi_thread_set_context(app->thread, args); | ||||||
| 
 | 
 | ||||||
|     return app->thread; |     return app->thread; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriThread* flipper_application_get_thread(FlipperApplication* app) { |  | ||||||
|     return app->thread; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void const* flipper_application_get_entry_address(FlipperApplication* app) { |  | ||||||
|     return (void*)app->entry; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const char* preload_status_strings[] = { | static const char* preload_status_strings[] = { | ||||||
|     [FlipperApplicationPreloadStatusSuccess] = "Success", |     [FlipperApplicationPreloadStatusSuccess] = "Success", | ||||||
|     [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", |     [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", | ||||||
|  | |||||||
| @ -1,3 +1,7 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file flipper_application.h | ||||||
|  |  * Flipper application | ||||||
|  |  */ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "application_manifest.h" | #include "application_manifest.h" | ||||||
| @ -79,6 +83,14 @@ void flipper_application_free(FlipperApplication* app); | |||||||
| FlipperApplicationPreloadStatus | FlipperApplicationPreloadStatus | ||||||
|     flipper_application_preload(FlipperApplication* app, const char* path); |     flipper_application_preload(FlipperApplication* app, const char* path); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Validate elf file and load application manifest  | ||||||
|  |  * @param app Application pointer | ||||||
|  |  * @return Preload result code | ||||||
|  |  */ | ||||||
|  | FlipperApplicationPreloadStatus | ||||||
|  |     flipper_application_preload_manifest(FlipperApplication* app, const char* path); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * @brief Get pointer to application manifest for preloaded application |  * @brief Get pointer to application manifest for preloaded application | ||||||
|  * @param app Application pointer |  * @param app Application pointer | ||||||
| @ -93,13 +105,6 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic | |||||||
|  */ |  */ | ||||||
| FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app); | FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * @brief Get state object for loaded application  |  | ||||||
|  * @param app Application pointer |  | ||||||
|  * @return Pointer to state object |  | ||||||
|  */ |  | ||||||
| const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * @brief Create application thread at entry point address, using app name and |  * @brief Create application thread at entry point address, using app name and | ||||||
|  * stack size from metadata. Returned thread isn't started yet.  |  * stack size from metadata. Returned thread isn't started yet.  | ||||||
| @ -110,20 +115,6 @@ const FlipperApplicationState* flipper_application_get_state(FlipperApplication* | |||||||
|  */ |  */ | ||||||
| FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); | FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * @brief Get previously spawned thread |  | ||||||
|  * @param app Application pointer |  | ||||||
|  * @return Created thread |  | ||||||
|  */ |  | ||||||
| FuriThread* flipper_application_get_thread(FlipperApplication* app); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Return relocated and valid address of app's entry point |  | ||||||
|  * @param app Application pointer |  | ||||||
|  * @return Address of app's entry point |  | ||||||
|  */ |  | ||||||
| void const* flipper_application_get_entry_address(FlipperApplication* app); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| @ -1,99 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "elf.h" |  | ||||||
| #include "flipper_application.h" |  | ||||||
| #include <m-dict.h> |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" { |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| DICT_DEF2(RelocationAddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Callable elf entry type |  | ||||||
|  */ |  | ||||||
| typedef int32_t(entry_t)(void*); |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     void* data; |  | ||||||
|     uint16_t sec_idx; |  | ||||||
|     uint16_t rel_sec_idx; |  | ||||||
| } ELFSection_t; |  | ||||||
| 
 |  | ||||||
| struct FlipperApplication { |  | ||||||
|     const ElfApiInterface* api_interface; |  | ||||||
|     File* fd; |  | ||||||
|     FlipperApplicationState state; |  | ||||||
|     FlipperApplicationManifest manifest; |  | ||||||
| 
 |  | ||||||
|     size_t sections; |  | ||||||
|     off_t section_table; |  | ||||||
|     off_t section_table_strings; |  | ||||||
| 
 |  | ||||||
|     size_t symbol_count; |  | ||||||
|     off_t symbol_table; |  | ||||||
|     off_t symbol_table_strings; |  | ||||||
|     off_t entry; |  | ||||||
| 
 |  | ||||||
|     ELFSection_t text; |  | ||||||
|     ELFSection_t rodata; |  | ||||||
|     ELFSection_t data; |  | ||||||
|     ELFSection_t bss; |  | ||||||
| 
 |  | ||||||
|     FuriThread* thread; |  | ||||||
|     RelocationAddressCache_t relocation_cache; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     FoundERROR = 0, |  | ||||||
|     FoundSymTab = (1 << 0), |  | ||||||
|     FoundStrTab = (1 << 2), |  | ||||||
|     FoundText = (1 << 3), |  | ||||||
|     FoundRodata = (1 << 4), |  | ||||||
|     FoundData = (1 << 5), |  | ||||||
|     FoundBss = (1 << 6), |  | ||||||
|     FoundRelText = (1 << 7), |  | ||||||
|     FoundRelRodata = (1 << 8), |  | ||||||
|     FoundRelData = (1 << 9), |  | ||||||
|     FoundRelBss = (1 << 10), |  | ||||||
|     FoundFappManifest = (1 << 11), |  | ||||||
|     FoundDebugLink = (1 << 12), |  | ||||||
|     FoundValid = FoundSymTab | FoundStrTab | FoundFappManifest, |  | ||||||
|     FoundExec = FoundValid | FoundText, |  | ||||||
|     FoundGdbSection = FoundText | FoundRodata | FoundData | FoundBss, |  | ||||||
|     FoundAll = FoundSymTab | FoundStrTab | FoundText | FoundRodata | FoundData | FoundBss | |  | ||||||
|                FoundRelText | FoundRelRodata | FoundRelData | FoundRelBss | FoundDebugLink, |  | ||||||
| } FindFlags_t; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Load and validate basic ELF file headers |  | ||||||
|  * @param e Application instance |  | ||||||
|  * @param path FS path to application file |  | ||||||
|  * @return true if ELF file is valid  |  | ||||||
|  */ |  | ||||||
| bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Iterate over all sections and save related indexes |  | ||||||
|  * @param e Application instance |  | ||||||
|  * @return true if all required sections are found |  | ||||||
|  */ |  | ||||||
| bool flipper_application_load_section_table(FlipperApplication* e); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Load section data to memory and process relocations |  | ||||||
|  * @param e Application instance  |  | ||||||
|  * @return Status code |  | ||||||
|  */ |  | ||||||
| FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Release section data |  | ||||||
|  * @param s section pointer |  | ||||||
|  */ |  | ||||||
| void flipper_application_free_section(ELFSection_t* s); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sergey Gavrilov
						Sergey Gavrilov