0

I'm trying to find a method to, from a program, check the presence of one or more ".c" files and load one or more functions of it.

Basically, I would have a "main" program which will check if the "script.c" file exists, and will launch (if it exists) the main() function of this one.

Here is the content of my "main" program before compilation:

int main(int argc, char *argv[]){
    ...
    if(argc == 1){
        FILE *file;
        if((file = fopen("script.c", "r"))){
            printf("file script.c loaded.");
            // to compile the script.c file and modify on it the "main()" function to "_main()" (in order to be able to recompile it with the main library) before restarting the program
            int ret = system("gcc -c script.c && objcopy --redefine-sym main=_main script.o && gcc main script.o -o main && ./main -l script.o");
            printf("ret(0)=%d\n", ret);
        }
        else{
            int ret = system("./main -l");
            printf("ret(1)=%d\n", ret);
        }
    }
    else{
        if(argc == 3 && strcmp(argv[2], "script.o") == 0){
            _main(argc, argv);
        }
        else{
            printf("no file found.\n");
        }
    }
    ...
}

He is the content of my "script.c" file:

int main(int argc, char *argv[]){
    ...
    printf("main() function in script.c loaded.\n");
    ...
}

If the script.c file exists, running main should give:

file script.c loaded.
ret(0)=0
main() function in script.c loaded.

If the script.c file does not exist, running main should give:

file script.c loaded.
ret(1)=0
no file found.

Obviously, this does not work for several reasons.

  1. It is impossible to use the "main" program to recompile the script.o file (especially since this is supposed to be in use)
  2. It is impossible to compile my "main" program with a _main() function that does not exist (on the 1st launch, and potentially on the second too if script.c dont found)

Do you have an idea for me to achieve my goal ? So from a single executable program (here "main") to be able to check the presence of an external file (here "script.c") and launch one or more functions from it... PS: Having already seen it in other projects, I know it's possible, but I can't find the solution. PS2: Only the executable file (main) and potentially the script.c file must be present (therefore no main.c...which therefore perhaps suggests that a "main" file should be merged with the "main.o" associated which would be unpacked and executed)

Kryk
  • 11
  • 1
  • 1
    Well, missed it at first, but the OP seem to be aware, the program needs to be compiled and has some code calling `gcc` to do so. – Eugene Sh. Apr 11 '22 at 20:36
  • 2
    Wouldn't it be easier to compile it into a shared library, load that and execute a `start()` function (or something else other than `main`) in it? You wouldn't even need to restart your "main" program. – Ted Lyngmo Apr 11 '22 at 20:38
  • 1
    Many systems won't allow you to overwrite an executable that's currently being run. So you can't replace `main` while `main` is running. You should rename the current `main` to `main.old` so you can create a new one. – Barmar Apr 11 '22 at 20:39
  • 2
    You can load a library dynamically such as in [`dlopen`](https://man7.org/linux/man-pages/man3/dlopen.3.html) and then call functions from it. If you really want, you can compile this library from your `main` (but I am not sure why would you want it). – Eugene Sh. Apr 11 '22 at 20:41
  • Are you trying to make `main`-function from `script.c` appear in the current running process? You cannot do that by rewriting the executable file on disk that belongs to the current process, at least not on any sane OS that I am aware of. – HAL9000 Apr 11 '22 at 20:58
  • Kryk: I noticed you left a comment to my answer but I pressed refresh and then your comment was gone and I didn't catch what you said. If you had a question, please don't be afraid to ask for clarification. I'll gladly try to improve the answer if I can. – Ted Lyngmo Apr 12 '22 at 21:49

1 Answers1

3

So from a single executable program (here "main") to be able to check the presence of an external file (here "script.c") and launch one or more functions from it...

I'm going to make an assumption that the main function you mentioned is not the most important and show how you can, from within your program, compile collections of functions into shared libraries that you load and then execute functions (other than main) in.

A simple driver could look like this:

// driver.c
#include <dlfcn.h> // to be able to dynamically load shared libraries

#include <stdio.h>
#include <stdlib.h>

// the signature of the `start` function you decide to have in all the files:
typedef void(*start_func_t)(void);

int main(int argc, char *argv[]){
    char sys[1024];

    for(int i = 1; i < argc; ++i) { // loop through all the arguments
        // try to compile the argument into an object file
        // (use a safer version than sprintf in real code)
        sprintf(sys, "gcc -fPIC -c -o %s.o %s", argv[i], argv[i]);
        if(system(sys)) {
            printf("%d failed\n", sys);
            exit(1);
        }

        // try to create a shared library from the object file
        sprintf(sys, "gcc -shared -o libcurrent.so %s.o", argv[i]);
        if(system(sys)) {
            printf("%d failed\n", sys);
            exit(1);
        }

        // load the shared library you just created
        (void)dlerror(); 
        void *handle = dlopen("./libcurrent.so", RTLD_NOW | RTLD_LOCAL);
        if(!handle) {
            puts(dlerror());
            exit(1);
        }

        // lookup the "start" symbol in the shared library:
        start_func_t start = dlsym(handle, "start");
        if(!start) {
            puts(dlerror());
            exit(1);
        }

        // call the loaded function:
        start();

        dlclose(handle); // close the library
    }
}

You need to link the above program with the dl library, so something like this should work:

gcc -o driver driver.c -ldl

Now, if you create some example files:

// t1.c
#include <stdio.h>
void start(void) { puts("Hello world"); }
// t2.c
#include <stdio.h>
void start(void) { puts("another file"); }

and then run:

./driver t1.c t2.c

It should produce this output if everything works out:

Hello world
another file

I also made a test to see how this works out if I put main in the library. That is, change the start_func_t signature to:

typedef int(*start_func_t)(int argc, char *argv[]);

and load and call main instead:

        start_func_t start = dlsym(handle, "main");
        if(!start) {
            puts(dlerror());
            exit(1);
        }

        // call the loaded function with some example arguments:
        char *cargv[] = {
            "foo", "hello", "world", NULL
        };
        start(3, cargv);

and change the test programs slightly:

// t1.c
#include <stdio.h>
int main(int argc, char *argv[]) {
    for(int i = 0; i < argc; ++i) {
        printf("t1: %s\n", argv[i]);
    }
}
// t2.c
#include <stdio.h>
int main(int argc, char *argv[]) {
    for(int i = 0; i < argc; ++i) {
        printf("t2: %s\n", argv[i]);
    }
}

and this worked fine too. However, main is a bit special and I'm not sure if this violates any rules.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108