How do you link external shared libraries to a native extension? - c++

I'm writing a pty native extension and want to link libutil so that I may use forkpty and openpty from <pty.h>.
I'm using the two commands taken from the official guide:
g++ -fPIC -lutil -I/home/crunchex/work/dart-sdk -c pty.cc -o pty.o
gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o
and I'm getting the following error:
/home/crunchex/work/dart-sdk/bin/dart: symbol lookup error: /home/crunchex/work/pty/bin/packages/pty/libpty.so: undefined symbol: forkpty
This may be more of a g++/gcc question, but as far as I can tell I'm doing that part right by adding -lutil and including <pty.h>. libutil.so is installed on my Ubuntu 14.04 system, so I'm fairly certain it's there.
Here's my test extension:
#include <string.h>
#include <pty.h>
#include "include/dart_api.h"
Dart_NativeFunction ResolveName(Dart_Handle name,
int argc,
bool* auto_setup_scope);
DART_EXPORT Dart_Handle pty_Init(Dart_Handle parent_library) {
if (Dart_IsError(parent_library)) {
return parent_library;
}
Dart_Handle result_code =
Dart_SetNativeResolver(parent_library, ResolveName, NULL);
if (Dart_IsError(result_code)) {
return result_code;
}
return Dart_Null();
}
Dart_Handle HandleError(Dart_Handle handle) {
if (Dart_IsError(handle)) {
Dart_PropagateError(handle);
}
return handle;
}
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
void PtyFork(Dart_NativeArguments args) {
Dart_EnterScope();
struct winsize winp;
winp.ws_col = 80;
winp.ws_row = 24;
winp.ws_xpixel = 0;
winp.ws_ypixel = 0;
int master = -1;
char name[40];
pid_t pid = forkpty(&master, name, NULL, &winp);
Dart_ExitScope();
}
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
struct FunctionLookup {
const char* name;
Dart_NativeFunction function;
};
FunctionLookup function_list[] = {
{"PtyFork", PtyFork},
{NULL, NULL}};
Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool* auto_setup_scope) {
if (!Dart_IsString(name)) return NULL;
Dart_NativeFunction result = NULL;
Dart_EnterScope();
const char* cname;
HandleError(Dart_StringToCString(name, &cname));
for (int i=0; function_list[i].name != NULL; ++i) {
if (strcmp(function_list[i].name, cname) == 0) {
result = function_list[i].function;
break;
}
}
Dart_ExitScope();
return result;
}

Copied from https://code.google.com/p/dart/issues/detail?id=22257#c4
The problem is that libutil, part of libc6, needs to be linked into your native extension shared library on the link command line, not the compile command line.
First, the -lutil library specification should go on the linking line, rather than the compiling line:
gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o -lutil
This puts a dependency on the shared library libutil.so into your shared library, and when it is loaded by dlload, the dependencies are also loaded and linked.
This fails unless the -lutil option is put after pty.o on your command
line, since linked libraries must be put in reverse dependency order on the linker command line.
After doing this, the output of objdump on libpty.so includes:
objdump -x libpty.so
Dynamic Section:
NEEDED libutil.so.1
NEEDED libc.so.6
SONAME libpty.so
INIT 0x00000000000009c0
FINI 0x0000000000000db4
INIT_ARRAY 0x0000000000201dd0
....
Version References:
required from libutil.so.1:
0x09691a75 0x00 04 GLIBC_2.2.5
required from libc.so.6:
0x09691a75 0x00 03 GLIBC_2.2.5
0x0d696914 0x00 02 GLIBC_2.4
....
0000000000000000 w *UND* 0000000000000000
_ITM_registerTMCloneTable
0000000000000000 F *UND* 0000000000000000
forkpty##GLIBC_2.2.5
0000000000000000 w F *UND* 0000000000000000
__cxa_finalize##GLIBC_2.2.5
00000000000009c0 g F .init 0000000000000000 _init
and running the test program main.dart no longer fails.
If you don't want to link a shared library into your library, then you need a static library, but there are many problems with this - it is not impossible, but much harder.
Then, the problem is that you may only have libutil.so on your system, not libutil.a, so your shared library will need to load libutil when it is loaded.
The dlopen function used by Dart to load your shared library should
recursively load other shared libraries it depends on, but this may or may not be working. When I compile with -lutil in the link step, the shared libraries shown by ldd libpty.so are just libc.so.6, and some standard linker ones ld-linux-.. and linux-vdso. So I don't see libutil there.
To link the functions you need statically into your shared library, you
would need something like
gcc -shared -Wl,-whole-archive /usr/lib/x86_64-linux-gnu/libutil.a
-Wl,-no-whole-archive -Wl,-soname,libpty.so -o libpty.so pty.o
But since the libutil.a in the distribution is not compiled with -wPIC, it can't be linked into a shared library:
/usr/bin/ld: /usr/lib/x86_64-linux-gnu/libutil.a(login.o): relocation
R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/lib/x86_64-linux-gnu/libutil.a(login.o): error adding symbols: Bad
value
I think the best bet is to make the shared library dependency upon
libutil.so work.

