Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dlsym()'ing a global variable in C++

I want to make a program which is able to dlopen() a series of libraries (written by myself) and run all the functions stored in a global variable called test_suite inside that .so file, which is a NULL-terminated array of function pointers (the functions' signatures are predefined by myself, no need to worry about that).

The problem is g++ mangles that variable. The library is compiled as:

g++ -Wall -shared -rdynamic -fPIC foo.cpp -o foo.so

and the "function index" is declared and allocated statically as:

const testunit_testcase test_suite = { ... }

yet

objdump -t foo.so  | grep test_suite

shows:

0000000000200940 l     O .data.rel.ro   0000000000000020              _ZL10test_suite

What I need is

0000000000200940 l     O .data.rel.ro   0000000000000020              test_suite

So I can dlsym(dlh, "test_suite") in the program dlopen()'ing foo.so

Thanks


Addendum

Yes, extern "C" was the first thing I've tried:

extern "C" {
        const testunit_testcase test_suite[] = { 
                //TESTUNIT_DEF_TESTCASE(doTest),
                {NULL, NULL},
        };  
}

I am using:

g++ -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/lto-wrapper Target: x86_64-unknown-linux-gnu Configured with: /build/src/gcc-4.5-20110127/configure --prefix=/usr --enable-languages=c,c++,fortran,objc,obj-c++,ada --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-gnu-unique-object --enable-lto --enable-plugin --enable-gold --with-plugin-ld=ld.gold --disable-multilib --disable-libstdcxx-pch --with-system-zlib --with-ppl --with-cloog --with-cloog-include=/usr/include/cloog-ppl --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info Thread model: posix gcc version 4.5.2 20110127 (prerelease) (GCC)


Addendum 2

For whatever reasons

extern "C" {
     const testunit_testcase test_suite = { ... }
}

does not work, BUT this one does:

extern "C" const testunit_testcase test_suite = { ... }

My question now: As I can see in some of your answers, enclosing extern "C" { ... } works for you. Are there any compiler flags I could use to make sure that test_suite will never be mangled, no matter what 4.x (at least) g++ version is used?

like image 846
Flavius Avatar asked Mar 28 '11 14:03

Flavius


People also ask

Are variables global in C?

The C compiler recognizes a variable as global, as opposed to local, because its declaration is located outside the scope of any of the functions making up the program. Of course, a global variable can only be used in an executable statement after it has been declared.

What can I use instead of global variables in C?

Use Local Variables Instead The idea is that the global variable gState is replaced by the local variable sState in the script of your main application stack.

Can I declare a global variable inside a function in C?

Use of the Global Variable in C The global variables get defined outside any function- usually at the very top of a program. After this, the variables hold their actual values throughout the lifetime of that program, and one can access them inside any function that gets defined for that program.

Why do we use global variables?

Variables that are created outside of a function (as in all of the examples above) are known as global variables. Global variables can be used by everyone, both inside of functions and outside.


2 Answers

The problem isn't one of name mangling. (Or probably isn't: public variable names are not usually mangled.) The real problem is that the "const" means implicit static, rendering the variable invisible outside the translation unit. To avoid this, the variable must be explicitly declared extern. And "The form of linkage-specification that contains a brace-enclosed declaration-seq does not affect whether the contained declarations are definitions or not (3.1); the form of linkage-specification directly containing a single declaration is treated as an extern specifier (7.1.1) for the purpose of determining whether the contained declaration is a definition." Which, while it doesn't seem to address your issue directly (the presence of an initializer ensures that the declaration is a definition), it does seem to indicate the intent: within a brace enclosed linkage specifier, the usual rules apply; if the linkage specifier applies directly to the declaration, it's as if the declaration were explicitly extern. So you can write either:

extern "C" {
    testunit_testcase const test_suite[] // ...
}

or

extern "C" testunit_testcase const test_suite[] // ...

But there must be an extern which applies explicitly to the definition, in order to override the implicit "static" of "const".

like image 200
James Kanze Avatar answered Oct 14 '22 01:10

James Kanze


This

x.cxx:

 extern "C"
 {
    extern const int test_suite[] = { 0 };
 }

works for me:

~/ec% g++ -Wall -rdynamic -shared x.cxx -o x.so
~/ec% objdump -t x.so | grep test_suite
00000444 g     O .rodata        00000004              test_suite

If I don't extern test_suite it doesn't get exported at all. This makes sense as const at file or namespace scope implies static. It doesn't make sense because I would expect the extern "C" block to "count" for that. I don't know if this is a gcc bug or not. Can you reduce your problem to something similar in size?

like image 28
Logan Capaldo Avatar answered Oct 14 '22 00:10

Logan Capaldo