On MacOS Ventura, obtaining a handle to the dynamic loader using dlopen(NULL, 0) returns a handle containing the entire executable's symbol table. Using this handle, one can obtain pointers to symbol data, access and modify their contents, and these changes will permeate across the program. However, attempting this with functions pointers does not work the same way.
For example, the following code:
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int my_var = 0;
int main() {
void *handle = dlopen(NULL, 0);
int *a = dlsym(handle, "my_var");
*a = 5;
printf("%d", my_var);
return 0;
}
will print 5 instead of 0. However, when attempting something similiar with function pointers:
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
typedef void (*ftype)(void);
void fun1() {
printf("a");
}
void fun2() {
printf("b");
}
int main() {
void *handle = dlopen(NULL, 0);
ftype f1ptr;
*(void **)(&f1ptr) = dlsym(handle, "fun1");
f1ptr = fun2;
fun1();
return 0;
}
will print a instead of b. Is there anyway to perform an operation similar to the first code segment with function pointers? Essentially, I would like fun1 to now point to the code that fun2 points to. Swapping f1ptr's type to "ftype *" and then performing "*f1ptr = fun2" causes a bus fault.
An identifier names an object or a function. The identifier for a function is not a function pointer, and your example does not show the code working differently for objects or functions.
In
int *a = dlsym(handle, "my_var");, your code obtains a pointer tomy_var. Then it uses*a = 5;to change the value ofmy_var.In
*(void **)(&f1ptr) = dlsym(handle, "fun1");, your code obtains a pointer tofun1(although it mishandles it, discussed below). Sof1ptris a pointer tofun1. It is a pointer to the function, not a pointer to a function pointer. If functions were somehow modifiable, then*f1ptr = …;would modify the function.However, you do not use
*f1ptr = …;. You usef1ptr = fun2;. This simply changesf1ptr. It does not change whatf1ptrpoints to. It does not changefun1.fun1is an identifier for the function. It is not a pointer. So there is no pointer to change. Nothing can be done in the program to makefun1be a different function or point to a different function.Regarding why
*(void **)(&f1ptr) = dlsym(handle, "fun1");is wrong, this says to take the address off1ptr, convert the address to avoid **, and to use that memory location as if it were avoid *. Then that memory location is assigned the value of thevoid *returned bydlsym. In other words, you are accessing the pointer-to-function objectf1ptrusing the type pointer tovoid. That violates C 2018 6.5 7, which says that an object shall be accessed only with its defined type or certain other types, none of which allowing accessing a pointer to a function withvoid *.The C standard allows a pointer to a function and a pointer to
voidto have different representations in memory and even different sizes, in which case this assignment would fail “mechanically”; the bytes written intof1ptrwould not be suitable for use as a pointer-to-function. But even in C implementations where all pointers have the same representation, this assignment can fall afoul of optimization by the compiler. It should never be used.The proper way to assign the result of
dlsymtof1ptris to convert the result to the necessary type:(This still will not let you change
fun1; it just shows you how to usedlsymcorrectly.)