If this is reported:
/home/crunchex/work/pty/bin/packages/pty/libpty.so: undefined symbol: forkpty
then the tool definitely needed that your libpty either provide this symbol, or request to be linked against another library that provides it. So this:
gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o
is definitely incomplete.
Normally when tools like that allow you to create your own library that may need to call some of the functions that the "user of your library" provides, then it at least requires to be linked against a "stub" library. There's usually provided a library named like libXXXXstubVV.a (VV may be a version number or something).
May happen that it has something to do with the problem that happens in static libraries. For static libraries this can't be solved any other way than putting the libraries in the correct order. For dynamic libraries it's usually solved by providing a request to be linked against some dependent library that is expected to provide lacking symbols - and this "stub" library should do it.

Related

Dynamic library do not have symbol of static library file

There are no symbols of static library files or functins in a dynamic library(.so) file.
I am using ubuntu 18.04 and 12.04 system.
I created an object file from cpp file with -fPIC option than the created static library (.a).
After that I created a dynamic library using a command :g++ -shared -I(include path) -L(other library path) -l(librarys) -o filename.so -Wl,-soname,filename.so staticlib.a"
I show symbol created in 12.04 ubuntu system but not in 18.04 system.
I show so file created by there no symbols in so file. we can check it using nm -g filename .so.
I got this type of result in nm command in 18.04 system if I try with 12.04 it give me whole sysmbols of all cpp files.
$ nm -g libPJ.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000201020 B __bss_start
w __cxa_finalize
w __gmon_start__
0000000000201020 D _edata
0000000000201028 B _end
000000000000052c T _fini
0000000000000410 T _init
When linking a shared library from a static library add -Wl,-whole-archive linker flag. From man ld:
--whole-archive
For each archive mentioned on the command line after the
--whole-archive option, include every object file in the archive in
the link, rather than searching the archive for the required object
files. This is normally used to turn an archive file into a shared
library, forcing every object to be included in the resulting
shared library. This option may be used more than once.
Two notes when using this option from gcc: First, gcc doesn’t know
about this option, so you have to use -Wl,-whole-archive. Second,
don’t forget to use -Wl,-no-whole-archive after your list of
archives, because gcc will add its own list of archives to your
link and you may not want this flag to affect those as well.

Is it possible to force linker errors in gcc/g++? [duplicate]

