2020-04-28 14:43:07 +02:00
/****************************************************************************
* Copyright ( C ) 2018 Maschell
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <string>
# include <vector>
# include <map>
# include <coreinit/cache.h>
2020-05-17 21:14:27 +02:00
# include <wums.h>
2020-04-28 14:43:07 +02:00
# include "ModuleDataFactory.h"
# include "elfio/elfio.hpp"
# include "utils/utils.h"
# include "ElfUtils.h"
2020-05-17 13:11:52 +02:00
# include "SectionInfo.h"
2020-05-17 21:14:27 +02:00
# include "ExportData.h"
2020-05-29 17:36:10 +02:00
# include "HookData.h"
2020-04-28 14:43:07 +02:00
using namespace ELFIO ;
2020-06-07 14:18:34 +02:00
std : : optional < ModuleData > ModuleDataFactory : : load ( std : : string path , uint32_t * destination_address_ptr , uint32_t maximum_size , relocation_trampolin_entry_t * trampolin_data , uint32_t trampolin_data_length ) {
2020-04-28 14:43:07 +02:00
elfio reader ;
2020-05-17 13:11:52 +02:00
ModuleData moduleData ;
2020-04-28 14:43:07 +02:00
// Load ELF data
if ( ! reader . load ( path ) ) {
DEBUG_FUNCTION_LINE ( " Can't find or process %s " , path . c_str ( ) ) ;
2020-05-17 13:11:52 +02:00
return std : : nullopt ;
2020-04-28 14:43:07 +02:00
}
uint32_t sec_num = reader . sections . size ( ) ;
uint8_t * * destinations = ( uint8_t * * ) malloc ( sizeof ( uint8_t * ) * sec_num ) ;
2020-05-28 21:45:44 +02:00
uint32_t baseOffset = * destination_address_ptr ;
2020-04-28 14:43:07 +02:00
uint32_t offset_text = baseOffset ;
uint32_t offset_data = offset_text ;
uint32_t entrypoint = offset_text + ( uint32_t ) reader . get_entry ( ) - 0x02000000 ;
uint32_t totalSize = 0 ;
2020-05-03 00:06:11 +02:00
uint32_t endAddress = 0 ;
2020-05-17 19:05:51 +02:00
for ( uint32_t i = 0 ; i < sec_num ; + + i ) {
section * psec = reader . sections [ i ] ;
2020-04-28 14:43:07 +02:00
if ( psec - > get_type ( ) = = 0x80000002 ) {
continue ;
}
if ( ( psec - > get_type ( ) = = SHT_PROGBITS | | psec - > get_type ( ) = = SHT_NOBITS ) & & ( psec - > get_flags ( ) & SHF_ALLOC ) ) {
uint32_t sectionSize = psec - > get_size ( ) ;
2020-12-01 13:06:58 +01:00
totalSize + = sectionSize ;
if ( totalSize > maximum_size ) {
DEBUG_FUNCTION_LINE ( " Couldn't load setup module because it's too big. " ) ;
return { } ;
}
2020-04-28 14:43:07 +02:00
uint32_t address = ( uint32_t ) psec - > get_address ( ) ;
destinations [ psec - > get_index ( ) ] = ( uint8_t * ) baseOffset ;
uint32_t destination = baseOffset + address ;
2020-05-17 19:05:51 +02:00
if ( ( address > = 0x02000000 ) & & address < 0x10000000 ) {
2020-04-28 14:43:07 +02:00
destination - = 0x02000000 ;
destinations [ psec - > get_index ( ) ] - = 0x02000000 ;
baseOffset + = sectionSize ;
offset_data + = sectionSize ;
2020-05-17 19:05:51 +02:00
} else if ( ( address > = 0x10000000 ) & & address < 0xC0000000 ) {
2020-04-28 14:43:07 +02:00
destination - = 0x10000000 ;
destinations [ psec - > get_index ( ) ] - = 0x10000000 ;
2020-05-17 19:05:51 +02:00
} else if ( address > = 0xC0000000 ) {
2020-04-28 14:43:07 +02:00
destination - = 0xC0000000 ;
destinations [ psec - > get_index ( ) ] - = 0xC0000000 ;
} else {
DEBUG_FUNCTION_LINE ( " Unhandled case " ) ;
free ( destinations ) ;
2020-05-17 13:11:52 +02:00
return std : : nullopt ;
2020-04-28 14:43:07 +02:00
}
2020-05-17 19:05:51 +02:00
const char * p = reader . sections [ i ] - > get_data ( ) ;
2020-04-28 14:43:07 +02:00
2020-05-17 19:05:51 +02:00
if ( psec - > get_type ( ) = = SHT_NOBITS ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " memset section %s %08X to 0 (%d bytes) " , psec - > get_name ( ) . c_str ( ) , destination , sectionSize ) ;
2020-05-17 19:05:51 +02:00
memset ( ( void * ) destination , 0 , sectionSize ) ;
} else if ( psec - > get_type ( ) = = SHT_PROGBITS ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Copy section %s %08X -> %08X (%d bytes) " , psec - > get_name ( ) . c_str ( ) , p , destination , sectionSize ) ;
2020-05-17 19:05:51 +02:00
memcpy ( ( void * ) destination , p , sectionSize ) ;
2020-04-28 14:43:07 +02:00
}
//nextAddress = ROUNDUP(destination + sectionSize,0x100);
2020-05-17 19:05:51 +02:00
if ( psec - > get_name ( ) . compare ( " .bss " ) = = 0 ) {
2020-05-17 13:11:52 +02:00
moduleData . setBSSLocation ( destination , sectionSize ) ;
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Saved %s section info. Location: %08X size: %08X " , psec - > get_name ( ) . c_str ( ) , destination , sectionSize ) ;
2020-05-17 19:05:51 +02:00
} else if ( psec - > get_name ( ) . compare ( " .sbss " ) = = 0 ) {
2020-05-17 13:11:52 +02:00
moduleData . setSBSSLocation ( destination , sectionSize ) ;
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Saved %s section info. Location: %08X size: %08X " , psec - > get_name ( ) . c_str ( ) , destination , sectionSize ) ;
}
2020-05-17 13:13:24 +02:00
moduleData . addSectionInfo ( SectionInfo ( psec - > get_name ( ) , destination , sectionSize ) ) ;
DEBUG_FUNCTION_LINE ( " Saved %s section info. Location: %08X size: %08X " , psec - > get_name ( ) . c_str ( ) , destination , sectionSize ) ;
2020-04-28 14:43:07 +02:00
2020-05-17 19:05:51 +02:00
if ( endAddress < destination + sectionSize ) {
2020-05-03 00:06:11 +02:00
endAddress = destination + sectionSize ;
}
2020-05-17 19:05:51 +02:00
DCFlushRange ( ( void * ) destination , sectionSize ) ;
ICInvalidateRange ( ( void * ) destination , sectionSize ) ;
2020-04-28 14:43:07 +02:00
}
}
2020-05-17 19:05:51 +02:00
for ( uint32_t i = 0 ; i < sec_num ; + + i ) {
section * psec = reader . sections [ i ] ;
2020-04-28 14:43:07 +02:00
if ( ( psec - > get_type ( ) = = SHT_PROGBITS | | psec - > get_type ( ) = = SHT_NOBITS ) & & ( psec - > get_flags ( ) & SHF_ALLOC ) ) {
2020-05-17 19:05:51 +02:00
DEBUG_FUNCTION_LINE ( " Linking (%d)... %s " , i , psec - > get_name ( ) . c_str ( ) ) ;
2020-04-28 14:43:07 +02:00
if ( ! linkSection ( reader , psec - > get_index ( ) , ( uint32_t ) destinations [ psec - > get_index ( ) ] , offset_text , offset_data , trampolin_data , trampolin_data_length ) ) {
DEBUG_FUNCTION_LINE ( " elfLink failed " ) ;
free ( destinations ) ;
2020-05-17 13:11:52 +02:00
return std : : nullopt ;
2020-04-28 14:43:07 +02:00
}
}
}
2020-05-17 13:11:52 +02:00
std : : vector < RelocationData > relocationData = getImportRelocationData ( reader , destinations ) ;
2020-04-28 14:43:07 +02:00
2020-05-17 19:05:51 +02:00
for ( auto const & reloc : relocationData ) {
2020-05-17 13:11:52 +02:00
moduleData . addRelocationData ( reloc ) ;
2020-04-28 14:43:07 +02:00
}
2020-05-17 21:14:27 +02:00
std : : optional < SectionInfo > secInfo = moduleData . getSectionInfo ( " .wums.exports " ) ;
if ( secInfo & & secInfo - > getSize ( ) > 0 ) {
size_t entries_count = secInfo - > getSize ( ) / sizeof ( wums_entry_t ) ;
wums_entry_t * entries = ( wums_entry_t * ) secInfo - > getAddress ( ) ;
if ( entries ! = NULL ) {
for ( size_t j = 0 ; j < entries_count ; j + + ) {
2020-06-07 14:18:34 +02:00
wums_entry_t * exp = & entries [ j ] ;
DEBUG_FUNCTION_LINE ( " Saving export of type %08X, name %s, target: %08X " /*,pluginData.getPluginInformation()->getName().c_str()*/ , exp - > type , exp - > name , ( void * ) exp - > address ) ;
2020-05-17 21:14:27 +02:00
ExportData export_data ( exp - > type , exp - > name , exp - > address ) ;
moduleData . addExportData ( export_data ) ;
}
}
}
2020-05-29 17:36:10 +02:00
secInfo = moduleData . getSectionInfo ( " .wums.hooks " ) ;
if ( secInfo & & secInfo - > getSize ( ) > 0 ) {
size_t entries_count = secInfo - > getSize ( ) / sizeof ( wums_hook_t ) ;
wums_hook_t * hooks = ( wums_hook_t * ) secInfo - > getAddress ( ) ;
if ( hooks ! = NULL ) {
for ( size_t j = 0 ; j < entries_count ; j + + ) {
2020-06-07 14:18:34 +02:00
wums_hook_t * hook = & hooks [ j ] ;
2020-05-29 17:36:10 +02:00
DEBUG_FUNCTION_LINE ( " Saving hook of type %08X, target: %08X " /*,pluginData.getPluginInformation()->getName().c_str()*/ , hook - > type , hook - > target ) ;
HookData hook_data ( hook - > type , hook - > target ) ;
moduleData . addHookData ( hook_data ) ;
}
}
}
2020-05-17 21:14:27 +02:00
secInfo = moduleData . getSectionInfo ( " .wums.meta " ) ;
if ( secInfo & & secInfo - > getSize ( ) > 0 ) {
wums_entry_t * entries = ( wums_entry_t * ) secInfo - > getAddress ( ) ;
if ( entries ! = NULL ) {
char * curEntry = ( char * ) secInfo - > getAddress ( ) ;
while ( ( uint32_t ) curEntry < ( uint32_t ) secInfo - > getAddress ( ) + secInfo - > getSize ( ) ) {
if ( * curEntry = = ' \0 ' ) {
curEntry + + ;
continue ;
}
auto firstFound = std : : string ( curEntry ) . find_first_of ( " = " ) ;
if ( firstFound ! = std : : string : : npos ) {
curEntry [ firstFound ] = ' \0 ' ;
std : : string key ( curEntry ) ;
std : : string value ( curEntry + firstFound + 1 ) ;
if ( key . compare ( " export_name " ) = = 0 ) {
DEBUG_FUNCTION_LINE ( " export_name = %s " , value . c_str ( ) ) ;
moduleData . setExportName ( value ) ;
2020-12-26 16:01:46 +01:00
} else if ( key . compare ( " skipEntrypoint " ) = = 0 ) {
2020-06-07 13:58:55 +02:00
if ( value . compare ( " true " ) = = 0 ) {
2020-12-26 16:01:46 +01:00
DEBUG_FUNCTION_LINE ( " skipEntrypoint = %s " , value . c_str ( ) ) ;
moduleData . setSkipEntrypoint ( true ) ;
}
} else if ( key . compare ( " initBeforeRelocationDoneHook " ) = = 0 ) {
if ( value . compare ( " true " ) = = 0 ) {
DEBUG_FUNCTION_LINE ( " initBeforeRelocationDoneHook = %s " , value . c_str ( ) ) ;
moduleData . setInitBeforeRelocationDoneHook ( true ) ;
2020-06-07 13:58:55 +02:00
}
2020-06-07 14:18:34 +02:00
}
if ( key . compare ( " wums " ) = = 0 ) {
2020-05-17 21:14:27 +02:00
if ( value . compare ( " 0.1 " ) ! = 0 ) {
DEBUG_FUNCTION_LINE ( " Warning: Ignoring module - Unsupported WUMS version: %s. \n " , value . c_str ( ) ) ;
return std : : nullopt ;
}
}
}
curEntry + = strlen ( curEntry ) + 1 ;
}
}
}
2020-06-07 14:18:34 +02:00
DCFlushRange ( ( void * ) * destination_address_ptr , totalSize ) ;
ICInvalidateRange ( ( void * ) * destination_address_ptr , totalSize ) ;
2020-04-28 14:43:07 +02:00
free ( destinations ) ;
2020-05-17 13:11:52 +02:00
moduleData . setEntrypoint ( entrypoint ) ;
2020-05-28 21:45:44 +02:00
moduleData . setStartAddress ( * destination_address_ptr ) ;
2020-05-17 13:11:52 +02:00
moduleData . setEndAddress ( endAddress ) ;
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Saved entrypoint as %08X " , entrypoint ) ;
2020-05-28 21:45:44 +02:00
DEBUG_FUNCTION_LINE ( " Saved startAddress as %08X " , * destination_address_ptr ) ;
2020-05-03 00:06:11 +02:00
DEBUG_FUNCTION_LINE ( " Saved endAddress as %08X " , endAddress ) ;
2020-04-28 14:43:07 +02:00
2020-05-28 21:45:44 +02:00
* destination_address_ptr = ( * destination_address_ptr + totalSize + 0x100 ) & 0xFFFFFF00 ;
2020-04-28 14:43:07 +02:00
return moduleData ;
}
2020-05-17 19:05:51 +02:00
std : : vector < RelocationData > ModuleDataFactory : : getImportRelocationData ( elfio & reader , uint8_t * * destinations ) {
2020-05-17 13:11:52 +02:00
std : : vector < RelocationData > result ;
2020-05-17 19:05:51 +02:00
std : : map < uint32_t , std : : string > infoMap ;
2020-04-28 14:43:07 +02:00
uint32_t sec_num = reader . sections . size ( ) ;
2020-05-17 19:05:51 +02:00
for ( uint32_t i = 0 ; i < sec_num ; + + i ) {
section * psec = reader . sections [ i ] ;
2020-04-28 14:43:07 +02:00
if ( psec - > get_type ( ) = = 0x80000002 ) {
infoMap [ i ] = psec - > get_name ( ) ;
}
}
2020-05-17 19:05:51 +02:00
for ( uint32_t i = 0 ; i < sec_num ; + + i ) {
section * psec = reader . sections [ i ] ;
if ( psec - > get_type ( ) = = SHT_RELA | | psec - > get_type ( ) = = SHT_REL ) {
DEBUG_FUNCTION_LINE ( " Found relocation section %s " , psec - > get_name ( ) . c_str ( ) ) ;
2020-04-28 14:43:07 +02:00
relocation_section_accessor rel ( reader , psec ) ;
2020-05-17 19:05:51 +02:00
for ( uint32_t j = 0 ; j < ( uint32_t ) rel . get_entries_num ( ) ; + + j ) {
Elf64_Addr offset ;
Elf_Word type ;
Elf_Sxword addend ;
std : : string sym_name ;
Elf64_Addr sym_value ;
Elf_Half sym_section_index ;
if ( ! rel . get_entry ( j , offset , sym_value , sym_name , type , addend , sym_section_index ) ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Failed to get relocation " ) ;
break ;
}
2020-06-07 14:17:02 +02:00
// uint32_t adjusted_sym_value = (uint32_t) sym_value;
2020-06-07 14:18:34 +02:00
if ( infoMap . count ( sym_section_index ) = = 0 ) {
2020-04-28 14:43:07 +02:00
continue ;
}
2020-05-17 13:11:52 +02:00
std : : optional < ImportRPLInformation > rplInfo = ImportRPLInformation : : createImportRPLInformation ( infoMap [ sym_section_index ] ) ;
2020-05-17 19:05:51 +02:00
if ( ! rplInfo ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Failed to create import information " ) ;
break ;
}
uint32_t section_index = psec - > get_info ( ) ;
// When these relocations are performed, we don't need the 0xC0000000 offset anymore.
2020-05-17 19:05:51 +02:00
RelocationData relocationData ( type , offset - 0x02000000 , addend , ( void * ) ( destinations [ section_index ] + 0x02000000 ) , sym_name , rplInfo . value ( ) ) ;
2020-04-28 14:43:07 +02:00
//relocationData->printInformation();
result . push_back ( relocationData ) ;
}
}
}
return result ;
}
2020-05-17 19:05:51 +02:00
bool ModuleDataFactory : : linkSection ( elfio & reader , uint32_t section_index , uint32_t destination , uint32_t base_text , uint32_t base_data , relocation_trampolin_entry_t * trampolin_data , uint32_t trampolin_data_length ) {
2020-04-28 14:43:07 +02:00
uint32_t sec_num = reader . sections . size ( ) ;
2020-05-17 19:05:51 +02:00
for ( uint32_t i = 0 ; i < sec_num ; + + i ) {
section * psec = reader . sections [ i ] ;
if ( psec - > get_info ( ) = = section_index ) {
DEBUG_FUNCTION_LINE ( " Found relocation section %s " , psec - > get_name ( ) . c_str ( ) ) ;
2020-04-28 14:43:07 +02:00
relocation_section_accessor rel ( reader , psec ) ;
2020-05-17 19:05:51 +02:00
for ( uint32_t j = 0 ; j < ( uint32_t ) rel . get_entries_num ( ) ; + + j ) {
Elf64_Addr offset ;
Elf_Word type ;
Elf_Sxword addend ;
std : : string sym_name ;
Elf64_Addr sym_value ;
Elf_Half sym_section_index ;
if ( ! rel . get_entry ( j , offset , sym_value , sym_name , type , addend , sym_section_index ) ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Failed to get relocation " ) ;
break ;
}
uint32_t adjusted_sym_value = ( uint32_t ) sym_value ;
2020-05-17 19:05:51 +02:00
if ( ( adjusted_sym_value > = 0x02000000 ) & & adjusted_sym_value < 0x10000000 ) {
2020-04-28 14:43:07 +02:00
adjusted_sym_value - = 0x02000000 ;
adjusted_sym_value + = base_text ;
2020-05-17 19:05:51 +02:00
} else if ( ( adjusted_sym_value > = 0x10000000 ) & & adjusted_sym_value < 0xC0000000 ) {
2020-04-28 14:43:07 +02:00
adjusted_sym_value - = 0x10000000 ;
adjusted_sym_value + = base_data ;
2020-05-17 19:05:51 +02:00
} else if ( adjusted_sym_value > = 0xC0000000 ) {
2020-04-28 14:43:07 +02:00
// Skip imports
continue ;
2020-05-17 19:05:51 +02:00
} else if ( adjusted_sym_value = = 0x0 ) {
2020-04-28 14:43:07 +02:00
//
} else {
2020-05-17 19:05:51 +02:00
DEBUG_FUNCTION_LINE ( " Unhandled case %08X " , adjusted_sym_value ) ;
2020-04-28 14:43:07 +02:00
return false ;
}
2020-05-17 19:05:51 +02:00
if ( sym_section_index = = SHN_ABS ) {
2020-04-28 14:43:07 +02:00
//
2020-05-17 19:05:51 +02:00
} else if ( sym_section_index > SHN_LORESERVE ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " NOT IMPLEMENTED: %04X " , sym_section_index ) ;
return false ;
}
2020-05-17 19:05:51 +02:00
if ( ! ElfUtils : : elfLinkOne ( type , offset , addend , destination , adjusted_sym_value , trampolin_data , trampolin_data_length , RELOC_TYPE_FIXED ) ) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE ( " Link failed " ) ;
return false ;
}
}
DEBUG_FUNCTION_LINE ( " done " ) ;
}
}
return true ;
}