/*---------------------------------------------------------------------------* Project: TwlSDK - ELF Loader File: loader_subset.c Copyright 2006-2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2008-12-15#$ $Rev: 9650 $ $Author: kitase_hirotake $ *---------------------------------------------------------------------------*/ #include "el_config.h" #ifdef SDK_TWL #include #else #include #endif #include "loader_subset.h" #include /*------------------------------------------------------ External Variables -----------------------------------------------------*/ //#ifndef SDK_TWL //#else extern ELAlloc i_elAlloc; extern ELFree i_elFree; //#endif extern ELDesc* i_eldesc_sim; /*------------------------------------------------------ External Functions -----------------------------------------------------*/ extern BOOL elRemoveAdrEntry( ELAdrEntry* AdrEnt); extern void elAddAdrEntry( ELAdrEntry** ELAdrEntStart, ELAdrEntry* AdrEnt); extern ELAdrEntry* elGetAdrEntry( ELDesc* elElfDesc, const char* ent_name, ELObject** ExpObjEnt); /*------------------------------------------------------ Static functions -----------------------------------------------------*/ static u32 el_veneer[3] = { //ARM code 0xE59FC000, //(LDR r12,[PC]) 0xE12FFF1C, //(BX r12) 0x00000000, //(data) }; static u16 el_veneer_t[10] = { //Thumb code for v4t 0xB580, //(PUSH {R7,LR}) 0x46FE, //(NOP) 0x46FE, //(MOV LR, PC) 0x2707, //(MOV R7, 7) 0x44BE, //(ADD LR,R7) 0x4F01, //(LDR R7,[PC+4]) 0x4738, //(BX R7) ---> ARM routine 0xBD80, //(POP {R7,PC} <--- LR (Thumb branch with no conditions for v4t) 0x0000, //(data) ARM routine address 0x0000, //(data) }; static BOOL ELi_BuildSymList( ELDesc* elElfDesc, u32 symsh_index); static ELSymEx* ELi_GetSymExfromList( ELSymEx* SymExStart, u32 index); static ELSymEx* ELi_GetSymExfromTbl( ELSymEx** SymExTbl, u32 index); static void ELi_InitImport( ELImportEntry* ImportInfo); //Extract entry from import information table (Do not delete ImpEnt!) BOOL ELi_ExtractImportEntry( ELImportEntry** StartEnt, ELImportEntry* ImpEnt); void ELi_AddImportEntry( ELImportEntry** ELUnrEntStart, ELImportEntry* UnrEnt); static void ELi_AddVeneerEntry( ELVeneer** start, ELVeneer* VenEnt); static BOOL ELi_RemoveVenEntry( ELVeneer** start, ELVeneer* VenEnt); static ELVeneer* ELi_GetVenEntry( ELVeneer** start, u32 data); static BOOL ELi_IsFar( u32 P, u32 S, s32 threshold); /*------------------------------------------------------ Align the addresses -----------------------------------------------------*/ #define ALIGN( addr, align_size ) (((addr) & ~((align_size) - 1)) + (align_size)) static u32 ELi_ALIGN( u32 addr, u32 align_size); u32 ELi_ALIGN( u32 addr, u32 align_size) { u32 aligned_addr; if( (addr % align_size) == 0) { aligned_addr = addr; }else{ aligned_addr = (((addr) & ~((align_size) - 1)) + (align_size)); } return aligned_addr; } /*------------------------------------------------------ Function to notify the IS-TWL_DEBUGGER -----------------------------------------------------*/ SDK_WEAK_SYMBOL BOOL _ISTDbgLib_RegistVeneerInfo( ISTDOVERLAYPROC nProc, ISTDVENEERTYPE vType, u32 nVeneerAddress, u32 nJumpAddress) { #pragma unused( nProc) #pragma unused( vType) #pragma unused( nVeneerAddress) #pragma unused( nJumpAddress) return( TRUE); } SDK_WEAK_SYMBOL BOOL _ISTDbgLib_UnregistVeneerInfo( ISTDOVERLAYPROC nProc, ISTDVENEERTYPE vType, u32 nVeneerAddress) { #pragma unused( nProc, vType, nVeneerAddress) return( TRUE); } static BOOL ELi_REGISTER_VENEER_INFO( ISTDOVERLAYPROC nProc, ISTDVENEERTYPE vType, u32 nVeneerAddress, u32 nJumpAddress, BOOL enable); static BOOL ELi_UNREGISTER_VENEER_INFO( ISTDOVERLAYPROC nProc, ISTDVENEERTYPE vType, u32 nVeneerAddress, BOOL enable); static BOOL ELi_REGISTER_VENEER_INFO( ISTDOVERLAYPROC nProc, ISTDVENEERTYPE vType, u32 nVeneerAddress, u32 nJumpAddress, BOOL enable) { if( enable) { return( _ISTDbgLib_RegistVeneerInfo( nProc, vType, nVeneerAddress, nJumpAddress)); } return( FALSE); } static BOOL ELi_UNREGISTER_VENEER_INFO( ISTDOVERLAYPROC nProc, ISTDVENEERTYPE vType, u32 nVeneerAddress, BOOL enable) { if( enable) { return( _ISTDbgLib_UnregistVeneerInfo( nProc, vType, nVeneerAddress)); } return( FALSE); } /*------------------------------------------------------ Copy the veneer to the buffer start: Call source data: Jump destination threshold: Reuse if there is already veneer in this range -----------------------------------------------------*/ void* ELi_CopyVeneerToBuffer( ELDesc* elElfDesc, ELObject* MYObject, u32 start, u32 data, s32 threshold) { #pragma unused(elElfDesc) u32* el_veneer_dest; u32 load_start; Elf32_Addr sh_size; ELVeneer* elVenEnt; if( (elElfDesc == i_eldesc_sim)&&(data == 0)) { }else{ /*--- If there already is veneer, use that ---*/ elVenEnt = ELi_GetVenEntry( &(MYObject->ELVenEntStart), data); if( elVenEnt != NULL) { if( ELi_IsFar( start, elVenEnt->adr, threshold) == FALSE) { PRINTDEBUG( "Veneer Hit!\n"); return( (void*)elVenEnt->adr); }else{ /*The veneer is too far and cannot be used, so delete from the link list*/ (void)ELi_RemoveVenEntry( &(MYObject->ELVenEntStart), elVenEnt); } } /*--------------------------------------*/ } /*Align*/ load_start = ELi_ALIGN( ((u32)(MYObject->buf_current)), 4); /*Size settings*/ sh_size = 12;//el_veneer if( elElfDesc != i_eldesc_sim) { /* If not simulation, actually copy */ OSAPI_CPUCOPY8( el_veneer, (u32*)load_start, sh_size); el_veneer_dest = (u32*)load_start; el_veneer_dest[2] = data; } /*Move the buffer pointer*/ MYObject->buf_current = (void*)(load_start + sh_size); /*--- Add to veneer list ---*/ elVenEnt = ELi_Malloc( elElfDesc, MYObject, sizeof( ELVeneer)); if(elVenEnt == NULL) return 0; elVenEnt->adr = load_start; elVenEnt->data = data; ELi_AddVeneerEntry( &(MYObject->ELVenEntStart), elVenEnt); (void)ELi_REGISTER_VENEER_INFO( ISTDRELOCATIONPROC_AUTO, ISTDVENEERTYPE_AUTO, elVenEnt->adr, elVenEnt->data, (elElfDesc != i_eldesc_sim)); /*--------------------------*/ /*Return the loaded header address*/ return (void*)load_start; } /*------------------------------------------------------ Copy the veneer to the buffer start: Call source data: Jump destination threshold: Reuse if there is already veneer in this range -----------------------------------------------------*/ void* ELi_CopyV4tVeneerToBuffer( ELDesc* elElfDesc, ELObject* MYObject, u32 start, u32 data, s32 threshold) { #pragma unused(elElfDesc) u32* el_veneer_dest; u32 load_start; Elf32_Addr sh_size; ELVeneer* elVenEnt; if( (elElfDesc == i_eldesc_sim)&&(data == 0)) { /*--- If there already is veneer, use that ---*/ elVenEnt = ELi_GetVenEntry( &(MYObject->ELV4tVenEntStart), data); if( elVenEnt != NULL) { if( ELi_IsFar( start, elVenEnt->adr, threshold) == FALSE) { PRINTDEBUG( "Veneer Hit!\n"); return( (void*)elVenEnt->adr); }else{ /*The veneer is too far and cannot be used, so delete from the link list*/ (void)ELi_RemoveVenEntry( &(MYObject->ELV4tVenEntStart), elVenEnt); } } /*--------------------------------------*/ } /*Align*/ load_start = ELi_ALIGN( ((u32)(MYObject->buf_current)), 4); /*Size settings*/ sh_size = 20;//el_veneer_t if( elElfDesc != i_eldesc_sim) { /* If not simulation, actually copy */ OSAPI_CPUCOPY8( el_veneer_t, (u32*)load_start, sh_size); el_veneer_dest = (u32*)load_start; el_veneer_dest[4] = data; } /*Move the buffer pointer*/ MYObject->buf_current = (void*)(load_start + sh_size); /*--- Add to veneer list ---*/ elVenEnt = ELi_Malloc( elElfDesc, MYObject, sizeof( ELVeneer)); if(elVenEnt == NULL) return 0; elVenEnt->adr = load_start; elVenEnt->data = data; ELi_AddVeneerEntry( &(MYObject->ELV4tVenEntStart), elVenEnt); (void)ELi_REGISTER_VENEER_INFO( ISTDRELOCATIONPROC_AUTO, ISTDVENEERTYPE_AUTO, elVenEnt->adr, elVenEnt->data, (elElfDesc != i_eldesc_sim)); /*--------------------------*/ /*Return the loaded header address*/ return (void*)load_start; } /*------------------------------------------------------ Copy the segment to the buffer -----------------------------------------------------*/ void* ELi_CopySegmentToBuffer( ELDesc* elElfDesc, ELObject* MYObject, Elf32_Phdr* Phdr) { u32 load_start; load_start = ELi_ALIGN( Phdr->p_vaddr, Phdr->p_align); if( elElfDesc != i_eldesc_sim) { /* If not simulation, actually copy */ if( elElfDesc->i_elReadStub( (void*)load_start, elElfDesc->FileStruct, (u32)(elElfDesc->ar_head), (u32)(elElfDesc->elf_offset)+(u32)(Phdr->p_offset), Phdr->p_filesz) == FALSE) { ELi_SetResultCode( elElfDesc, MYObject, EL_RESULT_CANNOT_ACCESS_ELF); return( NULL); } } return( (void*)load_start); } /*------------------------------------------------------ Copy the section to the buffer -----------------------------------------------------*/ void* ELi_CopySectionToBuffer( ELDesc* elElfDesc, ELObject* MYObject, Elf32_Shdr* Shdr) { u32 load_start; Elf32_Addr sh_size; /*Align*/ load_start = ELi_ALIGN( ((u32)(MYObject->buf_current)), (Shdr->sh_addralign)); /*Size settings*/ sh_size = Shdr->sh_size; if( elElfDesc != i_eldesc_sim) { /* If not simulation, actually copy */ if( elElfDesc->i_elReadStub( (void*)load_start, elElfDesc->FileStruct, (u32)(elElfDesc->ar_head), (u32)(elElfDesc->elf_offset)+(u32)(Shdr->sh_offset), sh_size) == FALSE) { ELi_SetResultCode( elElfDesc, MYObject, EL_RESULT_CANNOT_ACCESS_ELF); return( NULL); } } /*Move the buffer pointer*/ MYObject->buf_current = (void*)(load_start + sh_size); /*Return the loaded header address*/ return (void*)load_start; } /*------------------------------------------------------ Allocate a section in the buffer (do not copy) and zero-fill that region. -----------------------------------------------------*/ void* ELi_AllocSectionToBuffer( ELDesc* elElfDesc, ELObject* MYObject, Elf32_Shdr* Shdr) { #pragma unused(elElfDesc) u32 load_start; Elf32_Addr sh_size; /*Align*/ load_start = ELi_ALIGN( ((u32)(MYObject->buf_current)), (Shdr->sh_addralign)); /*Size settings*/ sh_size = Shdr->sh_size; /*Move the buffer pointer*/ MYObject->buf_current = (void*)(load_start + sh_size); /*Embed with 0 (because .bss section is expected)*/ OSAPI_CPUFILL8( (void*)load_start, 0, sh_size); /*Return the allocated header address*/ return (void*)load_start; } /*------------------------------------------------------ Get program header of the specified index in buffer -----------------------------------------------------*/ void ELi_GetPhdr( ELDesc* elElfDesc, u32 index, Elf32_Phdr* Phdr) { u32 offset; offset = (elElfDesc->CurrentEhdr.e_phoff) + ((u32)(elElfDesc->CurrentEhdr.e_phentsize) * index); (void)elElfDesc->i_elReadStub( Phdr, elElfDesc->FileStruct, (u32)(elElfDesc->ar_head), (u32)(elElfDesc->elf_offset) + offset, sizeof( Elf32_Shdr)); } /*------------------------------------------------------ Get section header for the specified index to the buffer -----------------------------------------------------*/ void ELi_GetShdr( ELDesc* elElfDesc, u32 index, Elf32_Shdr* Shdr) { u32 offset; offset = (elElfDesc->CurrentEhdr.e_shoff) + ((u32)(elElfDesc->shentsize) * index); (void)elElfDesc->i_elReadStub( Shdr, elElfDesc->FileStruct, (u32)(elElfDesc->ar_head), (u32)(elElfDesc->elf_offset) + offset, sizeof( Elf32_Shdr)); } /*------------------------------------------------------ Get section entry for the specified index to the buffer -----------------------------------------------------*/ void ELi_GetSent( ELDesc* elElfDesc, u32 index, void* entry_buf, u32 offset, u32 size) { Elf32_Shdr Shdr; ELi_GetShdr( elElfDesc, index, &Shdr); (void)elElfDesc->i_elReadStub( entry_buf, elElfDesc->FileStruct, (u32)(elElfDesc->ar_head), (u32)(elElfDesc->elf_offset) + (u32)(Shdr.sh_offset) + offset, size); } /*------------------------------------------------------ Get specified index entry for the specified section header to the buffer. (Only section with a fixed entry size, such as Rel, Rela, Sym, and the like.) Shdr: Specified section header index: Entry number (from 0) -----------------------------------------------------*/ void ELi_GetEntry( ELDesc* elElfDesc, Elf32_Shdr* Shdr, u32 index, void* entry_buf) { u32 offset; offset = (u32)(Shdr->sh_offset) + ((Shdr->sh_entsize) * index); (void)elElfDesc->i_elReadStub( entry_buf, elElfDesc->FileStruct, (u32)(elElfDesc->ar_head), (u32)(elElfDesc->elf_offset) + offset, Shdr->sh_entsize); } /*------------------------------------------------------ Get specified index string for the STR section header Shdr: Specified section header index: Entry index (from 0) -----------------------------------------------------*/ void ELi_GetStrAdr( ELDesc* elElfDesc, u32 strsh_index, u32 ent_index, char* str, u32 len) { /*Header address of the string entry*/ ELi_GetSent( elElfDesc, strsh_index, str, ent_index, len); } /*------------------------------------------------------ Redefine symbol. RelShdr->sh_link: Symbol section number RelShdr->sh_info: Target section number (ex.: Representing .text of rel.text) Rel->r_offset: Offset address in target section. ELF32_R_SYM( Rel->r_info): Symbol entry number. ELF32_R_TYPE( Rel->r_info): Relocate type. SymShdr->sh_link: String table section number of symbol Sym->st_name: String entry number of symbol Sym->st_value: Offset address in section to which the symbol belongs Sym->st_size: Size in section to which the symbol belongs Sym->st_shndx: Section number to which the symbol belongs ELF32_ST_BIND (Sym->st_info): Bind (Local or Global) ELF32_ST_TYPE( Sym->st_info): Type (Target associated with the symbol) -----------------------------------------------------*/ BOOL ELi_RelocateSym( ELDesc* elElfDesc, ELObject* MYObject, u32 relsh_index) { u32 i; u32 num_of_rel; //Number of symbols that should be redefined Elf32_Shdr RelOrRelaShdr; //REL or RELA section header Elf32_Rela CurrentRela; //REL or RELA entry copy destination Elf32_Shdr* SymShdr; ELSymEx* CurrentSymEx; ELShdrEx* TargetShdrEx; ELShdrEx* CurrentShdrEx; u32 relocation_adr; char sym_str[128]; u32 copy_size; ELAdrEntry* CurrentAdrEntry; u32 sym_loaded_adr = 0; u32 thumb_func_flag = 0; ELImportEntry UnresolvedInfo; ELImportEntry* UnrEnt; ELObject* ExpObjEnt; u32 unresolved_num = 0; /*Get REL or RELA section header*/ ELi_GetShdr( elElfDesc, relsh_index, &RelOrRelaShdr); /*Target section EX header*/ TargetShdrEx = ELi_GetShdrExfromList( elElfDesc->ShdrEx, RelOrRelaShdr.sh_info); num_of_rel = (RelOrRelaShdr.sh_size) / (RelOrRelaShdr.sh_entsize); //Number of symbols that should be redefined /*Create the symbol list*/ if(ELi_BuildSymList( elElfDesc, RelOrRelaShdr.sh_link) == FALSE) return FALSE; /*elElfDesc->SymShdr is set when ELi_BuildSymList is called*/ SymShdr = &(elElfDesc->SymShdr); PRINTDEBUG( "SymShdr->link:%02x, SymShdr->info:%02x\n", SymShdr->sh_link, SymShdr->sh_info); /*--- Redefine symbols that must be redefined ---*/ for( i=0; iSymExTbl[ELF32_R_SYM( CurrentRela.r_info)]); // if( CurrentSymEx->debug_flag == 1) { // *When it is debugging information * if( CurrentSymEx == NULL) { }else{ /*When not debugging information*/ /**/ ELi_InitImport( &UnresolvedInfo); /*Rewritten address (Called P in the specifications)*/ relocation_adr = (TargetShdrEx->loaded_adr) + (CurrentRela.r_offset); UnresolvedInfo.r_type = ELF32_R_TYPE( CurrentRela.r_info); UnresolvedInfo.A_ = (CurrentRela.r_addend); UnresolvedInfo.P_ = (relocation_adr); UnresolvedInfo.sh_type = (RelOrRelaShdr.sh_type); /*Locate the symbol address*/ if( CurrentSymEx->Sym.st_shndx == SHN_UNDEF) { /*Search from address table*/ ELi_GetStrAdr( elElfDesc, SymShdr->sh_link, CurrentSymEx->Sym.st_name, sym_str, 128); CurrentAdrEntry = elGetAdrEntry( elElfDesc, sym_str, &ExpObjEnt); /*Confirm symbol string*/ copy_size = (u32)OSAPI_STRLEN( sym_str) + 1; UnresolvedInfo.sym_str = ELi_Malloc( elElfDesc, MYObject, copy_size); if(UnresolvedInfo.sym_str == NULL) return FALSE; OSAPI_CPUCOPY8( sym_str, UnresolvedInfo.sym_str, copy_size); if( CurrentAdrEntry) { /*When found from the address table*/ sym_loaded_adr = (u32)(CurrentAdrEntry->adr); /*THUMB function flag (called T in the specifications) to differentiate THUMB or ARM*/ thumb_func_flag = CurrentAdrEntry->thumb_flag; PRINTDEBUG( "\n symbol found %s : %8x\n", sym_str, sym_loaded_adr); }else{ /*Do not resolve when not found from address table*/ /*Add and register to unresolved table*/ copy_size = sizeof( ELImportEntry); UnrEnt = ELi_Malloc( elElfDesc, MYObject, copy_size); if(UnrEnt == NULL) return FALSE; OSAPI_CPUCOPY8( &UnresolvedInfo, UnrEnt, copy_size); ELi_AddImportEntry( &(MYObject->UnresolvedImportAdrEnt), UnrEnt); unresolved_num++; /*Count number of unresolved symbols*/ PRINTDEBUG( "\n WARNING! cannot find symbol : %s\n", sym_str); } }else{ /* When resolved by yourself */ /*Get Ex header of section where symbol belongs*/ CurrentShdrEx = ELi_GetShdrExfromList( elElfDesc->ShdrEx, CurrentSymEx->Sym.st_shndx); sym_loaded_adr = CurrentShdrEx->loaded_adr; sym_loaded_adr += CurrentSymEx->Sym.st_value; //sym_loaded_adr is called S in the specifications /*THUMB function flag (called T in the specifications) to differentiate THUMB or ARM*/ thumb_func_flag = CurrentSymEx->thumb_flag; ExpObjEnt = MYObject; } if( ExpObjEnt) { /* Resolve when someone exported themselves */ /*Set to same variable name as in the specifications*/ UnresolvedInfo.S_ = (sym_loaded_adr); UnresolvedInfo.T_ = (thumb_func_flag); /*Add and register to unresolved table (only when referencing a separate object)*/ if( ExpObjEnt != MYObject) { copy_size = sizeof( ELImportEntry); UnrEnt = ELi_Malloc( elElfDesc, MYObject, copy_size); if(UnrEnt == NULL) return FALSE; OSAPI_CPUCOPY8( &UnresolvedInfo, UnrEnt, copy_size); UnrEnt->Dlld = (struct ELObject*)ExpObjEnt; /* Record objects used in resolution */ ELi_AddImportEntry( &(MYObject->ResolvedImportAdrEnt), UnrEnt); } /*--------------- Resolve symbol (Execute Relocation) ---------------*/ /*CurrentSymEx->relocation_val =*/ if( ELi_DoRelocate( elElfDesc, MYObject, &UnresolvedInfo) == FALSE) { return( FALSE); } /*------------------------------------------------------------*/ } } } /*-----------------------------------*/ /*Deallocate the symbol list*/ // ELi_FreeSymList( elElfDesc); /* After completing relocation */ if( unresolved_num == 0) { ELi_SetProcCode( elElfDesc, NULL, EL_PROC_RELOCATED); } return TRUE; } /*------------------------------------------------------ Put global symbols in address table SymShdr->sh_link: String table section number of symbol Sym->st_name: String entry number of symbol Sym->st_value: Offset address in section to which the symbol belongs Sym->st_size: Size in section to which the symbol belongs Sym->st_shndx: Section number to which the symbol belongs ELF32_ST_BIND (Sym->st_info): Bind (Local or Global) ELF32_ST_TYPE( Sym->st_info): Type (Target associated with the symbol) -----------------------------------------------------*/ BOOL ELi_GoPublicGlobalSym( ELDesc* elElfDesc, ELObject* MYObject, u32 symtblsh_index) { Elf32_Shdr SymShdr; ELSymEx* CurrentSymEx; ELShdrEx* CurrentShdrEx; ELAdrEntry* ExportAdrEntry; ELObject* DmyObjEnt; char sym_str[128]; u32 copy_size; /*Get SYMTAB section header*/ ELi_GetShdr( elElfDesc, symtblsh_index, &SymShdr); /*Create the symbol list*/ if(ELi_BuildSymList( elElfDesc, symtblsh_index) == FALSE) return FALSE; /*--- Made GLOBAL symbol in library public to address table ---*/ CurrentSymEx = elElfDesc->SymEx; //If traceable not from Tbl but from here, debug information is not included while( CurrentSymEx != NULL) { // CurrentSymEx = ELi_GetSymExfromList( elElfDesc->SymExTbl, i); // if( CurrentSymEx != NULL) { /*When global and the related section exist in the library*/ if( (ELF32_ST_BIND( CurrentSymEx->Sym.st_info) == STB_GLOBAL) && (CurrentSymEx->Sym.st_shndx != SHN_UNDEF)) { ExportAdrEntry = ELi_Malloc( elElfDesc, MYObject, sizeof(ELAdrEntry)); /*Memory allocation*/ if(ExportAdrEntry == NULL) return FALSE; ExportAdrEntry->next = NULL; ELi_GetStrAdr( elElfDesc, SymShdr.sh_link, CurrentSymEx->Sym.st_name, sym_str, 128); copy_size = (u32)OSAPI_STRLEN( sym_str) + 1; ExportAdrEntry->name = ELi_Malloc( elElfDesc, MYObject, copy_size); if(ExportAdrEntry->name == NULL) return FALSE; OSAPI_CPUCOPY8( sym_str, ExportAdrEntry->name, copy_size); CurrentShdrEx = ELi_GetShdrExfromList( elElfDesc->ShdrEx, CurrentSymEx->Sym.st_shndx); //There are cases where Sym.st_value is adjusted to enable determination of ARM/Thumb with odd/even numbers, so delete the adjustment to derive the net value. ExportAdrEntry->adr = (void*)(CurrentShdrEx->loaded_adr + ((CurrentSymEx->Sym.st_value)&0xFFFFFFFE)); ExportAdrEntry->func_flag = (u16)(ELF32_ST_TYPE( CurrentSymEx->Sym.st_info)); ExportAdrEntry->thumb_flag = CurrentSymEx->thumb_flag; if( elGetAdrEntry( elElfDesc, ExportAdrEntry->name, &DmyObjEnt) == NULL) { //If not in elAddAdrEntry( &(MYObject->ExportAdrEnt), ExportAdrEntry); /*registration*/ } } CurrentSymEx = CurrentSymEx->next; // } } /*----------------------------------------------------------------*/ /*Deallocate the symbol list*/ // ELi_FreeSymList( elElfDesc); return TRUE; } /*------------------------------------------------------ Create the symbol list -----------------------------------------------------*/ static BOOL ELi_BuildSymList( ELDesc* elElfDesc, u32 symsh_index) { u32 i; u32 num_of_sym; //Overall symbol count u16 debug_flag; Elf32_Sym TestSym; ELSymEx* CurrentSymEx; ELShdrEx* CurrentShdrEx; Elf32_Shdr* SymShdr; ELSymEx DmySymEx; if( elElfDesc->SymExTarget == symsh_index) { PRINTDEBUG( "%s skip.\n", __FUNCTION__); return TRUE; //List already created }else{ ELi_FreeSymList( elElfDesc); /*Deallocate the symbol list*/ } PRINTDEBUG( "%s build\n", __FUNCTION__); /*Get SYMTAB section header*/ ELi_GetShdr( elElfDesc, symsh_index, &(elElfDesc->SymShdr)); SymShdr = &(elElfDesc->SymShdr); num_of_sym = (SymShdr->sh_size) / (SymShdr->sh_entsize); //Overall symbol count elElfDesc->SymExTbl = ELi_Malloc( elElfDesc, NULL, num_of_sym * 4); if(elElfDesc->SymExTbl == NULL) return FALSE; /*---------- Create ELSymEx List ----------*/ CurrentSymEx = &DmySymEx; for( i=0; iShdrEx, TestSym.st_shndx); if( CurrentShdrEx) { debug_flag = CurrentShdrEx->debug_flag; }else{ debug_flag = 0; }/*-------------------------------*/ if( debug_flag == 1) { elElfDesc->SymExTbl[i] = NULL; }else{ CurrentSymEx->next = ELi_Malloc( elElfDesc, NULL, sizeof(ELSymEx)); if(CurrentSymEx->next == NULL) return FALSE; CurrentSymEx = (ELSymEx*)(CurrentSymEx->next); OSAPI_CPUCOPY8( &TestSym, &(CurrentSymEx->Sym), sizeof(TestSym)); elElfDesc->SymExTbl[i] = CurrentSymEx; PRINTDEBUG( "sym_no: %02x ... st_shndx: %04x\n", i, CurrentSymEx->Sym.st_shndx); } } CurrentSymEx->next = NULL; elElfDesc->SymEx = DmySymEx.next; /*-------------------------------------------*/ /*-------- Determine ARM or Thumb (Because if SymEx is not ready, ELi_CodeIsThumb does not work) --------*/ CurrentSymEx = elElfDesc->SymEx; while( CurrentSymEx != NULL) { //If traceable from here, there is no debug information /*-- Set the ELSymEx Thumb flag (Only the function symbol is required) -----*/ if( ELF32_ST_TYPE( CurrentSymEx->Sym.st_info) == STT_FUNC) { CurrentSymEx->thumb_flag = (u16)(ELi_CodeIsThumb( elElfDesc, CurrentSymEx->Sym.st_shndx, CurrentSymEx->Sym.st_value)); }else{ CurrentSymEx->thumb_flag = 0; }/*--------------------------------------------------------*/ CurrentSymEx = CurrentSymEx->next; } /*-------------------------------------------------------------------------------------*/ elElfDesc->SymExTarget = symsh_index; return TRUE; } /*------------------------------------------------------ Deallocate the symbol list -----------------------------------------------------*/ void ELi_FreeSymList( ELDesc* elElfDesc) { ELSymEx* CurrentSymEx; ELSymEx* FwdSymEx; /*--- First free SymExTbl ---*/ if( elElfDesc->SymExTbl != NULL) { OSAPI_FREE( elElfDesc->SymExTbl); elElfDesc->SymExTbl = NULL; } /*---------- Free the ELSymEx List ----------*/ CurrentSymEx = elElfDesc->SymEx; if( CurrentSymEx) { do { FwdSymEx = CurrentSymEx; CurrentSymEx = CurrentSymEx->next; OSAPI_FREE( FwdSymEx); }while( CurrentSymEx != NULL); elElfDesc->SymEx = NULL; } /*-----------------------------------------*/ elElfDesc->SymExTarget = 0xFFFFFFFF; } /*------------------------------------------------------ Resolve symbol based on unresolved information It is necessary to know all of the r_type, S_, A_, P_, and T_ -----------------------------------------------------*/ #define _S_ (UnresolvedInfo->S_) #define _A_ (UnresolvedInfo->A_) #define _P_ (UnresolvedInfo->P_) #define _T_ (UnresolvedInfo->T_) BOOL ELi_DoRelocate( ELDesc* elElfDesc, ELObject* MYObject, ELImportEntry* UnresolvedInfo) { s32 signed_val; u32 relocation_val = 0; u32 relocation_adr; BOOL ret_val; BOOL veneer_flag = TRUE; ret_val = TRUE; relocation_adr = _P_; switch( (UnresolvedInfo->r_type)) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: if( UnresolvedInfo->sh_type == SHT_REL) { _A_ = (s32)(((*(vu32*)relocation_adr)|0xFF800000) << 2); //Generally, this should be -8 } /*----------------- veneer -----------------*/ if( (elElfDesc == i_eldesc_sim)&&(_S_ == 0)) { veneer_flag = TRUE; //If resolution not possible with simulation, veneer is presumed }else{ veneer_flag = ELi_IsFar( _P_, _S_|_T_, 0x2000000); //Within +-32 Bytes? } #if (TARGET_ARM_V5 == 1) if( veneer_flag == TRUE) { #else //(TARGET_ARM_V4 == 1) //Use veneer when there is a long branch with ARM->Thumb and ARM->ARM if( (_T_) || (veneer_flag == TRUE)) { #endif //_A_ is an adjustment value for PC relative branch, so when an absolute branch, there is no relationship. _S_ = (u32)ELi_CopyVeneerToBuffer( elElfDesc, MYObject, _P_, (u32)(( (s32)(_S_) /*+ _A_*/) | (s32)(_T_)), 0x2000000); if(_S_ == 0) return FALSE; _T_ = 0; //Because veneer is ARM code }/*-----------------------------------------*/ if( elElfDesc == i_eldesc_sim) { //Do not actually redefine the symbol at simulation break; } signed_val = (( (s32)(_S_) + _A_) | (s32)(_T_)) - (s32)(_P_); if( _T_) { /*Jump from ARM to Thumb with BLX instruction (If less than v5, BL it is necessary to combine with BX using veneer)*/ relocation_val = (0xFA000000) | ((signed_val>>2) & 0x00FFFFFF) | (((signed_val>>1) & 0x1)<<24); }else{ /*Jump from ARM to ARM using the BL instruction*/ signed_val >>= 2; relocation_val = (*(vu32*)relocation_adr & 0xFF000000) | (signed_val & 0x00FFFFFF); } *(vu32*)relocation_adr = relocation_val; break; case R_ARM_ABS32: if( elElfDesc == i_eldesc_sim) { //Do not actually redefine the symbol at simulation break; } relocation_val = (( _S_ + _A_) | _T_); *(vu32*)relocation_adr = relocation_val; break; case R_ARM_REL32: case R_ARM_PREL31: if( elElfDesc == i_eldesc_sim) { //Do not actually redefine the symbol at simulation break; } relocation_val = (( _S_ + _A_) | _T_) - _P_; *(vu32*)relocation_adr = relocation_val; break; case R_ARM_LDR_PC_G0: /*----------------- veneer -----------------*/ if( (elElfDesc == i_eldesc_sim)&&(_S_ == 0)) { veneer_flag = TRUE; //If resolution not possible with simulation, veneer is presumed }else{ veneer_flag = ELi_IsFar( _P_, _S_|_T_, 0x2000000); //Within +-32 Bytes? } #if (TARGET_ARM_V5 == 1) if( veneer_flag == TRUE) { #else //(TARGET_ARM_V4 == 1) //Use veneer when there is a long branch with ARM->Thumb and ARM->ARM if( (_T_) || (veneer_flag == TRUE)) { #endif //_A_ is an adjustment value for PC relative branch, so when an absolute branch, there is no relationship. _S_ = (u32)ELi_CopyVeneerToBuffer( elElfDesc, MYObject, _P_, (u32)(( (s32)(_S_) /*+ _A_*/) | (s32)(_T_)), 0x2000000); if(_S_ == 0) return FALSE; _T_ = 0; //Because veneer is ARM code }/*-----------------------------------------*/ if( elElfDesc == i_eldesc_sim) { //Do not actually redefine the symbol at simulation break; } signed_val = ( (s32)(_S_) + _A_) - (s32)(_P_); signed_val >>= 2; relocation_val = (*(vu32*)relocation_adr & 0xFF000000) | (signed_val & 0x00FFFFFF); *(vu32*)relocation_adr = relocation_val; break; case R_ARM_ABS16: case R_ARM_ABS12: case R_ARM_THM_ABS5: case R_ARM_ABS8: if( elElfDesc == i_eldesc_sim) { //Do not actually redefine the symbol at simulation break; } relocation_val = _S_ + _A_; *(vu32*)relocation_adr = relocation_val; break; case R_ARM_THM_PC22:/*Different Name: R_ARM_THM_CALL*/ if( UnresolvedInfo->sh_type == SHT_REL) { _A_ = (s32)(((*(vu16*)relocation_adr & 0x07FF)<<11) + ((*((vu16*)(relocation_adr)+1)) & 0x07FF)); _A_ = (s32)((_A_ | 0xFFC00000) << 1); //Generally, this should be -4 (Because the PC is the current instruction address +4) } /*----------------- veneer -----------------*/ if( (elElfDesc == i_eldesc_sim)&&(_S_ == 0)) { veneer_flag = TRUE; //If resolution not possible with simulation, veneer is presumed }else{ veneer_flag = ELi_IsFar( _P_, _S_|_T_, 0x400000); //Within +-4 Bytes? } #if (TARGET_ARM_V5 == 1) if( veneer_flag == TRUE) { //_A_ is an adjustment value for PC relative branch, so when an absolute branch, there is no relationship. _S_ = (u32)ELi_CopyVeneerToBuffer( elElfDesc, MYObject, _P_, (u32)(( (s32)(_S_) /*+ _A_*/) | (s32)(_T_)), 0x400000); if(_S_ == 0) return FALSE; _T_ = 0; //Because veneer is ARM code }/*-----------------------------------------*/ #else //(TARGET_ARM_V4 == 1) /*----------------- v4t veneer -----------------*/ //Use v4T when there is a long branch with Thumb->ARM and Thumb->Thumb if( (_T_ == 0) || (veneer_flag == TRUE)) { _S_ = (u32)ELi_CopyV4tVeneerToBuffer( elElfDesc, MYObject, _P_, (u32)(( (s32)(_S_)) | (s32)(_T_)), 0x400000); if(_S_ == 0) return FALSE; _T_ = 1; //Because v4t veneer is Thumb code } /*---------------------------------------------*/ #endif if( elElfDesc == i_eldesc_sim) { //Do not actually redefine the symbol at simulation break; } signed_val = (( (s32)(_S_) + _A_) | (s32)(_T_)) - (s32)(_P_); signed_val >>= 1; if( _T_) { /*Jump from Thumb to Thumb using the BL instruction*/ relocation_val = (u32)(*(vu16*)relocation_adr & 0xF800) | ((signed_val>>11) & 0x07FF) + ((((*((vu16*)(relocation_adr)+1)) & 0xF800) | (signed_val & 0x07FF)) << 16); }else{ /*Jump from Thumb to ARM with BLX instruction (If less than v5, it is necessary to combine with BX with BL veneer)*/ if( (signed_val & 0x1)) { //Come here when _P_ is not 4-byte aligned signed_val += 1; } relocation_val = (u32)(*(vu16*)relocation_adr & 0xF800) | ((signed_val>>11) & 0x07FF) + ((((*((vu16*)(relocation_adr)+1)) & 0xE800) | (signed_val & 0x07FF)) << 16); } *(vu16*)relocation_adr = (vu16)relocation_val; *((vu16*)relocation_adr+1) = (vu16)((u32)(relocation_val) >> 16); break; case R_ARM_NONE: /*R_ARM_NONE retains target symbols and is not dead-stripped to the linker */ break; case R_ARM_THM_JUMP24: default: ELi_SetResultCode( elElfDesc, MYObject, EL_RESULT_UNSUPPORTED_ELF); ret_val = FALSE; PRINTDEBUG( "ERROR! : unsupported relocation type (0x%x)!\n", (UnresolvedInfo->r_type)); PRINTDEBUG( "S = 0x%x\n", _S_); PRINTDEBUG( "A = 0x%x\n", _A_); PRINTDEBUG( "P = 0x%x\n", _P_); PRINTDEBUG( "T = 0x%x\n", _T_); break; } return ret_val;//relocation_val; } #undef _S_ #undef _A_ #undef _P_ #undef _T_ /*------------------------------------------------------ Read ELSymEx of the index specified from the list. -----------------------------------------------------*/ static ELSymEx* ELi_GetSymExfromList( ELSymEx* SymExStart, u32 index) { u32 i; ELSymEx* SymEx; SymEx = SymExStart; for( i=0; inext); if( SymEx == NULL) { break; } } return SymEx; } /*------------------------------------------------------ Read ELSymEx of the index specified from the table. -----------------------------------------------------*/ static ELSymEx* ELi_GetSymExfromTbl( ELSymEx** SymExTbl, u32 index) { return( (ELSymEx*)(SymExTbl[index])); } /*------------------------------------------------------ Read ELShdrx of the index specified from the list. -----------------------------------------------------*/ ELShdrEx* ELi_GetShdrExfromList( ELShdrEx* ShdrExStart, u32 index) { u32 i; ELShdrEx* ShdrEx; ShdrEx = ShdrExStart; for( i=0; inext); if( ShdrEx == NULL) { break; } } return ShdrEx; } /*------------------------------------------------------ Determine whether the section for the specified index is debug information - Sections whose section name begins with .debug. - Section, such as .rel.debug - section, where the sh_info indicates the debug information section number -----------------------------------------------------*/ BOOL ELi_ShdrIsDebug( ELDesc* elElfDesc, u32 index) { Elf32_Shdr TmpShdr; char shstr[6]; /*-- Get six characters of string for section name --*/ ELi_GetShdr( elElfDesc, index, &TmpShdr); ELi_GetStrAdr( elElfDesc, elElfDesc->CurrentEhdr.e_shstrndx, TmpShdr.sh_name, shstr, 6); /*-------------------------------------*/ if( OSAPI_STRNCMP( shstr, ".debug", 6) == 0) { /*When debugging section*/ return TRUE; }else{ /*When relocated section relates to the debugging section*/ if( (TmpShdr.sh_type == SHT_REL) || (TmpShdr.sh_type == SHT_RELA)) { if( ELi_ShdrIsDebug( elElfDesc, TmpShdr.sh_info) == TRUE) { return TRUE; } } } return FALSE; } /*------------------------------------------------------ Check the elElfDesc SymEx table, and determine whether the code in the specified offset of the specified index is ARM or THUMB (iConfigure elElfDesc->SymShdr and elElfDesc->SymEx in advance.) sh_index: Section index to be checked offset: Offset in section to check -----------------------------------------------------*/ u32 ELi_CodeIsThumb( ELDesc* elElfDesc, u16 sh_index, u32 offset) { u32 i; u32 thumb_flag; Elf32_Shdr* SymShdr; char str_adr[3]; ELSymEx* CurrentSymEx; /*Get symbol section header and SymEx list*/ SymShdr = &(elElfDesc->SymShdr); CurrentSymEx = elElfDesc->SymEx; i = 0; thumb_flag = 0; while( CurrentSymEx != NULL) { if( CurrentSymEx->Sym.st_shndx == sh_index) { ELi_GetStrAdr( elElfDesc, SymShdr->sh_link, CurrentSymEx->Sym.st_name, str_adr, 3); if( OSAPI_STRNCMP( str_adr, "$a\0", OSAPI_STRLEN("$a\0")) == 0) { thumb_flag = 0; }else if( OSAPI_STRNCMP( str_adr, "$t\0", OSAPI_STRLEN("$t\0")) == 0) { thumb_flag = 1; } if( CurrentSymEx->Sym.st_value > offset) { break; } } CurrentSymEx = CurrentSymEx->next; i++; } return thumb_flag; } /*--------------------------------------------------------- Initialize the import information entry --------------------------------------------------------*/ static void ELi_InitImport( ELImportEntry* ImportInfo) { ImportInfo->sym_str = NULL; ImportInfo->r_type = 0; ImportInfo->S_ = 0; ImportInfo->A_ = 0; ImportInfo->P_ = 0; ImportInfo->T_ = 0; ImportInfo->Dlld = NULL; } /*------------------------------------------------------ Extract entry from import information table (Do not delete ImpEnt!) -----------------------------------------------------*/ BOOL ELi_ExtractImportEntry( ELImportEntry** StartEnt, ELImportEntry* ImpEnt) { ELImportEntry DmyImpEnt; ELImportEntry* CurImpEnt; DmyImpEnt.next = (*StartEnt); CurImpEnt = &DmyImpEnt; while( CurImpEnt->next != ImpEnt) { if( CurImpEnt->next == NULL) { return FALSE; }else{ CurImpEnt = (ELImportEntry*)CurImpEnt->next; } } /*Relink link list*/ CurImpEnt->next = ImpEnt->next; (*StartEnt) = DmyImpEnt.next; /*Free*/ // OSAPI_FREE( ImpEnt); return TRUE; } /*--------------------------------------------------------- Add entry to the import information table --------------------------------------------------------*/ void ELi_AddImportEntry( ELImportEntry** ELUnrEntStart, ELImportEntry* UnrEnt) { ELImportEntry DmyUnrEnt; ELImportEntry* CurrentUnrEnt; if( !(*ELUnrEntStart)) { (*ELUnrEntStart) = UnrEnt; }else{ DmyUnrEnt.next = (*ELUnrEntStart); CurrentUnrEnt = &DmyUnrEnt; while( CurrentUnrEnt->next != NULL) { CurrentUnrEnt = CurrentUnrEnt->next; } CurrentUnrEnt->next = (void*)UnrEnt; } UnrEnt->next = NULL; } /*------------------------------------------------------ Release all import information tables -----------------------------------------------------*/ void ELi_FreeImportTbl( ELImportEntry** ELImpEntStart) { ELImportEntry* FwdImpEnt; ELImportEntry* CurrentImpEnt; /*---------- Free the ELImportEntry List ----------*/ CurrentImpEnt = (*ELImpEntStart); if( CurrentImpEnt) { do { FwdImpEnt = CurrentImpEnt; CurrentImpEnt = CurrentImpEnt->next; OSAPI_FREE( FwdImpEnt->sym_str); //Symbol name character-string OSAPI_FREE( FwdImpEnt); //Structure itself }while( CurrentImpEnt != NULL); (*ELImpEntStart) = NULL; } /*------------------------------------*/ } /*--------------------------------------------------------- Add entry to veneer table --------------------------------------------------------*/ static void ELi_AddVeneerEntry( ELVeneer** start, ELVeneer* VenEnt) { ELVeneer DmyVenEnt; ELVeneer* CurrentVenEnt; if( !(*start)) { (*start) = VenEnt; }else{ DmyVenEnt.next = (*start); CurrentVenEnt = &DmyVenEnt; while( CurrentVenEnt->next != NULL) { CurrentVenEnt = CurrentVenEnt->next; } CurrentVenEnt->next = (void*)VenEnt; } VenEnt->next = NULL; } /*------------------------------------------------------ Delete entry from the veneer table -----------------------------------------------------*/ static BOOL ELi_RemoveVenEntry( ELVeneer** start, ELVeneer* VenEnt) { ELVeneer DmyVenEnt; ELVeneer* CurrentVenEnt; DmyVenEnt.next = (*start); CurrentVenEnt = &DmyVenEnt; while( CurrentVenEnt->next != VenEnt) { if( CurrentVenEnt->next == NULL) { return FALSE; }else{ CurrentVenEnt = (ELVeneer*)CurrentVenEnt->next; } } /*Relink link list*/ CurrentVenEnt->next = VenEnt->next; (*start) = DmyVenEnt.next; /*Free*/ OSAPI_FREE( VenEnt); return TRUE; } /*------------------------------------------------------ Search for entry corresponding to specified data in the veneer table -----------------------------------------------------*/ static ELVeneer* ELi_GetVenEntry( ELVeneer** start, u32 data) { ELVeneer* CurrentVenEnt; CurrentVenEnt = (*start); if( CurrentVenEnt == NULL) { return NULL; } while( CurrentVenEnt->data != data) { CurrentVenEnt = (ELVeneer*)CurrentVenEnt->next; if( CurrentVenEnt == NULL) { break; } } return CurrentVenEnt; } /*------------------------------------------------------ Deallocate the veneer table -----------------------------------------------------*/ void* ELi_FreeVenTbl( ELDesc* elElfDesc, ELObject* MYObject) { ELVeneer* FwdVenEnt; ELVeneer* CurrentVenEnt; /*---------- Free the ELVenEntry List ----------*/ CurrentVenEnt = MYObject->ELVenEntStart; while( CurrentVenEnt != NULL) { FwdVenEnt = CurrentVenEnt; CurrentVenEnt = CurrentVenEnt->next; (void)ELi_UNREGISTER_VENEER_INFO( ISTDRELOCATIONPROC_AUTO, ISTDVENEERTYPE_AUTO, FwdVenEnt->adr, (elElfDesc != i_eldesc_sim)); OSAPI_FREE( FwdVenEnt); //Structure itself } MYObject->ELVenEntStart = NULL; /*------------------------------------*/ /*---------- Free the ELVenEntry List ----------*/ CurrentVenEnt = MYObject->ELV4tVenEntStart; while( CurrentVenEnt != NULL) { FwdVenEnt = CurrentVenEnt; CurrentVenEnt = CurrentVenEnt->next; (void)ELi_UNREGISTER_VENEER_INFO( ISTDRELOCATIONPROC_AUTO, ISTDVENEERTYPE_AUTO, FwdVenEnt->adr, (elElfDesc != i_eldesc_sim)); OSAPI_FREE( FwdVenEnt); //Structure itself } MYObject->ELV4tVenEntStart = NULL; /*------------------------------------*/ return NULL; } /*------------------------------------------------------ Check whether the difference of two address values fits in (+threshold) to (-threshold) -----------------------------------------------------*/ static BOOL ELi_IsFar( u32 P, u32 S, s32 threshold) { s32 diff; diff = (s32)S - (s32)(P); if( (diff < threshold)&&( diff > -threshold)) { return( FALSE); //Near }else{ return( TRUE); //Far } }