changing extern function pointer to extern pointer using preprocessor

Viewed 197

I am using library that I shouldn't change it files, that including my h file.

the code of the library looks somthing like like:

#include "my_file"
extern void (*some_func)();
void foo()
{
    (some_func)();
}

my problem is that I want that some_func will be extern function and not extern pointer to function (I am implementing and linking some_func). and that how main will call it.

that way I will save little run time and code space, and no one in mistake will change this global.

is it possible?

I thought about adding in my_file.h somthing as #define *some_func some_func but it won't compile because asterisk is not allowed in #define.

EDIT

The file is not compiled already, so changes at my_file.h will effect the compilation.

2 Answers

First of all, you say that you can't change the source of the library. Well, this is bad, and some "betrayal" is necessary.

My approach is to let the declaration of the pointer some_func as is, a non-constant writable variable, but to implement it as constant non-writable variable, which will be initialized once for all with the wanted address.

Here comes the minimal, reproducible example.

The library is implemented as you show us:

// lib.c

#include "my_file"

extern void (*some_func)();

void foo()
{
    (some_func)();
}

Since you have this include file in the library's source, I provide one. But it is empty.

// my_file

I use a header file that declares the public API of the library. This file still has the writable declaration of the pointer, so that offenders believe they can change it.

// lib.h

extern void (*some_func)();

void foo();

I separated an offending module to try the impossible. It has a header file and an implementation file. In the source the erroneous assignment is marked, already revealing what will happen.

// offender.h

void offend(void);
// offender.c

#include <stdio.h>

#include "lib.h"
#include "offender.h"

static void other_func()
{
    puts("other_func");
}

void offend(void)
{
    some_func = other_func; // the assignment gives a run-time error
}

The test program consists of this little source. To avoid compiler errors, the declaration has to be attributed as const. Here, where we are including the declarating header file, we can use some preprocessor magic.

// main.c

#include <stdio.h>

#define some_func const some_func
#include "lib.h"
#undef some_func

#include "offender.h"

static void my_func()
{
    puts("my_func");
}

void (* const some_func)() = my_func;

int main(void)
{
    foo();

    offend();
    foo();

    return 0;
}

The trick is, that the compiler places the pointer variable in the read-only section of the executable. The const attribute is just used by the compiler and is not stored in the intermediate object files, and the linker happily resolves all references. Any write access to the variable will generate a runtime error.

Now all of this is compiled in an executable, I used GCC on Windows. I did not bother to create a separated library, because it doesn't make a difference for the effect.

gcc -Wall -Wextra -g main.c offender.c lib.c -o test.exe

If I run the executable in "cmd", it just prints "my_func". Apparently the second call of foo() is never executed. The ERRORLEVEL is -1073741819, which is 0xC0000005. Looking up this code gives the meaning "STATUS_ACCESS_VIOLATION", on other systems known as "segmentation fault".

Because I deliberately compiled with the debugging flag -g, I can use the debugger to examine more deeply.

d:\tmp\StackOverflow\103> gdb -q test.exe
Reading symbols from test.exe...done.
(gdb) r
Starting program: d:\tmp\StackOverflow\103\test.exe
[New Thread 12696.0x1f00]
[New Thread 12696.0x15d8]
my_func

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00000000004015c9 in offend () at offender.c:16
16          some_func = other_func;

Alright, as I intended, the assignment is blocked. However, the reaction of the system is quite harsh.

Unfortunately we cannot get a compile-time or link-time error. This is because of the design of the library, which is fixed, as you say.

You could look at the ifunc attribute if you are using GCC or related. It should patch a small trampoline at load time. So when calling the function, the trampoline is called with a known static address and then inside the trampoline there is a jump instruction that was patched with the real address. So when running, all jump locations are directly in the code, which should be efficient with the instruction cache. Note that it might even be more efficient than this, but at most as bad as calling the function pointer. Here is how you would implement it:

extern void (*some_func)(void); // defined in the header you do not have control about

void some_func_resolved(void) __attribute__((ifunc("resolve_some_func")));

static void (*resolve_some_func(void)) (void)
{
    return some_func;
}

// call some_func_resolved instead now
Related