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.
It is impossible to use the "main" program to recompile the script.o file (especially since this is supposed to be in use)
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)
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.
Related
I want to specify during run-time to ignore a function call for a function (which is of course defined) inside my executable. Please suggest some methodology for doing the same in C language on Linux.
Probably the best you can do is something like this:
// Filename mycode.c
int main()
{
// ...
#ifndef SOME_MACRO
someFUnction();
#endif
//...
}
int someFUnction()
{
// does something
}
To exclude the function call in main, you need to compile with
gcc -DSOME_MACRO mycode.c
If you will compile simply as
gcc mycode.c
then the function call will be enabled.
You cannot ignore function calls at runtime, you either call the function or you don't.
But let's assume for the sake of this answer that there exists a condition under which the function gets called and at least another condition under which the function is not called.
You can tell the program these conditions in several ways, for example per command-line, change of environment/file, and probably a long list more. For simplicity let's use the command-line and give the conditions in form of a argument to the executable. Additionally, because it is simple and short, use a signal.
File optional_functions.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
static void a(void)
{
puts("Function \"a\" called");
}
static void b(void)
{
puts("Function \"b\" called");
}
static void c(void)
{
puts("Function \"c\" called (by signal SIGINT)");
}
#include<signal.h>
#include<unistd.h>
static void signal_handler(int signal_number)
{
if (signal_number == SIGINT) {
c();
exit(EXIT_SUCCESS);
}
}
int main(int argc, char **argv)
{
void *dynlib;
void (*function_d) (void);
char *dynlib_error;
if (argc == 2) {
if (argv[1][0] == 'a') {
a();
} else if (argv[1][0] == 'b') {
b();
} else if (argv[1][0] == 'd') {
puts("External function \"d\" wanted, loading library");
dynlib = dlopen("libfunctiond.so", RTLD_LAZY);
if (dynlib == NULL) {
fprintf(stderr, "Failed loading lib: %s\n", dlerror());
exit(EXIT_FAILURE);
}
*(void **) (&function_d) = dlsym(dynlib, "d");
dynlib_error = dlerror();
if (dynlib_error != NULL) {
fprintf(stderr, "Failed calling function \"d\" fom lib: %s\n",
dynlib_error);
exit(EXIT_FAILURE);
}
(*function_d) ();
} else {
fprintf(stderr, "A function named \"%c\" does not exist, bailing out\n",
argv[1][0]);
exit(EXIT_FAILURE);
}
} else {
if (signal(SIGINT, signal_handler) == SIG_ERR) {
fprintf(stderr, "signal catching failed, bailing out\n");
exit(EXIT_FAILURE);
}
sleep(5);
puts("Signal catching timed out, assuming no function wanted in the first place.");
}
exit(EXIT_SUCCESS);
}
File functiond.h
#ifndef FUNCTIOND_H
#define FUNCTIOND_H
void d(void);
#endif
File functiond.c
#include <stdio.h>
#include "functiond.h"
void d(void)
{
puts("Function \"d\" called and says hello from the library");
}
Compile as
clang -Weverything -fPIC -c functiond.c
clang -shared -Wl,-soname,libfunctiond.so.1 -o libfunctiond.so.1.0 functiond.o
ln -sf libfunctiond.so.1.0 libfunctiond.so.1
ln -sf libfunctiond.so.1 libfunctiond.so
clang -Weverything -o optional_functions optional_functions.c example.c -ldl
Run it
$ ./optional_functions # waiting 5 seconds
Signal catching timed out, assuming no function wanted in the first place.
$ ./optional_functions # press CTRL+c in less than 5 seonds
^CFunction "c" called (by signal SIGINT)
$ ./optional_functions 1
A function named "1" does not exist, bailing out.
$ ./optional_functions a
Function "a" called
$ ./optional_functions b
Function "b" called
$ ./optional_functions d
External function "d" wanted, loading library
Failed loading lib: libfunctiond.so: cannot open shared object file: No such file or directory
That was expected. Either give dlopen() the complete path to the library or let the environment variable LD_LIBRARY_PATH do the job:
$ LD_LIBRARY_PATH=. ./optional_functions d
External function "d" wanted, loading library
Function "d" called and says hello from the library
It is not the proper way to make, install and and use dynamic libraries, of course, but again: for the sake of simplicity…
I'm writing an application using libao for audio output. The portion
of my program that calls into libao lives in a shared object:
// playao.c
// compile with: gcc -shared -o libplayao.so playao.c -lao -lm
#include <ao/ao.h>
#include <stdio.h>
#include <math.h>
void playao(void) {
int i;
unsigned char samps[8000];
ao_initialize();
ao_sample_format sf;
sf.bits = 8;
sf.rate = 8000;
sf.channels = 1;
sf.byte_format = AO_FMT_NATIVE;
sf.matrix = "M";
ao_device *device = ao_open_live(ao_default_driver_id(), &sf, NULL);
if(!device) {
puts("ao_open_live error");
ao_shutdown();
return;
}
for(i = 0; i < 8000; ++i) {
float time = (float)i / 8000;
float freq = 440;
float angle = time * freq * M_PI * 2;
float value = sinf(angle);
samps[i] = (unsigned char)(value * 127 + 127);
}
if(!ao_play(device, (char *)samps, 8000)) {
puts("ao_play error");
}
ao_close(device);
ao_shutdown();
}
If I link against this shared object in a program, it works fine:
// directlink.c
// compile with: gcc -o directlink directlink.c libplayao.so -Wl,-rpath,'$ORIGIN'
void playao(void);
int main(int argc, char **argv) {
playao();
return 0;
}
However, if I use dlopen/dlsym to invoke it, there are no errors, but the
program does not cause any sound to be emitted:
// usedl.c
// compile with: gcc -o usedl usedl.c -ldl
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char **argv) {
void *handle = dlopen("./libplayao.so", RTLD_LAZY);
if(!handle) {
puts("dlopen failed");
return 1;
}
void *playao = dlsym(handle, "playao");
if(!playao) {
puts("dlsym failed");
dlclose(handle);
return 1;
}
((void (*)(void))playao)();
dlclose(handle);
return 0;
}
However, running usedl with LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libao.so.4
does work. So there's something about libao that wants to be loaded when the
program starts up, and doesn't like being loaded any later.
Why is this? Is there any way to work around this, so that libao works
correctly even if loaded later in the program's execution?
I'm running Debian 10 "buster" if it matters.
I asked about this on the #xiph channel on Freenode and xiphmont suggested turning turning on verbose mode. Once I did that, the failing case started getting the message:
ERROR: Failed to load plugin /usr/lib/x86_64-linux-gnu/ao/plugins-4/libalsa.so => dlopen() failed
So libao itself is trying to dlopen something, and it's failing. It's not showing me any more details, so I ran the program under GDB and set a breakpoint on dlopen. After hitting the dlopen breakpoint for libalsa and running finish, I tried finding what the error was by using print (const char *)dlerror(). And with this, I get a more detailed error:
/usr/lib/x86_64-linux-gnu/ao/plugins-4/libalsa.so: undefined symbol: ao_is_big_endian
So ao's libalsa plugin is trying to reference symbols back in libao, but it's not finding them. Why could this be? Referencing the dlopen documentation, I see:
Zero or more of the following values may also be ORed in flags:
RTLD_GLOBAL: The symbols defined by this shared object will be made available for symbol resolution of subsequently loaded shared objects.
RTLD_LOCAL: This is the converse of RTLD_GLOBAL, and the default if neither flag is specified. Symbols defined in this shared object are not made available to resolve references in subsequently loaded shared objects.
Because my dlopen call only used RTLD_LAZY and didn't include RTLD_GLOBAL or RTLD_LOCAL, it defaulted to RTLD_LOCAL, which does not expose the symbols in the shared object (like ao_is_big_endian) to subsequently loaded shared objects (like libalsa.so).
So, I tried changing the code from:
void *handle = dlopen("./libplayao.so", RTLD_LAZY);
To:
void *handle = dlopen("./libplayao.so", RTLD_LAZY | RTLD_GLOBAL);
And lo and behold, it works!
I am trying to use two .c files together. I am lost at how to do this, I have a simple setup for each file but I get a undefined reference to format_lines error when I try to compile. Any help would be muchly appreciated;
formatter.h
#ifndef _FORMATTER_H_
#define _FORMATTER_H_
#include <stdio.h>
char **format_file(FILE *);
char **format_lines(char **, int);
void test();
#endif
formatter.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "formatter.h"
char **format_file(FILE *infile) {
return NULL;
}
char **format_lines(char **lines, int num_lines) {
char **result = NULL;
#ifdef DEBUG
result = (char **)malloc(sizeof(char *) * 2);
if (result == NULL) {
return NULL;
}
result[0] = (char *)malloc(sizeof(char) * 80);
if (result[0] == NULL) {
return NULL;
}
strncpy(result[0], "(machine-like voice) EXTERMINATE THEM!", 79);
result[1] = (char *)malloc(sizeof(char) * 2);
if (result[1] == NULL) {
return NULL;
}
result[1][0] = '\0';
#endif
}
void test(){
print("here");
}
and sengfmt.c
#include <stdio.h>
#include <stdlib.h>
#include "formatter.h"
int main(int argc, char *argv[]) {
test();
#ifdef DEBUG
printf("%s does nothing right now.\n", argv[0]);
#endif
exit(0);
}
When I try to compile, I just type this.
$ gcc sengfmt3.c
/tmp/cc7Ttgne.o: In function `main':
sengfmt3.c:(.text+0x15): undefined reference to `test'
collect2: ld returned 1 exit status
I suspect that your main used to try to call format_lines
You need to do this
gcc formatter.c sendgfmt.c -o myprog
You must list all the c files that you want compiled together
If you have code in multiple source files, then you need to build with all the source files.
There are two ways of doing this:
Compile and link all source files using using one command:
$ gcc sengfmt3.c someOtherSourceFile.c someThirdSourceFile.c
First make object files of all source files, and then link the object files together. This is more work, but if you have a makefile or other build-system it will be better since only the modified source files will be recompiled, and might save you some build-time:
$ gcc -c sengfmt3.c
$ gcc -c someOtherSourceFile.c
$ gcc -c someThirdSourceFile.c
$ gcc sengfmt.o someOtherSourceFile.o someThirdSorceFile.o
Note the command-line option -c for the compilation, this tells GCC to generate object files. Also note that for the linking command (the last one) the file extensions have changed from .c to .o.
The command in point 1 does this internally, using temporary files which are removed when done.
I was able to generate the callgraph of one file using gnu - cflow, but I was not able to find out how to generate the call graph for multiple files using cflow.
I tried following
cflow test.c,hello.c
It generates the callgraph for test.c and not creating it for hello.c
cflow test.c hello.c
It generates the callgraph for hello.c and not creating it for test.c
I don't know how to pass multiple files to cflow.
Any idea about this?
hello.c
int
who_am_i (void)
{
struct passwd *pw;
char *user = NULL;
pw = getpwuid (geteuid ());
if (pw)
user = pw->pw_name;
else if ((user = getenv ("USER")) == NULL)
{
fprintf (stderr, "I don't know!\n");
return 1;
}
printf ("%s\n", user);
unused_function();
return 0;
}
int
main (int argc, char **argv)
{
if (argc > 1)
{
fprintf (stderr, "usage: whoami\n");
return 1;
}
return who_am_i ();
}
void unused_function()
{
printf();
error1();
printf();
}
void error1()
{
error2();
}
void error2()
{
}
test.c
int tests()
{ return 0;}
cflow test.c hello.c
Actually above statement is correct and tests() does not show up in callgraph, because it is never called.
answer given by #AndreasGrapentin
Another convenient command is:
cflow *.c
Note:
This command will ignore C source files in all sub-directories.
Reference:
GNU cflow manual: Chapter 6-Controlling Symbol Types
For cflow to be able to process such declarations, declare __P as a wrapper, for example:
cflow --symbol __P:wrapper *.c
When loaded a shared library is opened via the function dlopen(), is there a way for it to call functions in main program?
Code of dlo.c (the lib):
#include <stdio.h>
// function is defined in main program
void callb(void);
void test(void) {
printf("here, in lib\n");
callb();
}
Compile with
gcc -shared -olibdlo.so dlo.c
Here the code of the main program (copied from dlopen manpage, and adjusted):
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void callb(void) {
printf("here, i'm back\n");
}
int
main(int argc, char **argv)
{
void *handle;
void (*test)(void);
char *error;
handle = dlopen("libdlo.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Clear any existing error */
*(void **) (&test) = dlsym(handle, "test");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
(*test)();
dlclose(handle);
exit(EXIT_SUCCESS);
}
Build with
gcc -ldl -rdynamic main.c
Output:
[js#HOST2 dlopen]$ LD_LIBRARY_PATH=. ./a.out
here, in lib
here, i'm back
[js#HOST2 dlopen]$
The -rdynamic option puts all symbols in the dynamic symbol table (which is mapped into memory), not only the names of the used symbols. Read further about it here. Of course you can also provide function pointers (or a struct of function pointers) that define the interface between the library and your main program. It's actually the method what i would choose probably. I heard from other people that it's not so easy to do -rdynamic in windows, and it also would make for a cleaner communication between library and main program (you've got precise control on what can be called and not), but it also requires more house-keeping.
Yes, If you provide your library a pointer to that function, I'm sure the library will be able to run/execute the function in the main program.
Here is an example, haven't compiled it so beware ;)
/* in main app */
/* define your function */
int do_it( char arg1, char arg2);
int do_it( char arg1, char arg2){
/* do it! */
return 1;
}
/* some where else in main app (init maybe?) provide the pointer */
LIB_set_do_it(&do_it);
/** END MAIN CODE ***/
/* in LIBRARY */
int (*LIB_do_it_ptr)(char, char) = NULL;
void LIB_set_do_it( int (*do_it_ptr)(char, char) ){
LIB_do_it_ptr = do_it_ptr;
}
int LIB_do_it(){
char arg1, arg2;
/* do something to the args
...
... */
return LIB_do_it_ptr( arg1, arg2);
}
The dlopen() function, as discussed by #litb, is primarily provided on systems using ELF format object files. It is rather powerful and will let you control whether symbols referenced by the loaded library can be satisfied from the main program, and generally does let them be satisfied. Not all shared library loading systems are as flexible - be aware if it comes to porting your code.
The callback mechanism outlined by #hhafez works now that the kinks in that code are straightened out.