Been fighting with this on and off for 48 hours now; I'm still getting undefined reference errors when attempting to link a dynamic library with its dependency - despite all exports existing, and the library being found successfully.
Scenario:
libmemory (C++) - exports functions with extern "C"
libstring (C) - exports functions, imports from libmemory
libmemory builds successfully:
$ g++ -shared -fPIC -o ./builds/libmemory.so ...$(OBJECTS)...
libstring compiles successfully, but fails to link:
$ gcc -shared -fPIC -o ./builds/libstring.so ...$(OBJECTS)... -L./builds -lmemory
./temp/libstring/string.o: In function `STR_duplicate':
string.c:(.text+0x1cb): undefined reference to `MEM_priv_alloc'
./temp/libstring/string.o: In function `STR_duplicate_replace':
string.c:(.text+0x2a0): undefined reference to `MEM_priv_free'
string.c:(.text+0x2bf): undefined reference to `MEM_priv_alloc'
/usr/bin/ld: ./builds/libstring.so: hidden symbol `MEM_priv_free' isn't defined
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
Verifying libmemory exports its symbols, and the library itself is found by using -v to gcc:
...
attempt to open ./builds/libmemory.so succeeded
-lmemory (./builds/libmemory.so)
...
$ nm -gC ./builds/libmemory.so | grep MEM_
0000000000009178 T MEM_exit
0000000000009343 T MEM_init
00000000000093e9 T MEM_print_leaks
00000000000095be T MEM_priv_alloc
000000000000971d T MEM_priv_free
00000000000099c1 T MEM_priv_realloc
0000000000009d26 T MEM_set_callback_leak
0000000000009d3f T MEM_set_callback_noleak
$ objdump -T ./builds/libmemory.so | grep MEM_
0000000000009d3f g DF .text 0000000000000019 Base MEM_set_callback_noleak
00000000000093e9 g DF .text 00000000000001d5 Base MEM_print_leaks
0000000000009d26 g DF .text 0000000000000019 Base MEM_set_callback_leak
00000000000099c1 g DF .text 0000000000000365 Base MEM_priv_realloc
0000000000009343 g DF .text 00000000000000a6 Base MEM_init
00000000000095be g DF .text 000000000000015f Base MEM_priv_alloc
000000000000971d g DF .text 00000000000002a4 Base MEM_priv_free
0000000000009178 g DF .text 00000000000000a7 Base MEM_exit
$ readelf -Ws ./builds/libmemory.so | grep MEM_
49: 0000000000009d3f 25 FUNC GLOBAL DEFAULT 11 MEM_set_callback_noleak
95: 00000000000093e9 469 FUNC GLOBAL DEFAULT 11 MEM_print_leaks
99: 0000000000009d26 25 FUNC GLOBAL DEFAULT 11 MEM_set_callback_leak
118: 00000000000099c1 869 FUNC GLOBAL DEFAULT 11 MEM_priv_realloc
126: 0000000000009343 166 FUNC GLOBAL DEFAULT 11 MEM_init
145: 00000000000095be 351 FUNC GLOBAL DEFAULT 11 MEM_priv_alloc
192: 000000000000971d 676 FUNC GLOBAL DEFAULT 11 MEM_priv_free
272: 0000000000009178 167 FUNC GLOBAL DEFAULT 11 MEM_exit
103: 0000000000009343 166 FUNC GLOBAL DEFAULT 11 MEM_init
108: 0000000000009178 167 FUNC GLOBAL DEFAULT 11 MEM_exit
148: 0000000000009d3f 25 FUNC GLOBAL DEFAULT 11 MEM_set_callback_noleak
202: 00000000000095be 351 FUNC GLOBAL DEFAULT 11 MEM_priv_alloc
267: 000000000000971d 676 FUNC GLOBAL DEFAULT 11 MEM_priv_free
342: 0000000000009d26 25 FUNC GLOBAL DEFAULT 11 MEM_set_callback_leak
346: 00000000000099c1 869 FUNC GLOBAL DEFAULT 11 MEM_priv_realloc
366: 00000000000093e9 469 FUNC GLOBAL DEFAULT 11 MEM_print_leaks
Is there something horribly simple I'm missing? All the other related questions to this have simple answers such as link library order, and the paths used - but I've already verified they're in place and working as expected.
Tinkering with -fvisibility led to no changes either.
The same result exists whether using clang or gcc.
Linux 3.16.0-38-generic
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
You should mark MEM_priv_alloc() function as extern "C" or wrap function body into extern "C" { /* function implementation */ } (already done as I can see from description).
And use combination of __cplusplus and extern "C" in header
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
EXTERNC int MEM_priv_alloc (void);
Please also double check how MEM_priv_alloc prototype is done. For example, MEM_priv_alloc should not be inline (unfortunately I do not know phisics of this, however inline example failed for me)
Below is simplified example that works for me:
files:
$ ls
main.c Makefile mem.cpp mem.h strings.c strings.h
mem.cpp
#include <stdio.h>
#include "mem.h"
extern "C" int MEM_priv_alloc (void)
{
return 0;
}
mem.h
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
EXTERNC int MEM_priv_alloc (void);
strings.c:
#include <stdio.h>
#include "mem.h"
int STR_duplicate_replace (void)
{
MEM_priv_alloc();
}
strings.h:
int STR_duplicate_replace (void);
main.c
#include <stdio.h>
#include "string.h"
int main (void)
{
STR_duplicate_replace ();
return 0;
}
Makefile:
prog: libmemory.so libstrings.so
gcc -o $# -L. -lmemory -lstrings main.c
libmemory.so: mem.cpp
g++ -shared -fPIC -o $# $^
libstrings.so: strings.c
gcc -shared -fPIC -o $# $^
and the build log:
g++ -shared -fPIC -o libmemory.so mem.cpp
gcc -shared -fPIC -o libstrings.so strings.c
gcc -o prog -L. -lmemory -lstrings main.c
please also see more details in
Using C++ library in C code and
wiki How to mix C and C++
So, I was stripping out the final parts of the amalgamation, and uncovered the issue.
My import/export is modelled off of this: https://gcc.gnu.org/wiki/Visibility
My equivalent implementation ends up looking like this:
# if GCC_IS_V4_OR_LATER
# define DLLEXPORT __attribute__((visibility("default")))
# define DLLIMPORT __attribute__((visibility("hidden")))
# else
# define DLLEXPORT
# define DLLIMPORT
# endif
The DLLIMPORT (visibility hidden) is causing the problem; I replace it with a blank definition and it's all ok. Yes, I did also have the equivalent for clang, which is why that also failed in the same way.
My takeaway from this is that the C code only ever saw these would-be-symbols as hidden, and therefore couldn't import them no matter how hard it tried, and however much they actually existed!
Related
I try to build an executable that links to various shared and static libraries. It turns out that two of the static libraries both define the same symbol, which results in a multiple definition linker error. My executable doesn't use this symbol so it's not really a concern.
I can avoid the error by adding the --allow-multiple-definitions flag but that seems like a nuclear option. I would like the linker to complain if I try to use a multiple-time defined symbol.
Is there a way to tell the linker "complain for multiple definitions only if the symbol is used"? Or alternatively tell it, "from lib ABC ignore symbol XYZ". I am developing with g++ on linux.
You may have a one variant of the problem or a different variant,
depending on facts whose relevance you haven't yet considered. Or possibly you have a mixture of both, so I'll walk through a solution to each variant.
You should be familiar with the nature of static libraries and how they are consumed in linkage,
as summarised here
The Superflous Globals Symbols Variant
Here are a couple of source files and a header file:
one.cpp
#include <onetwo.h>
int clash = 1;
int get_one()
{
return clash;
}
two.cpp
#include <onetwo.h>
int get_two()
{
return 2;
}
onetwo.h
#pragma once
extern int get_one();
extern int get_two();
These have been built into a static library libonetwo.a
$ g++ -Wall -Wextra -pedantic -I. -c one.cpp two.cpp
$ ar rcs libonetwo.a one.o two.o
whose intended API is defined in onetwo.h
Simarily, some other source files and a header have been
built into a static libary libfourfive.a whose intended API
is defined in fourfive.h
four.cpp
#include <fourfive.h>
int clash = 4;
int get_four()
{
return clash;
}
five.cpp
#include <fourfive.h>
int get_five()
{
return 5;
}
fourfive.h
#pragma once
extern int get_four();
extern int get_five();
And here's the source of a program that depends on both libraries:
prog.cpp
#include <onetwo.h>
#include <fourfive.h>
int main()
{
return get_one() + get_four();
}
which we try to build like so:
$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(four.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(one.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
encountering a name-collision for the symbol clash, because it is globally defined in two of the
object files that the linkage requires, one.o and four.o:
$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z7get_twov
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z8get_fivev
The problem symbol clash is not referenced in our own code, prog.(cpp|o). You wondered:
Is there a way to tell the linker "complain for multiple definitions only if the symbol is used"?
No there isn't, but that's immaterial. one.o would not have been extracted from libonetwo.a
and linked into the program if the linker didn't need it to resolve some symbol. It needed it
to resolve get_one. Likewise it only linked four.o because it's needed to resolve get_four.
So the colliding definitions of clash are in the linkage. And although prog.o doesn't use clash,
it does use get_one, which uses clash and which intends to use the defintion of clash in one.o.
Likewise prog.o uses get_four, which uses clash and intends to use the different definition in four.o.
Even if clash was unused by each libary as well as the program, the fact that it is defined
in multiple object files that must be linked into the program means that the program will contain
multiple definitions of it, and only --allow-multiple-definitions will allow that.
In that light you'll also see that:
Or alternatively [is there a way to] tell it, "from lib ABC ignore symbol XYZ".
in general won't fly. If we could tell the linker to ignore (say) the definition of clash
in four.o and resolve the symbol everywhere to the definition in one.o (the only other candidate) then get_four() would return 1 instead of 4 in our program. That
is in fact the effect of --allow-multiple-definitions, since it causes the first definition
in the linkage to be used.
By inspection of the source code of libonetwo.a (or libfourfive.a) we can fairly confidently spot the root
cause of the problem. The symbol clash has been left with external linkage where
it only needed internal linkage, since it isn't declared in the associated
header file and is referenced nowhere in the libary except
in the file where it's defined. The offending source files should have written:
one_good.cpp
#include <onetwo.h>
namespace {
int clash = 1;
}
int get_one()
{
return clash;
}
four_good.cpp
#include <fourfive.h>
namespace {
int clash = 4;
}
int get_four()
{
return clash;
}
and all would be good:
$ g++ -Wall -Wextra -pedantic -I. -c one_good.cpp four_good.cpp
$ readelf -s one_good.o four_good.o | egrep '(File|Symbol|OBJECT|FUNC)'
File: one_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: four_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
$ g++ -o prog prog.o one_good.o four_good.o
$./prog; echo $?
5
Since re-writing the source code like that is not a option, we have to modify the object files to the
same effect. The tool for this is objcopy.
$ objcopy --localize-symbol=clash libonetwo.a libonetwo_good.a
This command has the same effect as running:
$ objcopy --localize-symbol=clash orig.o fixed.o
on each of the object files libonetwo(orig.o) to output a fixed object file fixed.o,
and archiving all the fixed.o files in a new static library libonetwo_good.a. And the
effect of --localize-symbol=clash, on each object file, is to change the linkage of
the symbol clash, if defined, from external (GLOBAL) to internal (LOCAL):
$ readelf -s libonetwo_good.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 10 entries:
Now the linker cannot see the LOCAL definition of clash in libonetwo_good.a(one.o).
That's sufficient to head off the multiple definition error, but since libfourfive.a has
the same defect, we'll fix it too:
$ objcopy --localize-symbol=clash libfourfive.a libfourfive_good.a
And then we can relink prog successfully, using the fixed libraries.
$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5
The Global Symbols Deadlock Variant
In this scenario, the sources and headers for libonetwo.a are:
one.cpp
#include <onetwo.h>
#include "priv_onetwo.h"
int inc_one()
{
return inc(clash);
}
two.cpp
#include <onetwo.h>
#include "priv_onetwo.h"
int inc_two()
{
return inc(clash + 1);
}
priv_onetwo.cpp
#include "priv_onetwo.h"
int clash = 1;
int inc(int i)
{
return i + 1;
}
priv_onetwo.h
#pragma once
extern int clash;
extern int inc(int);
onetwo.h
#pragma once
extern int inc_one();
extern int inc_two();
And for libfourfive.a they are:
four.cpp
#include <fourfive.h>
#include "priv_fourfive.h"
int dec_four()
{
return dec(clash);
}
five.cpp
#include <fourfive.h>
#include "priv_fourfive.h"
int dec_five()
{
return dec(clash + 1);
}
priv_fourfive.cpp
#include "priv_fourfive.h"
int clash = 4;
int dec(int i)
{
return i - 1;
}
priv_fourfive.h
#pragma once
extern int clash;
extern int dec(int);
fourfive.h
#pragma once
extern int dec_four();
extern int dec_five();
Each of these libraries is built with some common internals defined
in a source file - (priv_onetwo.cpp|priv_fourfive.cpp)
- and these internals are globally declared for building the library through a private header
- (priv_onetwo.h|priv_fourfive.h) - that is not distributed with the library.
They are undocumented symbols but nevertheless exposed to the linker.
Now there are two files in each library make that undefined (UND) references
to the global symbol clash, which is defined in another file:
$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC|clash)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z7inc_onev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z7inc_twov
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3inci
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z8dec_fourv
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z8dec_fivev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(priv_fourfive.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3deci
Our program source this time is:
prog.cpp
#include <onetwo.h>
#include <fourfive.h>
int main()
{
return inc_one() + dec_four();
}
and:
$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(priv_fourfive.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(priv_onetwo.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
once again clash is multiply defined. To resolve inc_one in main, the
linker needed one.o, which obliged it to resolve inc, which made it need
priv_onetwo.o, which contains the first definition of clash. To resolve dec_four in main, the
linker needed four.o, which obliged it to resolve dec, which made it need
priv_fourfive.o, which contains a rival definition of clash.
In this scenario, it isn't a coding error in either library that clash has external linkage.
It needs to have external linkage. Localizing the definition of clash with objcopy in either of
libonetwo.a(priv_onetwo.o) or libfourfive.a(priv_fourfive.o) will not work. If we do
that the linkage will succeed but output a bugged program, because the linker will resolve
clash to the one surviving GLOBAL definition from the other object file: then dec_four()
will return 0 instead of 3 in the program, dec_five() will return 1 not 4 ; or else inc_one() will return 5 and inc_two() will return 6.
And if we localize both definitions then no definition of clash will be found in the
linkage of prog to satisfy the references in one.o or four.o, and it will fail for undefined reference to clash
This time objcopy comes to the rescue again, but with a different option1:
$ objcopy --redefine-sym clash=clash_onetwo libonetwo.a libonetwo_good.a
The effect of this command is to create a new static library libonetwo_good.a,
containing new object files that are pairwise the same as those in libonetwo.a,
except that the symbol clash has been everywhere replaced with clash_onetwo:
$ readelf -s libonetwo_good.a | egrep '(File|Symbol|clash)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash_onetwo
We'll do the corresponding thing with libfourfive.a:
$ objcopy --redefine-sym clash=clash_fourfive libfourfive.a libfourfive_good.a
Now we're good to go once more:
$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5
Of the two solutions, use the fix for The Superflous Globals Symbols Variant if
superflous globals is what you've got, although the fix for the The Global Symbols Deadlock Variant
would also work. It is never desirable to tamper with object files
between compilation and linkage; it can only be unavoidable or the lesser of evils. But
if you're going to tamper with them, localizing a global symbol that should never have been global
is a more transparent tampering than changing the name of a symbol to one that
has no origin in source code.
[1] Don't forget that if you want to use objcopy with any option argument that
is a symbol in a C++ object file, you have to use the mangled name of the
C++ identifier than maps to the symbol. In this demo code it happens that the mangled name of the
C++ identifier clash is also clash. But if, e.g. the fully qualified identfier had
been onetwo::clash, its mangled name would be _ZN6onetwo5clashE, as reported by
nm or readelf. Conversely of course if you wished to use objcopy to change _ZN6onetwo5clashE in
an object file to a symbol that will demangle as onetwo::klash, then that symbol will
be _ZN6onetwo5klashE.
I am trying to write a makefile for a project. The project involves a test program that defines a main function written in C++11 that is supposed to call a shared object library written in c99 and run some tests.
My makefile successfully compiles the c99 library and produces "libhanoi.so".
When I try to link the C99 library to the C++11 part, I get the following error:
g++ -std=gnu++11 -L. -lhanoi -o main tests/testing.cpp tests/main.cpp
/tmp/cctHQTcW.o: In function `binarion_constructor(unsigned long*)':
main.cpp:(.text+0x28): undefined reference to `binarion64_t'
collect2: error: ld returned 1 exit status
Makefile:29: recipe for target 'tests' failed
make: *** [tests] Error 1
However, the output of "nm -C libhanoi.so" shows that the binarion64_t function is being exported by libhanoi.so:
0000000000000610 T binarion64_t(long long, long long)
When I introduce a typo into the name of libhanoi.so, it introduces an error saying it can't find libhanoi.so.
So it must be able to find libhanoi.so and libhanoi.so is exporting the unimplemented function in main.cpp, yet it still is giving an undefined reference. What's going on?
Minimal example:
hanoi.h:
#ifndef HANOI_H
#define HANOI_H
#include <inttypes.h>
// binarion (base -1+i) constructor
uint64_t binarion64_t(long long A, long long B);
#endif // HANOI_H
binarion.c:
#include "hanoi.h"
uint64_t binarion64_t(long long A,long long B){
return 0;
}
main.cpp:
#include <stdio.h>
extern "C" {
#include "hanoi.h"
};
uint64_t binarion_constructor(uint64_t * args){
return binarion64_t(args[0], args[1]);
}
int main(void){
return 0;
}
Compile:
g++ -std=c99 -c binarion.c
g++ -std=c99 -shared -o libhanoi.so binarion.o -lm
g++ -std=gnu++11 -L. -lhanoi -o main main.cpp
output:
/tmp/ccjoRmCg.o: In function `binarion_constructor(unsigned long*)':
main.cpp:(.text+0x28): undefined reference to `binarion64_t'
collect2: error: ld returned 1 exit status
EDIT:
The commands I'm running are:
gcc -std=c99 -c binarion.c
gcc -std=c99 -shared -o libhanoi.so binarion.o -lm
g++ -std=gnu++11 -L. -lhanoi -o main main.cpp
The files are exactly the ones in the question. The output of "readelf -s libhanoi.so | grep binarion" is:
12: 0000000000000660 19 FUNC GLOBAL DEFAULT 11 binarion64_t
33: 0000000000000000 0 FILE LOCAL DEFAULT ABS binarion.c
46: 0000000000000660 19 FUNC GLOBAL DEFAULT 11 binarion64_t
and the output of "g++ -std=gnu++11 -L. -lhanoi -o main main.cpp" is:
/tmp/cczfgY8M.o: In function `binarion_constructor(unsigned long*)':
main.cpp:(.text+0x28): undefined reference to `binarion64_t'
collect2: error: ld returned 1 exit status
TL; DR:
Use:
gcc -std=c99 -c binarion.c
gcc -std=c99 -shared -o libhanoi.so binarion.o -lm
g++ -std=gnu++11 -L. -lhanoi -o main main.cpp
Explanation:
You should be using gcc to compile a C file, g++ is for C++. When you do g++ -std=c99 -c binarion.c the compiler gives you a hint with:
cc1plus: warning: command line option ‘-std=c99’ is valid for C/ObjC but not for C++
This means that you end up compiling your library as a C++ library. You can verify that by calling readelf -s libhanoi.so | grep binarion:
9: 00000000000005da 19 FUNC GLOBAL DEFAULT 9 _Z12binarion64_txx
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS binarion.c
44: 00000000000005da 19 FUNC GLOBAL DEFAULT 9 _Z12binarion64_txx
As you can see, the function has been name-mangled, which is something C++ does, and C doesn't.
However, when compiling main.cpp you tell the compiler that binarion_t has C linkage:
extern "C" {
#include "hanoi.h"
};
So it is searching for unmangled binarion_t (instead of _Z12binarion64_txx).
I have a header file that declares a template with a static variable and also defines it:
/* my_header.hpp */
#ifndef MY_HEADER_HPP_
#define MY_HEADER_HPP_
#include <cstdio>
template<int n>
struct foo {
static int bar;
static void dump() { printf("%d\n", bar); }
};
template<int n>
int foo<n>::bar;
#endif // MY_HEADER_HPP_
This header is included both by main.cpp and a shared library mylib. In particular, mylib_baz.hpp just includes this template and declares a function that modifies a specialization of the template.
/* mylib_baz.hpp */
#ifndef MYLIB_BAZ_HPP_
#define MYLIB_BAZ_HPP_
#include "my_header.hpp"
typedef foo<123> mylib_foo;
void init_mylib_foo();
#endif // MYLIB_BAZ_HPP_
and
/* mylib_baz.cpp */
#include "mylib_baz.hpp"
void init_mylib_foo() {
mylib_foo::bar = 123;
mylib_foo::dump();
};
When I make mylib.so (containing mylib_baz.o), the symbol for foo<123>::bar is present and declared weak. However, the symbol for foo<123>::bar is declared weak also in my main.o:
/* main.cpp */
#include "my_header.hpp"
#include "mylib_baz.hpp"
int main() {
foo<123>::bar = 456;
foo<123>::dump(); /* outputs 456 */
init_mylib_foo(); /* outputs 123 */
foo<123>::dump(); /* outputs 123 -- is this guaranteed? */
}
It appears that I am violating one definition rule (foo<123>::bar defined both in my_header.cpp and main.cpp). However, both with g++ and clang the symbols are declared weak (or unique), so I am not getting bitten by this -- all accesses to foo<123>::bar modify the same object.
Question 1: Is this a coincidence (maybe ODR works differently for static members of templates?) or am I in fact guaranteed this behavior by the standard?
Question 2: How could I have predicted the behavior I'm observing? That is, what exactly makes the compiler declare symbol weak?
There is no ODR violation. You have one definition of bar. It is here:
template<int n>
int foo<n>::bar; // <==
As bar is static, that indicates that there is one definition across all translation units. Even though bar will show up once in all of your object files (they need a symbol for it, after all), the linker will merge them together to be the one true definition of bar. You can see that:
$ g++ -std=c++11 -c mylib_baz.cpp -o mylib_baz.o
$ g++ -std=c++11 -c main.cpp -o main.o
$ g++ main.o mylib_baz.o -o a.out
Produces:
$ nm mylib_baz.o | c++filt | grep bar
0000000000000000 u foo<123>::bar
$ nm main.o | c++filt | grep bar
0000000000000000 u foo<123>::bar
$ nm a.out | c++filt | grep bar
0000000000601038 u foo<123>::bar
Where u indicates:
"u"
The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.
I want to build a dynamic library containing haskell functions. I work on linux and want to call this dynamic library from C++ code.
I used the example at http://wiki.python.org/moin/PythonVsHaskell and have the following files:
Test.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
module Test where
import Foreign.C.Types
hsfun :: CInt -> IO CInt
hsfun x = do
putStrLn "Hello World"
return (42 + x)
foreign export ccall
hsfun :: CInt -> IO CInt
module_init.c:
#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a
#include <HsFFI.h>
extern void CAT (__stginit_, MODULE) (void);
static void library_init (void) __attribute__ ((constructor));
static void
library_init (void)
{
/* This seems to be a no-op, but it makes the GHCRTS envvar work. */
static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv;
static int argc = 1;
hs_init (&argc, &argv_);
hs_add_root (CAT (__stginit_, MODULE));
}
static void library_exit (void) __attribute__ ((destructor));
static void
library_exit (void)
{
hs_exit ();
}
Now I compile this files to a dynamic library:
$ ghc -dynamic -shared -fPIC -optc '-DMODULE=Test' Test.hs module_init.c -o libTest.so
[1 of 1] Compiling Test ( Test.hs, Test.o )
Linking libTest.so ...
This creates among other things the file Test_stub.h:
#include "HsFFI.h"
#ifdef __cplusplus
extern "C" {
#endif
extern HsInt32 hsfun(HsInt32 a1);
#ifdef __cplusplus
}
#endif
and Test_stub.c:
#define IN_STG_CODE 0
#include "Rts.h"
#include "Stg.h"
#ifdef __cplusplus
extern "C" {
#endif
extern StgClosure Test_zdfhsfunzua165_closure;
HsInt32 hsfun(HsInt32 a1)
{
Capability *cap;
HaskellObj ret;
HsInt32 cret;
cap = rts_lock();
cap=rts_evalIO(cap,rts_apply(cap,(HaskellObj)runIO_closure,rts_apply(cap,&Test_zdfhsfunzua165_closure,rts_mkInt32(cap,a1))) ,&ret);
rts_checkSchedStatus("hsfun",cap);
cret=rts_getInt32(ret);
rts_unlock(cap);
return cret;
}
static void stginit_export_Test_zdfhsfunzua165() __attribute__((constructor));
static void stginit_export_Test_zdfhsfunzua165()
{getStablePtr((StgPtr) &Test_zdfhsfunzua165_closure);}
#ifdef __cplusplus
}
#endif
Then I create a cpp file main.cpp:
#include "Test_stub.h"
#include <iostream>
using namespace std;
int main()
{
cout << hsfun(5);
}
and want to compile and link it. But when I call g++, it says:
$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp
/tmp/ccFP2AuB.o: In function `main':
main.cpp:(.text+0xa): undefined reference to `hsfun'
collect2: ld gab 1 als Ende-Status zurück
So I added the Test_stub.o file to the command line (although I think the hsfun function should already be defined in libTest.so which is added via the -lTest parameter. I don't think, I should link the Test_stub.o file into the executable because I want to use dynamic linking), but this also doesn't work:
$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp Test_stub.o
Test_stub.o: In function `hsfun':
Test_stub.c:(.text+0x9): undefined reference to `rts_lock'
Test_stub.c:(.text+0x16): undefined reference to `rts_mkInt32'
Test_stub.c:(.text+0x1d): undefined reference to `Test_zdfhsfunzua165_closure'
Test_stub.c:(.text+0x28): undefined reference to `rts_apply'
Test_stub.c:(.text+0x2f): undefined reference to `base_GHCziTopHandler_runIO_closure'
Test_stub.c:(.text+0x3a): undefined reference to `rts_apply'
Test_stub.c:(.text+0x4a): undefined reference to `rts_evalIO'
Test_stub.c:(.text+0x5c): undefined reference to `rts_checkSchedStatus'
Test_stub.c:(.text+0x66): undefined reference to `rts_getInt32'
Test_stub.c:(.text+0x70): undefined reference to `rts_unlock'
Test_stub.o: In function `stginit_export_Test_zdfhsfunzua165':
Test_stub.c:(.text.startup+0x3): undefined reference to `Test_zdfhsfunzua165_closure'
Test_stub.c:(.text.startup+0x8): undefined reference to `getStablePtr'
collect2: ld gab 1 als Ende-Status zurück
Do I have to link the Test_stub.o? If yes, why? And which arguments should I pass to the linker?
Probably easier than wrestling with g++ is letting ghc do the work,
ghc main.cpp -o hithere -L. -lTest -lstdc++
did the job for me after creating the shared lib the way you did. I have tested it with 7.2.2 and 7.0.2, both worked here.
I want to build 2 dlls, lets call them Foo and Bar. I want Bar to import some class from Foo.
Foo.h:
#ifdef EXPORT
#define DECL __declspec(dllexport)
#else
#define DECL __declspec(dllimport)
#endif
class DECL Foo {
public:
Foo();
void bar();
};
Bar.cpp:
#include "bar.h"
void bar(){
Foo f;
f.bar();
}
To build Foo.dll, I do
g++ -DEXPORT -c Foo.cpp -o Foo.o
g++ -shared Foo.o -o Foo.dll
This produces the following references in Foo.o:
$ nm Foo.o
00000000 b .bss
00000000 d .data
00000000 i .drectve
00000000 t .text
0000000c T __ZN3Foo3barEv
00000006 T __ZN3FooC1Ev
00000000 T __ZN3FooC2Ev
Now, when I want to build Bar.dll, I do
$ g++ -shared Bar.cpp -o Bar.dll
/tmp/ccr8F57C.o:Bar.cpp:(.text+0xd): undefined reference to `__imp___ZN3FooC1Ev'
/tmp/ccr8F57C.o:Bar.cpp:(.text+0x1a): undefined reference to `__imp___ZN3Foo3barEv'
If I try to build Foo.cpp with EXPORT not defined (so that the macro DECL evaluates to __declspec(dllimport), I get the following:
$ g++ -c Foo.cpp
Foo.cpp:3: warning: function 'Foo::Foo()' is defined after prior declaration as dllimport: attribute ignored
Foo.cpp: In constructor `Foo::Foo()':
Foo.cpp:3: warning: function 'Foo::Foo()' is defined after prior declaration as dllimport: attribute ignored
Foo.cpp: In member function `void Foo::bar()':
Foo.cpp:7: warning: function 'void Foo::bar()' is defined after prior declaration as dllimport: attribute ignored
which does make sense, since a function declared dllimport can't then be defined.
How am I supposed to reference Foo in Bar?
When you build Bar.dll you also need to link against Foo.lib with -l option