Portable way to implement static function registry in C - c

It is possible to implement a static function registry in C for gcc/clang by using the section variable attribute and relying on the ELF linker to define the __start_<section> and __stop_<section> symbols pointing to the address of the custom section.
(See below for an example using this approach, which should illustrate the point.)
This approach, however, is very specific to GCC, ELF and Unix/Linux.
Is there an alternative approach to solve this same problem of static function registry, only in a more portable way?
In particular, I would like to be able to target MSVC compiler for Windows.
As an example, consider this program using this set of source files:
1) registry.h
struct reg_func {
const char *name;
int (*func)(void);
};
#define REGISTER_FUNC(name) \
static int func_ ## name(void); \
static struct reg_func descr_ ## name \
__attribute__((section("registry"))) \
= { # name, func_ ## name }; \
static int func_ ## name(void)
extern struct reg_func __start_registry;
extern struct reg_func __stop_registry;
2) a.c
#include "registry.h"
REGISTER_FUNC(a) {
return 1;
}
3) b.c
#include "registry.h"
REGISTER_FUNC(b) {
return 4;
}
4) c.c
#include "registry.h"
REGISTER_FUNC(cde) {
return 999;
}
5) main.c
#include <stdio.h>
#include "registry.h"
int main(int argc, char *argv[]) {
struct reg_func *p;
for (p = &__start_registry; p < &__stop_registry; p++) {
printf("Function %s returned %d.\n", p->name, p->func());
}
return 0;
}
6) Makefile
registry: main.o a.o b.o c.o
$(CC) -o $# $^
Build with:
$ make
cc -c -o main.o main.c
cc -c -o a.o a.c
cc -c -o b.o b.c
cc -c -o c.o c.c
cc -o registry main.o a.o b.o c.o
Execute with:
$ ./registry
Function a returned 1.
Function b returned 4.
Function cde returned 999.

Related

--localize-symbol does not work on windows with mingw-w64

I have two files
foo_var.c
int global_var = 1;
foo_print.c
#include <stdio.h>
extern int global_var;
void print_foo(void)
{
printf("print_foo = %d\n", global_var);
}
that compile and link into foo.o and localize global_var
gcc -c foo.c foo_print.c
ld -r -o foo.o foo_print.o foo_var.o
objcopy -L global_var foo.o
And two files
bar_var.c
int global_var = 2;
bar_print.c
#include <stdio.h>
extern int global_var;
void print_bar(void)
{
printf("print_bar = %d\n", global_var);
}
compile and link into bar.o and localize global_var too
gcc -c bar.c bar_print.c
ld -r -o bar.o bar_print.o bar_var.o
objcopy -L global_var bar.o
also I have main, where I call print_foo and print_bar
main.c
void print_foo(void);
void print_bar(void);
int main(int argc, char const *argv[])
{
print_foo();
print_bar();
return 0;
}
When I compile and link all together
gcc -c main.c
gcc main.o foo.o bar.o
and run a.exe I get this
print_foo = 1
print_bar = 1
instead
print_foo = 1
print_bar = 2
How localize symbols in mingw-w64 correctly?
gcc version 12.2.0 (x86_64-win32-seh-rev2, Built by MinGW-W64 project)

Linking extern variables using multiple library in C

In my project, I have two libraries and one program.
Lib1.c and Lib1.h are two files of first library(Lib1.so).
Lib2.c and Lib2.h are two files of second library(Lib2.so).
prog.c is the main file of program(prog).
The program(prog) is linked only to the second library(Lib2.so) and the second library(Lib2.so) is linked to the first library(Lib1.so).
In Lib1.c, I have a declaration of global variable (int var = 0;) and in Lib1.h, I have a declaration (extern int var;).
In Lib2.h, I have a declaration (extern int var;) in order to use var variable in main program.
In main() function, I include the Lib2.h in prog.c file and I have a declaration (var = 5;)
Lib1.c :
#include <stdio.h>
#include "Lib1.h"
int var = 0;
int funct(void)
{
printf("hello world \n");
return 0;
}
Lib1.h :
extern int var;
int funct(void);
Lib2.c :
#include <stdio.h>
#include "Lib2.h"
int funct2(void)
{
printf("Library 2 \n");
funct();
return 0;
}
Lib2.h :
#include "Lib1.h"
extern int var;
int funct2(void);
prog.c :
#include <stdio.h>
#include "Lib2.h"
int main()
{
var = 5;
printf("===>var=%d\n", var);
funct2();
return 1;
}
Commands :
gcc -c -Wall -Werror -fpic Lib1.c
gcc -shared -o Lib1.so Lib1.o
gcc -c -Wall -Werror -fpic Lib2.c
gcc -shared -o Lib2.so Lib2.o -ldl /home/test/Lib1.so
gcc prog.c -o prog -ldl /home/test/Lib2.so
When I try to compile the program(prog.c), I get an error in the link step as below.
/usr/bin/ld: /tmp/ccKaq16a.o: undefined reference to symbol 'var'
/home/test/Lib1.so: error adding symbols: DSO missing from command line
Is there a way to use var variable in the main function when its defined in the first library?
You link your program against Lib2 but not Lib1. You need to add that as well. You also don't need to explicitly link Lib1 when you create Lib2
gcc -c -Wall -Werror -fpic Lib1.c
gcc -shared -o Lib1.so Lib1.o
gcc -c -Wall -Werror -fpic Lib2.c
gcc -shared -o Lib2.so Lib2.o
gcc prog.c -o prog /home/test/Lib2.so /home/test/Lib1.so

