I tried to find an answer to you, but failed.
What I actually succeeded to do - is only to simplify the questionable code:
void f1( )
{
}
int main( )
{
*(char*) f1 = *(char*) f1;
return( 0 );
}
And yes, it fails on segmentation fault (in gcc) or on memory access violation (in MS VC).
EDIT:
Actually I succeeded to do what you want
(basing on the answer of Basile Starynkevitch). But only for x86, only in gcc, and only for your specific example. Below are several code examples.
First - the simplified example.
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
void f1( )
{
}
int main( )
{
int rc;
int pagesize;
char *p;
f1( );
pagesize = sysconf( _SC_PAGE_SIZE );
printf( "pagesize=%d (0x%08X).\n", pagesize, pagesize );
if( pagesize == -1 )
return( 2 );
p = (char*) f1;
printf( "p=0x%08X.\n", p );
p = (char*) ((size_t) p & ~(pagesize - 1));
printf( "p=0x%08X.\n", p );
rc = mprotect( p, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC );
printf( "rc=%d.\n", rc );
if( rc != 0 )
return( 2 );
printf( "'mprotect()' succeeded.\n" );
*(char*) f1 = *(char*) f1;
printf( "Write succeeded.\n" );
f1( );
printf( "Call succeeded.\n" );
return( 0 );
}
You compile this and launch once. It will fail, but you will know page size. Let's say, it is 4096. Then you compile this example like this:
gcc a1.c -falign-functions=4096
And it should work.
Output:
pagesize=4096 (0x00001000).
p=0x00402000.
p=0x00402000.
rc=0.
'mprotect()' succeeded.
Write succeeded.
Call succeeded.
Now the advanced example:
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
//extern void f1( void ) __attribute__(( aligned( 4096 ) ));
__asm__( ".text" );
__asm__( ".align 4096" );
void f1( void )
{
printf( "%d\n", 123 );
}
void f2( void )
{
printf( "%d\n", 124 );
}
int main( void )
{
int rc;
int pagesize;
char *p;
int i;
printf( "f1=0x%08X.\n", f1 );
printf( "f2=0x%08X.\n", f2 );
f1( );
f2( );
pagesize = sysconf( _SC_PAGE_SIZE );
printf( "pagesize=%d (0x%08X).\n", pagesize, pagesize );
if( pagesize == -1 )
return( 2 );
p = (char*) f1;
printf( "p=0x%08X.\n", p );
p = (char*) ((size_t) p & ~(pagesize - 1));
printf( "p=0x%08X.\n", p );
rc = mprotect( p, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC );
printf( "rc=%d.\n", rc );
if( rc != 0 )
return( 2 );
printf( "'mprotect()' succeeded.\n" );
for( i = 0; i < (size_t) f2 - (size_t) f1; i++ ) {
if( ((char*) f2)[ i ] == 124 ) {
printf( "i=%d.\n", i );
((char*) f1)[ i ] = ((char*) f2)[ i ];
}
}
//memcpy( f1, f2, (size_t) f2 - (size_t) f1 );
printf( "Write succeeded.\n" );
f1( );
f2( );
printf( "Call succeeded.\n" );
return( 0 );
}
You can't use "memcpy()" here (it is commented) because calls to "printf()" inside "f1()" and "f2()" are relative, not absolute. And I could not find how to make them absolute ("neither -fPIC", nor "-fno-PIC" worked in my case). If you don't have relative function calls in "f1()" and "f2()", I believe you can use "memcpy()" (but I did not try).
You should also use alignment of "f1()" to page size (unless you are sure you have enough code before "f1()" starts). If you have gcc 4.3 and higher, you can use attribute (it is commented because I have gcc v4.1.2). If not, you can use that ugly and not reliable "_asm_".
Output:
f1=0x00402000.
f2=0x0040201E.
123
124
pagesize=4096 (0x00001000).
p=0x00402000.
p=0x00402000.
rc=0.
'mprotect()' succeeded.
i=12.
Write succeeded.
124
124
Call succeeded.
And, of course, that horrible "if( ((char*) f2)[ i ] == 124 )". It serves to distinguish between what should be replaced (the printed number) and what should not (relative references). Clearly, this is very simplified algorithm. You will have to implement your own, suitable for your task.