2020-01-18 09:38:21 +01:00
/*
2020-02-01 17:57:12 -07:00
* Copyright ( c ) 2019 - 2020 , Andrew Kaster < andrewdkaster @ gmail . com >
2020-10-10 18:17:49 +03:00
* Copyright ( c ) 2020 , Itamar S . < itamar8910 @ gmail . com >
2020-01-18 09:38:21 +01:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2021-01-24 15:28:26 +01:00
# include <AK/Debug.h>
2021-01-30 09:21:54 +01:00
# include <AK/Optional.h>
2020-01-03 23:31:51 -05:00
# include <AK/StringBuilder.h>
2020-04-11 12:24:07 -06:00
# include <LibELF/DynamicLoader.h>
2020-04-11 12:34:00 -06:00
# include <LibELF/Validation.h>
2020-01-03 23:31:51 -05:00
# include <assert.h>
# include <dlfcn.h>
# include <stdio.h>
# include <stdlib.h>
2020-03-08 12:05:14 +01:00
# include <string.h>
2020-08-08 22:45:20 -04:00
# include <sys/mman.h>
2021-01-31 10:13:23 +01:00
# include <sys/stat.h>
2020-01-03 23:31:51 -05:00
2020-08-08 22:45:20 -04:00
# ifndef __serenity__
static void * mmap_with_name ( void * addr , size_t length , int prot , int flags , int fd , off_t offset , const char * )
{
return mmap ( addr , length , prot , flags , fd , offset ) ;
}
2021-01-28 15:03:35 +01:00
# define MAP_RANDOMIZED 0
2020-08-08 22:45:20 -04:00
# endif
2020-04-11 12:24:07 -06:00
namespace ELF {
2020-10-17 14:39:36 +03:00
static bool s_always_bind_now = false ;
2020-01-03 23:31:51 -05:00
2021-01-31 10:13:23 +01:00
RefPtr < DynamicLoader > DynamicLoader : : try_create ( int fd , String filename )
2020-01-03 23:31:51 -05:00
{
2021-01-31 10:13:23 +01:00
struct stat stat ;
if ( fstat ( fd , & stat ) < 0 ) {
perror ( " DynamicLoader::try_create fstat " ) ;
return { } ;
}
2020-01-03 23:31:51 -05:00
2021-01-31 10:13:23 +01:00
ASSERT ( stat . st_size > = 0 ) ;
size_t size = static_cast < size_t > ( stat . st_size ) ;
2020-10-10 18:17:49 +03:00
if ( size < sizeof ( Elf32_Ehdr ) )
2021-01-31 10:13:23 +01:00
return { } ;
2020-10-10 18:17:49 +03:00
2021-01-31 10:13:23 +01:00
String file_mmap_name = String : : formatted ( " ELF_DYN: {} " , filename ) ;
auto * data = mmap_with_name ( nullptr , size , PROT_READ , MAP_PRIVATE , fd , 0 , file_mmap_name . characters ( ) ) ;
if ( data = = MAP_FAILED ) {
perror ( " DynamicLoader::try_create mmap " ) ;
return { } ;
}
return adopt ( * new DynamicLoader ( fd , move ( filename ) , data , size ) ) ;
2020-10-10 18:17:49 +03:00
}
2021-01-31 10:13:23 +01:00
DynamicLoader : : DynamicLoader ( int fd , String filename , void * data , size_t size )
: m_filename ( move ( filename ) )
2020-01-03 23:31:51 -05:00
, m_file_size ( size )
, m_image_fd ( fd )
2021-01-31 10:13:23 +01:00
, m_file_data ( data )
, m_elf_image ( ( u8 * ) m_file_data , m_file_size )
2020-01-03 23:31:51 -05:00
{
2020-10-10 18:17:49 +03:00
m_tls_size = calculate_tls_size ( ) ;
2020-10-17 14:39:36 +03:00
m_valid = validate ( ) ;
2020-10-10 18:17:49 +03:00
}
2021-01-31 10:13:23 +01:00
DynamicLoader : : ~ DynamicLoader ( )
{
munmap ( m_file_data , m_file_size ) ;
}
2021-01-25 13:16:39 +01:00
const DynamicObject & DynamicLoader : : dynamic_object ( ) const
2020-10-10 18:17:49 +03:00
{
2021-01-25 13:16:39 +01:00
if ( ! m_cached_dynamic_object ) {
VirtualAddress dynamic_section_address ;
m_elf_image . for_each_program_header ( [ & dynamic_section_address ] ( auto program_header ) {
if ( program_header . type ( ) = = PT_DYNAMIC ) {
dynamic_section_address = VirtualAddress ( program_header . raw_data ( ) ) ;
}
return IterationDecision : : Continue ;
} ) ;
ASSERT ( ! dynamic_section_address . is_null ( ) ) ;
m_cached_dynamic_object = ELF : : DynamicObject : : construct ( VirtualAddress ( m_elf_image . base_address ( ) ) , dynamic_section_address ) ;
}
return * m_cached_dynamic_object ;
2020-10-10 18:17:49 +03:00
}
size_t DynamicLoader : : calculate_tls_size ( ) const
{
size_t tls_size = 0 ;
m_elf_image . for_each_program_header ( [ & tls_size ] ( auto program_header ) {
if ( program_header . type ( ) = = PT_TLS ) {
tls_size = program_header . size_in_memory ( ) ;
}
2020-12-25 14:42:42 +01:00
return IterationDecision : : Continue ;
2020-10-10 18:17:49 +03:00
} ) ;
return tls_size ;
}
bool DynamicLoader : : validate ( )
{
2021-01-31 10:13:23 +01:00
auto * elf_header = ( Elf32_Ehdr * ) m_file_data ;
return validate_elf_header ( * elf_header , m_file_size ) & & validate_program_headers ( * elf_header , m_file_size , ( u8 * ) m_file_data , m_file_size , & m_program_interpreter ) ;
2020-01-03 23:31:51 -05:00
}
2020-04-11 12:24:07 -06:00
void * DynamicLoader : : symbol_for_name ( const char * name )
2020-01-03 23:31:51 -05:00
{
auto symbol = m_dynamic_object - > hash_section ( ) . lookup_symbol ( name ) ;
if ( symbol . is_undefined ( ) )
return nullptr ;
return m_dynamic_object - > base_address ( ) . offset ( symbol . value ( ) ) . as_ptr ( ) ;
}
2020-10-10 18:17:49 +03:00
RefPtr < DynamicObject > DynamicLoader : : load_from_image ( unsigned flags , size_t total_tls_size )
2020-01-03 23:31:51 -05:00
{
2020-12-18 15:59:22 +02:00
m_valid = m_elf_image . is_valid ( ) ;
2020-01-03 23:31:51 -05:00
if ( ! m_valid ) {
2021-01-25 10:04:03 +01:00
dbgln ( " DynamicLoader::load_from_image failed: image is invalid " ) ;
2020-10-10 18:17:49 +03:00
return nullptr ;
2020-01-03 23:31:51 -05:00
}
2020-10-10 18:17:49 +03:00
load_program_headers ( ) ;
2020-02-28 22:47:39 -07:00
2020-10-10 18:17:49 +03:00
m_dynamic_object = DynamicObject : : construct ( m_text_segment_load_address , m_dynamic_section_address ) ;
m_dynamic_object - > set_tls_offset ( m_tls_offset ) ;
m_dynamic_object - > set_tls_size ( m_tls_size ) ;
2020-01-03 23:31:51 -05:00
2020-10-10 18:17:49 +03:00
auto rc = load_stage_2 ( flags , total_tls_size ) ;
2020-12-18 15:59:22 +02:00
if ( ! rc ) {
2021-01-25 10:04:03 +01:00
dbgln ( " DynamicLoader::load_from_image failed at load_stage_2 " ) ;
2020-10-10 18:17:49 +03:00
return nullptr ;
2020-12-18 15:59:22 +02:00
}
2020-10-10 18:17:49 +03:00
return m_dynamic_object ;
2020-01-03 23:31:51 -05:00
}
2020-10-10 18:17:49 +03:00
bool DynamicLoader : : load_stage_2 ( unsigned flags , size_t total_tls_size )
2020-01-03 23:31:51 -05:00
{
ASSERT ( flags & RTLD_GLOBAL ) ;
2021-01-24 15:28:26 +01:00
# if DYNAMIC_LOAD_DEBUG
2020-01-03 23:31:51 -05:00
m_dynamic_object - > dump ( ) ;
# endif
if ( m_dynamic_object - > has_text_relocations ( ) ) {
ASSERT ( m_text_segment_load_address . get ( ) ! = 0 ) ;
2020-12-29 02:13:26 +01:00
2020-12-29 10:55:35 +01:00
# ifndef AK_OS_MACOS
2020-12-29 02:13:26 +01:00
// Remap this text region as private.
if ( mremap ( m_text_segment_load_address . as_ptr ( ) , m_text_segment_size , m_text_segment_size , MAP_PRIVATE ) = = MAP_FAILED ) {
perror ( " mremap .text: MAP_PRIVATE " ) ;
return false ;
}
2020-12-29 10:55:35 +01:00
# endif
2020-12-29 02:13:26 +01:00
2020-01-03 23:31:51 -05:00
if ( 0 > mprotect ( m_text_segment_load_address . as_ptr ( ) , m_text_segment_size , PROT_READ | PROT_WRITE ) ) {
2020-01-08 21:38:05 -07:00
perror ( " mprotect .text: PROT_READ | PROT_WRITE " ) ; // FIXME: dlerror?
2020-01-03 23:31:51 -05:00
return false ;
}
}
2021-01-02 00:48:19 +00:00
do_main_relocations ( total_tls_size ) ;
return true ;
}
void DynamicLoader : : do_main_relocations ( size_t total_tls_size )
{
auto do_single_relocation = [ & ] ( ELF : : DynamicObject : : Relocation relocation ) {
switch ( do_relocation ( total_tls_size , relocation ) ) {
case RelocationResult : : Failed :
dbgln ( " Loader.so: {} unresolved symbol '{}' " , m_filename , relocation . symbol ( ) . name ( ) ) ;
ASSERT_NOT_REACHED ( ) ;
break ;
case RelocationResult : : ResolveLater :
m_unresolved_relocations . append ( relocation ) ;
break ;
case RelocationResult : : Success :
break ;
}
return IterationDecision : : Continue ;
} ;
m_dynamic_object - > relocation_section ( ) . for_each_relocation ( do_single_relocation ) ;
m_dynamic_object - > plt_relocation_section ( ) . for_each_relocation ( do_single_relocation ) ;
}
2020-01-03 23:31:51 -05:00
2021-01-02 00:48:19 +00:00
RefPtr < DynamicObject > DynamicLoader : : load_stage_3 ( unsigned flags , size_t total_tls_size )
{
do_lazy_relocations ( total_tls_size ) ;
2020-10-10 18:17:49 +03:00
if ( flags & RTLD_LAZY ) {
setup_plt_trampoline ( ) ;
}
2020-01-03 23:31:51 -05:00
2021-01-29 14:36:46 +01:00
if ( mprotect ( m_text_segment_load_address . as_ptr ( ) , m_text_segment_size , PROT_READ | PROT_EXEC ) < 0 ) {
perror ( " mprotect .text: PROT_READ | PROT_EXEC " ) ; // FIXME: dlerror?
return nullptr ;
2020-01-03 23:31:51 -05:00
}
call_object_init_functions ( ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Loaded {} " , m_filename ) ;
2021-01-02 00:48:19 +00:00
return m_dynamic_object ;
}
void DynamicLoader : : do_lazy_relocations ( size_t total_tls_size )
{
for ( const auto & relocation : m_unresolved_relocations ) {
if ( auto res = do_relocation ( total_tls_size , relocation ) ; res ! = RelocationResult : : Success ) {
dbgln ( " Loader.so: {} unresolved symbol '{}' " , m_filename , relocation . symbol ( ) . name ( ) ) ;
ASSERT_NOT_REACHED ( ) ;
}
}
2020-01-03 23:31:51 -05:00
}
2020-10-10 18:17:49 +03:00
void DynamicLoader : : load_program_headers ( )
2020-01-03 23:31:51 -05:00
{
2021-01-30 09:21:54 +01:00
Optional < ProgramHeaderRegion > text_region ;
Optional < ProgramHeaderRegion > data_region ;
Optional < ProgramHeaderRegion > tls_region ;
2020-01-03 23:31:51 -05:00
2020-01-08 21:38:05 -07:00
VirtualAddress dynamic_region_desired_vaddr ;
2020-01-03 23:31:51 -05:00
2020-10-10 18:17:49 +03:00
m_elf_image . for_each_program_header ( [ & ] ( const Image : : ProgramHeader & program_header ) {
2021-01-30 09:21:54 +01:00
ProgramHeaderRegion region ;
region . set_program_header ( program_header . raw_header ( ) ) ;
2021-01-29 13:45:41 +01:00
if ( region . is_tls_template ( ) ) {
2021-01-30 09:21:54 +01:00
ASSERT ( ! tls_region . has_value ( ) ) ;
tls_region = region ;
2021-01-29 13:45:41 +01:00
} else if ( region . is_load ( ) ) {
if ( region . is_executable ( ) ) {
2021-01-30 09:21:54 +01:00
ASSERT ( ! text_region . has_value ( ) ) ;
text_region = region ;
2021-01-29 13:45:41 +01:00
} else {
2021-01-30 09:21:54 +01:00
ASSERT ( ! data_region . has_value ( ) ) ;
data_region = region ;
2021-01-29 13:45:41 +01:00
}
2020-03-08 12:05:14 +01:00
} else if ( region . is_dynamic ( ) ) {
2020-01-08 21:38:05 -07:00
dynamic_region_desired_vaddr = region . desired_load_address ( ) ;
}
2020-12-25 14:42:42 +01:00
return IterationDecision : : Continue ;
2020-01-03 23:31:51 -05:00
} ) ;
2021-01-30 09:21:54 +01:00
ASSERT ( text_region . has_value ( ) ) ;
ASSERT ( data_region . has_value ( ) ) ;
2020-01-03 23:31:51 -05:00
// Process regions in order: .text, .data, .tls
2021-01-30 09:21:54 +01:00
void * requested_load_address = m_elf_image . is_dynamic ( ) ? nullptr : text_region . value ( ) . desired_load_address ( ) . as_ptr ( ) ;
2020-12-29 02:13:26 +01:00
2021-01-28 15:03:35 +01:00
int text_mmap_flags = MAP_SHARED ;
if ( m_elf_image . is_dynamic ( ) )
text_mmap_flags | = MAP_RANDOMIZED ;
else
text_mmap_flags | = MAP_FIXED ;
2021-01-30 09:21:54 +01:00
ASSERT ( ! text_region . value ( ) . is_writable ( ) ) ;
2020-12-29 02:13:26 +01:00
2021-01-28 15:03:35 +01:00
// First, we map the text *and* data segments, in order to allocate enough VM
// to hold both contiguously in the address space.
Checked < size_t > total_mapping_size ;
2021-01-30 09:21:54 +01:00
total_mapping_size = text_region . value ( ) . required_load_size ( ) ;
total_mapping_size + = data_region . value ( ) . required_load_size ( ) ;
2021-01-28 15:03:35 +01:00
ASSERT ( ! total_mapping_size . has_overflow ( ) ) ;
2021-01-29 13:45:41 +01:00
auto * text_segment_begin = ( u8 * ) mmap_with_name (
2020-12-18 15:59:22 +02:00
requested_load_address ,
2021-01-28 15:03:35 +01:00
total_mapping_size . value ( ) ,
2021-01-29 14:36:46 +01:00
PROT_READ ,
2021-01-28 15:03:35 +01:00
text_mmap_flags ,
2020-10-17 14:39:36 +03:00
m_image_fd ,
2021-01-30 09:21:54 +01:00
text_region . value ( ) . offset ( ) ,
2021-01-25 12:08:57 +01:00
String : : formatted ( " {}: .text " , m_filename ) . characters ( ) ) ;
2021-01-29 13:45:41 +01:00
2020-01-03 23:31:51 -05:00
if ( MAP_FAILED = = text_segment_begin ) {
2021-01-29 08:50:43 +01:00
perror ( " mmap text / initial segment " ) ;
2020-01-03 23:31:51 -05:00
ASSERT_NOT_REACHED ( ) ;
}
2021-01-29 13:45:41 +01:00
2020-12-18 15:59:22 +02:00
ASSERT ( requested_load_address = = nullptr | | requested_load_address = = text_segment_begin ) ;
2021-01-30 09:21:54 +01:00
m_text_segment_size = text_region . value ( ) . required_load_size ( ) ;
2020-08-08 23:13:31 -04:00
m_text_segment_load_address = VirtualAddress { ( FlatPtr ) text_segment_begin } ;
2020-01-08 21:38:05 -07:00
2021-01-28 15:03:35 +01:00
// Then, we unmap the data segment part of the above combined VM allocation.
2021-01-30 09:21:54 +01:00
auto * data_segment_address = ( u8 * ) text_segment_begin + text_region . value ( ) . required_load_size ( ) ;
if ( munmap ( data_segment_address , data_region . value ( ) . required_load_size ( ) ) < 0 ) {
2021-01-28 15:03:35 +01:00
perror ( " munmap " ) ;
ASSERT_NOT_REACHED ( ) ;
}
2020-12-18 15:59:22 +02:00
if ( m_elf_image . is_dynamic ( ) )
m_dynamic_section_address = dynamic_region_desired_vaddr . offset ( m_text_segment_load_address . get ( ) ) ;
else
m_dynamic_section_address = dynamic_region_desired_vaddr ;
2020-01-03 23:31:51 -05:00
2021-01-28 15:03:35 +01:00
// Finally, we remap the data segment, this time privately.
2021-01-29 13:45:41 +01:00
auto * data_segment = ( u8 * ) mmap_with_name (
2021-01-28 15:03:35 +01:00
data_segment_address ,
2021-01-30 09:21:54 +01:00
data_region . value ( ) . required_load_size ( ) ,
data_region . value ( ) . mmap_prot ( ) ,
2021-01-27 20:49:56 +01:00
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED ,
2020-10-17 14:39:36 +03:00
0 ,
0 ,
2021-01-25 12:08:57 +01:00
String : : formatted ( " {}: .data " , m_filename ) . characters ( ) ) ;
2021-01-29 13:45:41 +01:00
if ( MAP_FAILED = = data_segment ) {
2021-01-28 15:03:35 +01:00
perror ( " mmap data segment " ) ;
2020-01-03 23:31:51 -05:00
ASSERT_NOT_REACHED ( ) ;
}
2021-01-29 13:45:41 +01:00
VirtualAddress data_segment_start ;
if ( m_elf_image . is_dynamic ( ) )
2021-01-30 09:21:54 +01:00
data_segment_start = data_region . value ( ) . desired_load_address ( ) . offset ( ( FlatPtr ) text_segment_begin ) ;
2021-01-29 13:45:41 +01:00
else
2021-01-30 09:21:54 +01:00
data_segment_start = data_region . value ( ) . desired_load_address ( ) ;
2021-01-29 13:45:41 +01:00
2021-01-31 10:13:23 +01:00
memcpy ( data_segment_start . as_ptr ( ) , ( u8 * ) m_file_data + data_region . value ( ) . offset ( ) , data_region . value ( ) . size_in_image ( ) ) ;
2021-01-29 13:45:41 +01:00
2020-12-18 15:59:22 +02:00
// FIXME: Initialize the values in the TLS section. Currently, it is zeroed.
2020-01-03 23:31:51 -05:00
}
2021-01-02 00:48:19 +00:00
DynamicLoader : : RelocationResult DynamicLoader : : do_relocation ( size_t total_tls_size , ELF : : DynamicObject : : Relocation relocation )
2020-01-03 23:31:51 -05:00
{
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Relocation symbol: {}, type: {} " , relocation . symbol ( ) . name ( ) , relocation . type ( ) ) ;
2021-01-02 00:48:19 +00:00
FlatPtr * patch_ptr = nullptr ;
if ( is_dynamic ( ) )
patch_ptr = ( FlatPtr * ) ( m_dynamic_object - > base_address ( ) . as_ptr ( ) + relocation . offset ( ) ) ;
else
patch_ptr = ( FlatPtr * ) ( FlatPtr ) relocation . offset ( ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " dynamic object base address: {:p} " , m_dynamic_object - > base_address ( ) . as_ptr ( ) ) ;
dbgln < DYNAMIC_LOAD_DEBUG > ( " relocation offset: {:#08x} " , relocation . offset ( ) ) ;
dbgln < DYNAMIC_LOAD_DEBUG > ( " patch_ptr: {:p} " , patch_ptr ) ;
2021-01-02 00:48:19 +00:00
switch ( relocation . type ( ) ) {
case R_386_NONE :
// Apparently most loaders will just skip these?
// Seems if the 'link editor' generates one something is funky with your code
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " None relocation. No symbol, no nothing. " ) ;
2021-01-02 00:48:19 +00:00
break ;
case R_386_32 : {
auto symbol = relocation . symbol ( ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Absolute relocation: name: '{}', value: {} " , symbol . name ( ) , symbol . value ( ) ) ;
2021-01-02 00:48:19 +00:00
auto res = lookup_symbol ( symbol ) ;
2021-01-25 13:09:08 +01:00
if ( ! res . has_value ( ) ) {
if ( symbol . bind ( ) = = STB_WEAK )
2021-01-02 00:48:19 +00:00
return RelocationResult : : ResolveLater ;
dbgln ( " ERROR: symbol not found: {}. " , symbol . name ( ) ) ;
ASSERT_NOT_REACHED ( ) ;
2020-01-03 23:31:51 -05:00
}
2021-01-25 13:09:08 +01:00
u32 symbol_address = res . value ( ) . address ;
2021-01-02 00:48:19 +00:00
* patch_ptr + = symbol_address ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Symbol address: {:p} " , * patch_ptr ) ;
2021-01-02 00:48:19 +00:00
break ;
}
case R_386_PC32 : {
auto symbol = relocation . symbol ( ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " PC-relative relocation: '{}', value: {:p} " , symbol . name ( ) , symbol . value ( ) ) ;
2021-01-02 00:48:19 +00:00
auto res = lookup_symbol ( symbol ) ;
2021-01-25 13:09:08 +01:00
ASSERT ( res . has_value ( ) ) ;
u32 relative_offset = ( res . value ( ) . address - ( FlatPtr ) ( m_dynamic_object - > base_address ( ) . as_ptr ( ) + relocation . offset ( ) ) ) ;
2021-01-02 00:48:19 +00:00
* patch_ptr + = relative_offset ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Symbol address: {:p} " , * patch_ptr ) ;
2021-01-02 00:48:19 +00:00
break ;
}
case R_386_GLOB_DAT : {
auto symbol = relocation . symbol ( ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Global data relocation: '{}', value: {:p} " , symbol . name ( ) , symbol . value ( ) ) ;
2021-01-02 00:48:19 +00:00
auto res = lookup_symbol ( symbol ) ;
2021-01-25 13:09:08 +01:00
if ( ! res . has_value ( ) ) {
2021-01-02 00:48:19 +00:00
// We do not support these
// TODO: Can we tell gcc not to generate the piece of code that uses these?
// (--disable-tm-clone-registry flag in gcc conifugraion?)
if ( ! strcmp ( symbol . name ( ) , " __deregister_frame_info " ) | | ! strcmp ( symbol . name ( ) , " _ITM_registerTMCloneTable " )
| | ! strcmp ( symbol . name ( ) , " _ITM_deregisterTMCloneTable " ) | | ! strcmp ( symbol . name ( ) , " __register_frame_info " ) ) {
break ;
2020-12-20 22:32:10 +02:00
}
2021-01-02 00:48:19 +00:00
2021-01-25 13:09:08 +01:00
if ( symbol . bind ( ) = = STB_WEAK )
2021-01-02 00:48:19 +00:00
return RelocationResult : : ResolveLater ;
// Symbol not found
return RelocationResult : : Failed ;
2020-01-03 23:31:51 -05:00
}
2021-01-25 13:09:08 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " symbol found, location: {:#08x} " , res . value ( ) . address ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " object: {} " , m_filename ) ;
2021-01-02 00:48:19 +00:00
2021-01-25 13:09:08 +01:00
u32 symbol_location = res . value ( ) . address ;
2021-01-02 00:48:19 +00:00
ASSERT ( symbol_location ! = ( FlatPtr ) m_dynamic_object - > base_address ( ) . as_ptr ( ) ) ;
* patch_ptr = symbol_location ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Symbol address: {:p} " , * patch_ptr ) ;
2021-01-02 00:48:19 +00:00
break ;
}
case R_386_RELATIVE : {
// FIXME: According to the spec, R_386_relative ones must be done first.
// We could explicitly do them first using m_number_of_relocatoins from DT_RELCOUNT
// However, our compiler is nice enough to put them at the front of the relocations for us :)
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Load address relocation at offset {:#08x} " , relocation . offset ( ) ) ;
dbgln < DYNAMIC_LOAD_DEBUG > ( " patch ptr == %p, adding load base address ({:p}) to it and storing {:p} " , * patch_ptr , m_dynamic_object - > base_address ( ) . as_ptr ( ) , * patch_ptr + m_dynamic_object - > base_address ( ) . as_ptr ( ) ) ;
2021-01-02 00:48:19 +00:00
* patch_ptr + = ( FlatPtr ) m_dynamic_object - > base_address ( ) . as_ptr ( ) ; // + addend for RelA (addend for Rel is stored at addr)
break ;
}
case R_386_TLS_TPOFF32 :
case R_386_TLS_TPOFF : {
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Relocation type: R_386_TLS_TPOFF at offset {:#08x} " , relocation . offset ( ) ) ;
2021-01-02 00:48:19 +00:00
auto symbol = relocation . symbol ( ) ;
// For some reason, LibC has a R_386_TLS_TPOFF that refers to the undefined symbol.. huh
if ( relocation . symbol_index ( ) = = 0 )
2020-01-03 23:31:51 -05:00
break ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Symbol index: {} " , symbol . index ( ) ) ;
dbgln < DYNAMIC_LOAD_DEBUG > ( " Symbol is_undefined?: {} " , symbol . is_undefined ( ) ) ;
dbgln < DYNAMIC_LOAD_DEBUG > ( " TLS relocation: '{}', value: {:p} " , symbol . name ( ) , symbol . value ( ) ) ;
2021-01-02 00:48:19 +00:00
auto res = lookup_symbol ( symbol ) ;
2021-01-25 13:09:08 +01:00
if ( ! res . has_value ( ) )
2020-01-03 23:31:51 -05:00
break ;
2021-01-25 13:09:08 +01:00
u32 symbol_value = res . value ( ) . value ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " symbol value: {} " , symbol_value ) ;
2021-01-25 13:09:08 +01:00
auto * dynamic_object_of_symbol = res . value ( ) . dynamic_object ;
2021-01-02 00:48:19 +00:00
ASSERT ( dynamic_object_of_symbol ) ;
size_t offset_of_tls_end = dynamic_object_of_symbol - > tls_offset ( ) . value ( ) + dynamic_object_of_symbol - > tls_size ( ) . value ( ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " patch ptr: {:p} " , patch_ptr ) ;
dbgln < DYNAMIC_LOAD_DEBUG > ( " tls end offset: {}, total tls size: {} " , offset_of_tls_end , total_tls_size ) ;
2021-01-02 00:48:19 +00:00
* patch_ptr = ( offset_of_tls_end - total_tls_size - symbol_value - sizeof ( Elf32_Addr ) ) ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " *patch ptr: {} " , ( i32 ) * patch_ptr ) ;
2021-01-02 00:48:19 +00:00
break ;
}
case R_386_JMP_SLOT : {
2020-01-03 23:31:51 -05:00
// FIXME: Or BIND_NOW flag passed in?
if ( m_dynamic_object - > must_bind_now ( ) | | s_always_bind_now ) {
// Eagerly BIND_NOW the PLT entries, doing all the symbol looking goodness
// The patch method returns the address for the LAZY fixup path, but we don't need it here
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " patching plt reloaction: {:p} " , relocation . offset_in_section ( ) ) ;
2020-12-20 16:09:48 -07:00
[[maybe_unused]] auto rc = m_dynamic_object - > patch_plt_entry ( relocation . offset_in_section ( ) ) ;
2020-01-03 23:31:51 -05:00
} else {
u8 * relocation_address = relocation . address ( ) . as_ptr ( ) ;
2020-12-18 15:59:22 +02:00
if ( m_elf_image . is_dynamic ( ) )
* ( u32 * ) relocation_address + = ( FlatPtr ) m_dynamic_object - > base_address ( ) . as_ptr ( ) ;
2020-01-03 23:31:51 -05:00
}
2021-01-02 00:48:19 +00:00
break ;
}
default :
// Raise the alarm! Someone needs to implement this relocation type
2021-01-25 10:04:03 +01:00
dbgln ( " Found a new exciting relocation type {} " , relocation . type ( ) ) ;
2021-01-02 00:48:19 +00:00
// printf("DynamicLoader: Found unknown relocation type %d\n", relocation.type());
ASSERT_NOT_REACHED ( ) ;
break ;
}
return RelocationResult : : Success ;
2020-01-03 23:31:51 -05:00
}
// Defined in <arch>/plt_trampoline.S
2020-12-24 08:41:54 -07:00
extern " C " void _plt_trampoline ( void ) __attribute__ ( ( visibility ( " hidden " ) ) ) ;
2020-01-03 23:31:51 -05:00
2020-04-11 12:24:07 -06:00
void DynamicLoader : : setup_plt_trampoline ( )
2020-01-03 23:31:51 -05:00
{
2020-10-17 14:39:36 +03:00
ASSERT ( m_dynamic_object ) ;
2020-01-03 23:31:51 -05:00
VirtualAddress got_address = m_dynamic_object - > plt_got_base_address ( ) ;
2020-08-08 23:13:31 -04:00
FlatPtr * got_ptr = ( FlatPtr * ) got_address . as_ptr ( ) ;
2020-10-17 14:39:36 +03:00
got_ptr [ 1 ] = ( FlatPtr ) m_dynamic_object . ptr ( ) ;
2020-08-08 23:13:31 -04:00
got_ptr [ 2 ] = ( FlatPtr ) & _plt_trampoline ;
2020-01-03 23:31:51 -05:00
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Set GOT PLT entries at {:p}: [0] = {:p} [1] = {:p}, [2] = {:p} " , got_ptr , ( void * ) got_ptr [ 0 ] , ( void * ) got_ptr [ 1 ] , ( void * ) got_ptr [ 2 ] ) ;
2020-01-03 23:31:51 -05:00
}
2020-08-11 23:53:54 +02:00
// Called from our ASM routine _plt_trampoline.
// Tell the compiler that it might be called from other places:
2020-10-17 14:39:36 +03:00
extern " C " Elf32_Addr _fixup_plt_entry ( DynamicObject * object , u32 relocation_offset ) ;
extern " C " Elf32_Addr _fixup_plt_entry ( DynamicObject * object , u32 relocation_offset )
2020-01-03 23:31:51 -05:00
{
return object - > patch_plt_entry ( relocation_offset ) ;
}
2020-04-11 12:24:07 -06:00
void DynamicLoader : : call_object_init_functions ( )
2020-01-03 23:31:51 -05:00
{
typedef void ( * InitFunc ) ( ) ;
2020-10-10 18:17:49 +03:00
if ( m_dynamic_object - > has_init_section ( ) ) {
auto init_function = ( InitFunc ) ( m_dynamic_object - > init_section ( ) . address ( ) . as_ptr ( ) ) ;
2020-01-03 23:31:51 -05:00
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Calling DT_INIT at {:p} " , init_function ) ;
2020-10-10 18:17:49 +03:00
( init_function ) ( ) ;
}
2020-01-03 23:31:51 -05:00
2020-10-10 18:17:49 +03:00
if ( m_dynamic_object - > has_init_array_section ( ) ) {
auto init_array_section = m_dynamic_object - > init_array_section ( ) ;
InitFunc * init_begin = ( InitFunc * ) ( init_array_section . address ( ) . as_ptr ( ) ) ;
InitFunc * init_end = init_begin + init_array_section . entry_count ( ) ;
while ( init_begin ! = init_end ) {
// Android sources claim that these can be -1, to be ignored.
// 0 definitely shows up. Apparently 0/-1 are valid? Confusing.
if ( ! * init_begin | | ( ( FlatPtr ) * init_begin = = ( FlatPtr ) - 1 ) )
continue ;
2021-01-25 10:04:03 +01:00
dbgln < DYNAMIC_LOAD_DEBUG > ( " Calling DT_INITARRAY entry at {:p} " , * init_begin ) ;
2020-10-10 18:17:49 +03:00
( * init_begin ) ( ) ;
+ + init_begin ;
}
2020-01-03 23:31:51 -05:00
}
}
2020-04-11 12:24:07 -06:00
u32 DynamicLoader : : ProgramHeaderRegion : : mmap_prot ( ) const
2020-01-03 23:31:51 -05:00
{
int prot = 0 ;
prot | = is_executable ( ) ? PROT_EXEC : 0 ;
prot | = is_readable ( ) ? PROT_READ : 0 ;
prot | = is_writable ( ) ? PROT_WRITE : 0 ;
return prot ;
}
2020-04-11 12:24:07 -06:00
2021-01-25 13:09:08 +01:00
Optional < DynamicObject : : SymbolLookupResult > DynamicLoader : : lookup_symbol ( const ELF : : DynamicObject : : Symbol & symbol ) const
2020-10-10 18:17:49 +03:00
{
2020-10-17 14:39:36 +03:00
return m_dynamic_object - > lookup_symbol ( symbol ) ;
2020-10-10 18:17:49 +03:00
}
2020-04-11 12:24:07 -06:00
} // end namespace ELF