Hierarchical Linking in C

I want to link three files but in hierarchical way.
// a.c
int fun1(){...}
int fun2(){...}
// b.c
extern int parameter;
int fun3(){...//using parameter here}
// main.c
int parameter = 1;
int main(){...// use fun1 fun2 fun3}
So, I first compile three files separately into object file a.o, b.o and main.o. And then I want to combine a.o and b.o into another object file tools.o. And eventually use tools.o and main.o to generate executable file.
But, when I try to combine a.o and b.o like ld -o tools.o a.o b.o, the linker says undefined reference to 'parameter'. How could I link those object files into an intermediate object file?
You want the -r option to produce a relocatable object file (think 'reusable'):
ld -o tools.o -r a.o b.o
Working code
abmain.h
extern void fun1(void);
extern void fun2(void);
extern void fun3(void);
extern int parameter;
a.c
#include <stdio.h>
#include "abmain.h"
void fun1(void){printf("%s\n", __func__);}
void fun2(void){printf("%s\n", __func__);}
b.c
#include <stdio.h>
#include "abmain.h"
void fun3(void){printf("%s (%d)\n", __func__, ++parameter);}
main.c
#include <stdio.h>
#include "abmain.h"
int parameter = 1;
int main(void){fun1();fun3();fun2();fun3();return 0;}
Compilation and execution
$ gcc -Wall -Wextra -c a.c
$ gcc -Wall -Wextra -c b.c
$ gcc -Wall -Wextra -c main.c
$ ld -r -o tools.o a.o b.o
$ gcc -o abmain main.o tools.o
$ ./abmain
fun1
fun3 (2)
fun2
fun3 (3)
$
Proved on Mac OS X 10.11.6 with GCC 6.1.0 (and the XCode 7.3.0 loader, etc). However, the -r option has been in the ld command on mainstream Unix since at least the 7th Edition Unix (circa 1978), so it is likely to be available with most Unix-based compilation systems, even if it is one of the more widely unused options.

multiple definition in g++?

The code is as follows:
global.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>
int test;
void test_fun(void);
#endif
global.c
#include "global.h"
void test_fun()
{
printf("%d\n", test);
}
main.c
#include "global.h"
int main(void)
{
test_fun();
test = 1;
printf("%d\n", test);
}
Makefile using gcc compiler
main: main.o global.o
gcc -o main main.o global.o
main.o: main.c global.h
gcc -c main.c
global.o: global.c global.h
gcc -c global.c
clean:
rm -f global.o main.o main
This works well.
However, when I change my code to C++, as follows:
global.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include <iostream>
int test;
void test_fun(void);
#endif
global.cpp
#include "global.h"
void test_fun()
{
cout << test
}
main.cpp
#include "global.h"
int main(void)
{
test_fun();
test = 1;
std::cout << test;
}
Makefile using g++ compiler
main: main.o global.o
g++ -o main main.o global.o
main.o: main.cpp global.h
g++ main.cpp
global.o: global.cpp global.h
g++ global.cpp
clean:
rm -f global.o main.o main
The code above throws the output:
global.o:(.bss+0x0): multiple definition of `test'
What makes the different here?
You've int test; in a header which is included in 2 TUs, hence the error. Both the translation units main.c (or .cpp depending upon the compiler used) and global.c have global.h included, which leads to two definitions of the same variable in two object files, thus the linker error.
Pass test as an arguement to test_fun, thereby avoiding the usage of a global.
If you absolutely have to share the variable between the TUs, then remove int test; from global.h and in main.cpp do
int test;
and in global.cpp do
extern int test;
As an aside, since it's a global variable, test would be initialized to 0 and hence in main when you test_fun();, it should print 0 and then after setting it to 1, it'll print 1.
It's illegal in both C and C++ from a language standpoint, but as for why it works with a C compilers (like GCC) is because they implement a common extension, a legacy cruft.
... You are using a different programming language

Difference between getting function pointers from shared library

The question is how I can get function address from shared library (UNIX/LINUX)?
I had written some testcases in C (see below), compiled and run on Ubuntu 10.04 (amd64) and FreeBSD-8.2 (amd64). I hadn't feel any difference but I want to know more about possible troubles.
Here they are:
Test 1
lib.c
char* f0(void) {
return "Hello, World!";
}
main.c
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
void *hlib, *addr;
char* (*foo)(void);
char* s;
int main(int argc, char** argv) {
if ( !(hlib = dlopen("./lib.so", RTLD_LAZY)) )
return 1;
if ( !(addr = foo = dlsym(hlib, "f0")) )
return 2;
s = foo();
printf("%p => %s\n", addr, s);
return 0;
}
Now build it:
gcc -o lib.o -c lib.c -Wall -Werror -O3 -fPIC
gcc -o lib.so -shared -nostartfiles lib.o
gcc -o main.o -c main.c -Wall -Werror -O3
gcc -o prog main.o -ldl
This prints the address of library function f0() and the result of execution.
Test 2
lib.h (define here the standard interface of dynamically linking libraries)
#ifndef __LIB_H__
#define __LIB_H__
typedef struct __syminfo {
char* name; // function name
void* addr; // function address
} syminfo_t;
typedef struct __libinfo {
int num; // number of exported functions
syminfo_t sym[1]; // vector of exported function information
} libinfo_t;
extern int (*__getinfo)(libinfo_t**);
#endif
/* __LIB_H__
*/
lib.c (the library itself)
#include <stdlib.h>
#include <lib.h>
static libinfo_t* li;
char* foo(void);
__attribute__((constructor)) void __init() {
if ( (li = calloc(1, sizeof(libinfo_t))) ) {
li->num = 1;
li->sym[0].name = "foo";
li->sym[0].addr = &foo;
}
}
__attribute__((destructor)) void __free() {
if (li)
free(li);
}
int getinfo(libinfo_t** inf) {
if (!inf)
return -1;
*inf = li;
return 0;
}
char* foo(void) {
return "Hello, World!";
}
main.c
#include <stdio.h>
#include <dlfcn.h>
#include <lib.h>
libinfo_t* inf;
void* hlib;
int (*__getinfo)(libinfo_t**);
char* (*foo)(void);
char* s;
int main(int argc, char** argv) {
if ( !(hlib = dlopen("./lib.so", RTLD_LAZY)) )
return 1;
if ( !(__getinfo = dlsym(hlib, "getinfo")) )
return 2;
if (__getinfo(&inf))
return 3;
if ( !(foo = inf->sym[0].addr) )
return 4;
s = foo();
printf("%p => %s\n", inf->sym[0].addr, s);
return 0;
}
Now compile it (without -nostartfiles):
gcc -I. -o lib.o -c lib.c -Wall -Werror -O3 -fPIC
gcc -o lib.so lib.o -shared
gcc -I. -o main.o -c main.c -Wall -Werror -O3
gcc -o prog main.o -ldl
This printf the same as Test 1: the address of library function foo() and the result of its execution.
I tried to show how can I get shared library function address, but am I right in the second test? Shall I have got some troubles with it?
NOTE: in FreeBSD-8.2 there is no need to use -ldl argument, all dlfcn.h routines are in libc library.
Respectively waithing for any explanations.
That looks fairly standard to me. The only thing that you're using that could pose some problems is that you're using gcc attributes to create a constructor and destructor for your shared library. That may not be entirely portable; it depends on what platforms you care about.
Note that in this specific case there's no need to do something this complicated. The information that you're returning from the shared library in your second example is all known at compile time, so you can just create a static struct with that information and either retrieve the address of the struct with dlsym and poke around in it from the main program or call a known function to return the struct. (The latter is slightly more flexible in some corner cases, but both are fairly flexible.)

Resources