I have a shared library that is linked with another (third-party) shared library. My shared library is then loaded using dlopen in my application. All this works fine (assuming files are in the proper path etc).
Now, the problem is that I don't even need to specify to link against the third-party shared library when I link my library. GCC accept it without reporting errors about undefined references. So, the question; how can I force GCC to notify me about undefined references?
If I change my library to be (temporarily) an executable, I get undefined references (when not supplying the library to the linker). (Works fine if I specify it.)
I.e., the following is done:
g++ -fPIC -shared -o libb.so b.o
g++ -fPIC -shared -o liba.so a.o
g++ -o a.exe a.cpp
Where the second line does NOT give out an error and the third line complains about an undefined reference.
Sample code:
a.h:
class a
{
public:
void foobar();
};
a.cpp:
#include "a.h"
#include "b.h"
void a::foobar()
{
b myB;
myB.foobar();
}
int main()
{
a myA; myA.foobar();
}
b.h:
class b
{
public:
void foobar();
};
b.cpp:
#include "b.h"
void b::foobar()
{
}
-Wl,--no-undefined linker option can be used when building shared library, undefined symbols will be shown as linker errors.
g++ -shared -Wl,-soname,libmylib.so.5 -Wl,--no-undefined \
-o libmylib.so.1.1 mylib.o -lthirdpartylib
After more research, I realized what way the stuff works. There are two linker options to manipulate undefined symbols of shared libraries:
First one is --no-undefined. It reports the unresolved symbols that are not resolved immediately, at linking stage. Unless the symbol is found in a shared library linked against, either manually (with -l switch) or automatically (libgcc_s, C++ runtime; libc, C runtime; ld-linux-**.so, dynamic linker utils) picked, --no-undefined reports it as error. That's the key the questioner needed.
There is another key, --no-allow-shlib-undefined (whose description also suggests --no-undefined). It checks if definitions in the shared libraries which you link your shared library against are satisfied. This key is of little use in the case shown in this topic, but it can be useful. However, It has its own obstacles.
The manpage provides some rationale about why it's not default:
--allow-shlib-undefined
--no-allow-shlib-undefined
Allows (the default) or disallows undefined symbols in shared
libraries (It is meant, in shared libraries _linked_against_, not the
one we're creating!--Pavel Shved). This switch is similar to --no-un-
defined except that it determines the behaviour when the undefined
symbols are in a shared library rather than a regular object file. It
does not affect how undefined symbols in regular object files are
handled.
The reason that --allow-shlib-undefined is the default is that the
shared library being specified at link time may not be the same as
the one that is available at load time, so the symbols might actually
be resolvable at load time. Plus there are some systems, (eg BeOS)
where undefined symbols in shared libraries is normal. (The kernel
patches them at load time to select which function is most appropri-
ate for the current architecture. This is used for example to dynam-
ically select an appropriate memset function). Apparently it is also
normal for HPPA shared libraries to have undefined symbols.
The thing is that what is said above is also true, for example, for Linux systems, where some of the internal routines of the shared library is implemented in ld-linux.so, the dynamic loader (it's both executable and shared library). Unless you somehow link it, you will get something like this:
/lib64/libc.so.6: undefined reference to `_dl_argv#GLIBC_PRIVATE'
/lib64/libc.so.6: undefined reference to `_rtld_global_ro#GLIBC_PRIVATE'
/usr/lib64/gcc/x86_64-suse-linux/4.3/libstdc++.so: undefined reference to `__tls_get_addr#GLIBC_2.3'
/lib64/libc.so.6: undefined reference to `_rtld_global#GLIBC_PRIVATE'
/lib64/libc.so.6: undefined reference to `__libc_enable_secure#GLIBC_PRIVATE'
These are undefined references from the loader, ld-linux.so. It is platform-specific (for example, on my system the correct loader is /lib64/ld-linux-x86-64.so). You may link the loader with your library and check even the tricky references shown above:
g++ -fPIC -shared -o liba.so a.o -Wl,--no-allow-shlib-undefined /lib64/ld-linux-x86-64.so.2

Violating the one definition rule by simply linking dynamically

Question: Are dynamically linked C++ programs on ELF platforms always on the brink of producing undefined behavior by violating the one definition rule?
More specific: By simply writing a shared library exposing one function
#include <string>
int __attribute__((visibility("default"))) combined_length(const char *s,
const char *t)
{
const std::string t1(t);
const std::string u(s + t1);
return u.length();
}
and compiling it with GCC 7.3.0 via
$ g++ -Wall -g -fPIC -shared \
-fvisibility=hidden -fvisibility-inlines-hidden \
-o liblibrary.so library.cpp
I create a binary which defines a weak symbol for the operator+() of a pointer to a character array and a string:
$ readelf -sW liblibrary.so | grep "_ZStpl"
24: 0000000000000ee2 202 FUNC WEAK DEFAULT 12 _ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EEPKS5_RKS8_
...
But looking at the standard library binary I got
$ readelf -sW /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep "_ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EEPKS5_RKS8_"
2829: 000000000012b1c0 169 FUNC WEAK DEFAULT 13 _ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EEPKS5_RKS8_##GLIBCXX_3.4.21
That's the point where I say: Oh my gosh, the symbol inside my library ought to have a version attached to it too!
In the current state I'm fine because I can assume that the standard library binary is built with the same headers as my library. But what happens if the implementers of libstdc++-v3 decide to define a new version of this function and tag it with GLIBCXX_3.4.22? Since the symbol is weak, the runtime linker is free to decide whether it takes the unversioned symbol of my library or the versioned symbol of the libstdc++-v3. If I ship my library to such a system I provoke undefined behavior there. Something symbol versions should have solved for me.

cpp: usr/bin/ld: cannot find -l<nameOfTheLibrary>

I created a cpp project, which used a lib file named: libblpapi3_64.so
This file comes from a library which I download it from Internet.
My project runs without any error. So I update it to bitbucket.
Then my colleague downloads it and runs it at his own computer. But he gets an error:
usr/bin/ld: cannot find -lblpapi3_64.
In fact, I have copied it into my project repository. I mean I created a file named lib under my project and all lib files that I used are in it.
There are also other lib files such as liblog4cpp.a, but they are all good. Only the libblpapi3_64.so gets the error.
Is it because it's a .so file not .a file? Or there is other reason?
Btw, the file name of libblpapi3_64.so is green and others files(.a) is white. I think it's not a link file, it's the original file.
Briefly:
ld does not know about where your project libs are located. You have to place it into ld's known directories or specify the full path of your library by -L parameter to the linker.
To be able to build your program you need to have your library in /bin/ld search paths and your colleague too. Why? See detailed answer.
Detailed:
At first, we should understand what tools do what:
The compiler produces simple object files with unresolved symbols (it does not care about symbols so much at it's running time).
The linker combines a number of object and archive files, relocates their data and ties up symbol references into a single file: an executable or a library.
Let's start with some example. For example, you have a project which consists of 3 files: main.c, func.h and func.c.
main.c
#include "func.h"
int main() {
func();
return 0;
}
func.h
void func();
func.c
#include "func.h"
void func() { }
So, when you compile your source code (main.c) into an object file (main.o) it can't be run yet because it has unresolved symbols. Let's start from the beginning of producing an executable workflow (without details):
The preprocessor after its job produces the following main.c.preprocessed:
void func();
int main() {
func();
return 0;
}
and the following func.c.preprocessed:
void func();
void func() { }
As you may see in main.c.preprocessed, there are no connections to your func.c file and to the void func()'s implementation, the compiler simply does not know about it, it compiles all the source files separately. So, to be able to compile this project you have to compile both source files by using something like cc -c main.c -o main.o and cc -c func.c -o func.o, this will produce 2 object files, main.o and func.o. func.o has all it's symbols resolved because it has only one function which body is written right inside the func.c but main.o does not have func symbol resolved yet because it does not know where it is implemented.
Let's look what is inside func.o:
$ nm func.o
0000000000000000 T func
Simply, it contains a symbol which is in text code section so this is our func function.
And let's look inside main.o:
$ nm main.o
U func
0000000000000000 T main
Our main.o has an implemented and resolved static function main and we are able to see it in the object file. But we also see func symbol which marked as unresolved U, and thus we are unable to see its address offset.
For fixing that problem, we have to use the linker. It will take all the object files and resolve all these symbols (void func(); in our example). If the linker somehow is unable to do that it throws a error like unresolved external symbol: void func(). This may happen if you don't give the func.o object file to the linker. So, let's give all the object files we have to the linker:
ld main.o func.o -o test
The linker will go through main.o, then through func.o, try to resolve symbols and if it goes okay - put it's output to the test file. If we look at the produced output we will see all symbols are resolved:
$ nm test
0000000000601000 R __bss_start
0000000000601000 R _edata
0000000000601000 R _end
00000000004000b0 T func
00000000004000b7 T main
Here our job is done. Let's look the situation with dynamic(shared) libraries. Let's make a shared library from our func.c source file:
gcc -c func.c -o func.o
gcc -shared -fPIC -Wl,-soname,libfunc.so.1 -o libfunc.so.1.5.0 func.o
Voila, we have it. Now, let's put it into known dynamic linker library path, /usr/lib/:
sudo mv libfunc.so.1.5.0 /usr/lib/ # to make program be able to run
sudo ln -s libfunc.so.1.5.0 /usr/lib/libfunc.so.1 #creating symlink for the program to run
sudo ln -s libfunc.so.1 /usr/lib/libfunc.so # to make compilation possible
And let's make our project depend on that shared library by leaving func() symbol unresolved after compilation and static linkage process, creating an executable and linking it (dynamically) to our shared library (libfunc):
cc main.c -lfunc
Now if we look for the symbol in its symbols table we still have our symbol unresolved:
$ nm a.out | grep fun
U func
But this is not a problem anymore because func symbol will be resolved by dynamic loader before each program start. Okay, now let's back to the theory.
Libraries, in fact, are just the object files which are placed into a single archive by using ar tool with a single symbols table which is created by ranlib tool.
Compiler, when compiling object files, does not resolve symbols. These symbols will be replaced to addresses by a linker. So resolving symbols can be done by two things: the linker and dynamic loader:
The linker: ld, does 2 jobs:
a) For static libs or simple object files, this linker changes external symbols in the object files to the addresses of the real entities. For example, if we use C++ name mangling linker will change _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_ to 0x07f4123f0.
b) For dynamic libs it only checks if the symbols can be resolved (you try to link with correct library) at all but does not replace the symbols by address. If symbols can't be resolved (for example they are not implemented in the shared library you are linking to) - it throws undefined reference to error and breaks up the building process because you try to use these symbols but linker can't find such symbol in it's object files which it is processing at this time. Otherwise, this linker adds some information to the ELF executable which is:
i. .interp section - request for an interpreter - dynamic loader to be called before executing, so this section just contains a path to the dynamic loader. If you look at your executable which depends on shared library (libfunc) for example you will see the interp section $ readelf -l a.out:
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
ii. .dynamic section - a list of shared libraries which interpreter will be looking for before executing. You may see them by ldd or readelf:
$ ldd a.out
linux-vdso.so.1 => (0x00007ffd577dc000)
libfunc.so.1 => /usr/lib/libfunc.so.1 (0x00007fc629eca000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fefe148a000)
/lib64/ld-linux-x86-64.so.2 (0x000055747925e000)
$ readelf -d a.out
Dynamic section at offset 0xe18 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libfunc.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
Note that ldd also finds all the libraries in your filesystem while readelf only shows what libraries does your program need. So, all of these libraries will be searched by dynamic loader (next paragraph).
The linker works at build time.
Dynamic loader: ld.so or ld-linux. It finds and loads all the shared libraries needed by a program (if they were not loaded before), resolves the symbols by replacing them to real addresses right before the start of the program, prepares the program to run, and then runs it. It works after the build and before running the program. Less speaking, dynamic linking means resolving symbols in your executable before each program start.
Actually, when you run an ELF executable with .interp section (it needs to load some shared libraries) the OS (Linux) runs an interpreter at first but not your program. Otherwise you have an undefined behavior - you have symbols in your program but they are not defined by addresses which usually means that the program will be unable to work properly.
You may also run dynamic loader by yourself but it is unnecessary (binary is /lib/ld-linux.so.2 for 32-bit architecture elf and /lib64/ld-linux-x86-64.so.2 for 64-bit architecture elf).
Why does the linker claim that /usr/bin/ld: cannot find -lblpapi3_64 in your case? Because it tries to find all the libraries in it's known paths. Why does it search the library if it will be loaded during runtime? Because it needs to check if all the needed symbols can be resolved by this library and to put it's name into the .dynamic section for dynamic loader. Actually, the .interp section exists in almost every c/c++ elf because the libc and libstdc++ libraries are both shared, and compiler by default links any project dynamically to them. You may link them statically as well but this will enlarge the total executable size. So, if the shared library can't be found your symbols will remain unresolved and you will be UNABLE to run your application, thus it can't produce an executable. You may get the list of directories where libraries are usually searched by:
Passing a command to the linker in compiler arguments.
By parsing ld --verbose's output.
By parsing ldconfig's output.
Some of these methods are explained here.
Dynamic loader tries to find all the libraries by using:
DT_RPATH dynamic section of an ELF file.
DT_RUNPATH section of the executable.
LD_LIBRARY_PATH environment variable.
/etc/ld.so.cache - own cache file which contains a compiled list of candidate libraries previously found in the augmented library path.
Default paths: In the default path /lib, and then /usr/lib. If the binary was linked with -z nodeflib linker option, this step is skipped.
ld-linux search algorithm
Also, note please, that if we are talking about shared libraries, they are not named .so but in .so.version format instead. When you build your application the linker will look for .so file (which is usually a symlink to .so.version) but when you run your application the dynamic loader looks for .so.version file instead. For example, let's say we have a library test which version is 1.1.1 according to semver. In the filesystem it will look like:
/usr/lib/libtest.so -> /usr/lib/libtest.so.1.1.1
/usr/lib/libtest.so.1 -> /usr/lib/libtest.so.1.1.1
/usr/lib/libtest.so.1.1 -> /usr/lib/libtest.so.1.1.1
/usr/lib/libtest.so.1.1.1
So, to be able to compile you must have all of versioned files (libtest.so.1, libtest.so.1.1 and libtest.so.1.1.1) and a libtest.so file but for running your app you must have only 3 versioned library files listed first. This also explains why do Debian or rpm packages have devel-packages separately: normal one (which consists only of the files needed by already compiled applications for running them) which has 3 versioned library files and a devel package which has only symlink file for making it possible to compile the project.
Resume
After all of that:
You, your colleague and EACH user of your application code must have all the libraries in their system linker paths to be able to compile (build your application). Otherwise, they have to change Makefile (or compile command) to add the shared library location directory by adding -L<somePathToTheSharedLibrary> as argument.
After successful build you also need your library again to be able to run the program. Your library will be searched by dynamic loader (ld-linux) so it needs to be in it's paths (see above) or in system linker paths. In most of linux program distributions, for example, games from steam, there is a shell-script which sets the LD_LIBRARY_PATH variable which points to all shared libraries needed by the game.
You could look at our Rblapi package which uses this very library too.
Your basic question of "how do I make a library visible" really has two answers:
Use ld.so. The easiest way is to copy blpapi3_64.so to /usr/local/lib. If you then call ldconfig to update the cache you should be all set. You can test this via ldconfig -p | grep blpapi which should show it.
Use an rpath instruction when building your application; this basically encodes the path and makes you independent of ld.so.

Force GCC to notify about undefined references in shared libraries

I have a shared library that is linked with another (third-party) shared library. My shared library is then loaded using dlopen in my application. All this works fine (assuming files are in the proper path etc).
Now, the problem is that I don't even need to specify to link against the third-party shared library when I link my library. GCC accept it without reporting errors about undefined references. So, the question; how can I force GCC to notify me about undefined references?
If I change my library to be (temporarily) an executable, I get undefined references (when not supplying the library to the linker). (Works fine if I specify it.)
I.e., the following is done:
g++ -fPIC -shared -o libb.so b.o
g++ -fPIC -shared -o liba.so a.o
g++ -o a.exe a.cpp
Where the second line does NOT give out an error and the third line complains about an undefined reference.
Sample code:
a.h:
class a
{
public:
void foobar();
};
a.cpp:
#include "a.h"
#include "b.h"
void a::foobar()
{
b myB;
myB.foobar();
}
int main()
{
a myA; myA.foobar();
}
b.h:
class b
{
public:
void foobar();
};
b.cpp:
#include "b.h"
void b::foobar()
{
}
-Wl,--no-undefined linker option can be used when building shared library, undefined symbols will be shown as linker errors.
g++ -shared -Wl,-soname,libmylib.so.5 -Wl,--no-undefined \
-o libmylib.so.1.1 mylib.o -lthirdpartylib
After more research, I realized what way the stuff works. There are two linker options to manipulate undefined symbols of shared libraries:
First one is --no-undefined. It reports the unresolved symbols that are not resolved immediately, at linking stage. Unless the symbol is found in a shared library linked against, either manually (with -l switch) or automatically (libgcc_s, C++ runtime; libc, C runtime; ld-linux-**.so, dynamic linker utils) picked, --no-undefined reports it as error. That's the key the questioner needed.
There is another key, --no-allow-shlib-undefined (whose description also suggests --no-undefined). It checks if definitions in the shared libraries which you link your shared library against are satisfied. This key is of little use in the case shown in this topic, but it can be useful. However, It has its own obstacles.
The manpage provides some rationale about why it's not default:
--allow-shlib-undefined
--no-allow-shlib-undefined
Allows (the default) or disallows undefined symbols in shared
libraries (It is meant, in shared libraries _linked_against_, not the
one we're creating!--Pavel Shved). This switch is similar to --no-un-
defined except that it determines the behaviour when the undefined
symbols are in a shared library rather than a regular object file. It
does not affect how undefined symbols in regular object files are
handled.
The reason that --allow-shlib-undefined is the default is that the
shared library being specified at link time may not be the same as
the one that is available at load time, so the symbols might actually
be resolvable at load time. Plus there are some systems, (eg BeOS)
where undefined symbols in shared libraries is normal. (The kernel
patches them at load time to select which function is most appropri-
ate for the current architecture. This is used for example to dynam-
ically select an appropriate memset function). Apparently it is also
normal for HPPA shared libraries to have undefined symbols.
The thing is that what is said above is also true, for example, for Linux systems, where some of the internal routines of the shared library is implemented in ld-linux.so, the dynamic loader (it's both executable and shared library). Unless you somehow link it, you will get something like this:
/lib64/libc.so.6: undefined reference to `_dl_argv#GLIBC_PRIVATE'
/lib64/libc.so.6: undefined reference to `_rtld_global_ro#GLIBC_PRIVATE'
/usr/lib64/gcc/x86_64-suse-linux/4.3/libstdc++.so: undefined reference to `__tls_get_addr#GLIBC_2.3'
/lib64/libc.so.6: undefined reference to `_rtld_global#GLIBC_PRIVATE'
/lib64/libc.so.6: undefined reference to `__libc_enable_secure#GLIBC_PRIVATE'
These are undefined references from the loader, ld-linux.so. It is platform-specific (for example, on my system the correct loader is /lib64/ld-linux-x86-64.so). You may link the loader with your library and check even the tricky references shown above:
g++ -fPIC -shared -o liba.so a.o -Wl,--no-allow-shlib-undefined /lib64/ld-linux-x86-64.so.2