Say I have hello1.c
char *greeting = "Hello, Version 1";
char *greet(void) {
return greeting;
}
and hello2.c
int greeting = 42;
int greet(void) {
return greeting;
}
My host.c
looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#define LIBHELLO_SO "libhello.so"
void load_and_call_greet() {
// Open the shared library
void *dynamic_lib = dlopen(LIBHELLO_SO, RTLD_LAZY);
if (!dynamic_lib) {
fprintf(stderr, "%s\n", dlerror());
return;
}
// Define a function pointer for the function we want to call
char *(*greet)(void);
// Get the function from the shared library
greet = (char *(*)(void)) dlsym(dynamic_lib, "greet");
// Check for errors
char *error;
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
dlclose(dynamic_lib); // Close the library before exiting
return;
}
// Call the function and print the result
printf("%s\n", greet());
// Close the shared library
dlclose(dynamic_lib);
}
int main() {
while (1) {
load_and_call_greet();
sleep(5); // Wait for 5 seconds before reloading the library
}
return 0;
}
Then, I compile and run host file and without terminating host file I want to swap my dynamic library to the new signature (from hello1.c
to hello2.c
):
/ ./host
/ prints "Hello, Version 1" every 5 seconds
/ in another terminal re-compile libhello.so but use `hello2.c` as source file rather than `hello1.c`
/ expected result: ./host prints 42
/ actual result: './host terminated by signal SIGSEGV (Address boundary error)
Is it possible to change host.c
code to load function with changing signature from dynamic library?
I tried couple of approaches with ChatGPT but it gives SIGSEGV (Address boundary error)
.
This isn't possible, as calling a function through an invalid pointer triggers undefined behavior in your code.
The closest you can do is to keep a set signature and return a pointer to a known structure that contains a void *
and information about what it points to.
For example:
hello.h:
#define TYPE_INT 1
#define TYPE_CHARP 2
struct hello {
int type;
void *value;
};
hello1.c:
#include <stdlib.h>
#include <string.h>
#include "hello.h"
char *greeting = "Hello, Version 1";
struct hello *greet(void) {
struct hello *p = malloc(sizeof *p);
p->type = TYPE_CHARP;
p->value = strdup(greeting);
return greeting;
}
hello2.c:
#include <stdlib.h>
#include "hello.h"
int greeting = 42;
struct hello *greet(void) {
struct hello *p = malloc(sizeof *p);
p->type = TYPE_INT;
p->value = malloc(sizeof(int));
*p->value = greeting;
return greeting;
}
Then to call it:
// either define explicitly or include hello.h
#define TYPE_INT 1
#define TYPE_CHARP 2
struct hello {
int type;
void *value;
};
void load_and_call_greet() {
// either define explicitly or include hello.h
#define TYPE_INT 1
#define TYPE_CHARP 2
struct hello {
int type;
void *value;
};
// Open the shared library
void *dynamic_lib = dlopen(LIBHELLO_SO, RTLD_LAZY);
if (!dynamic_lib) {
fprintf(stderr, "%s\n", dlerror());
return;
}
// Define a function pointer for the function we want to call
struct hello *(*greet)(void);
// Get the function from the shared library
greet = (struct hello *(*)(void)) dlsym(dynamic_lib, "greet");
// Check for errors
char *error;
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
dlclose(dynamic_lib); // Close the library before exiting
return;
}
// Call the function and print the result
struct hello *result = greet();
if (result->type == TYPE_INT) {
print("%d\n", *((int *)result->value));
} else if (result->type == TYPE_CHARP) {
printf("%s\n", *((char *)result->value));
}
free(result->value);
free(result);
// Close the shared library
dlclose(dynamic_lib);
char *(*greet)(void);
// Get the function from the shared library
greet = (char *(*)(void)) dlsym(dynamic_lib, "greet");
}