cmemorydynamic-library

Call function from dynamic library with changing signature?


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).


Solution

  • 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");
    }