I am facing a unique problem as part of our application migration from HP to AIX.
The following simulated code produces different results in HP and AIX.
library.C **
#include <stdio.h>
#include "mylib.h"
int libimgclientFNXXX()
{
int check = 100;
check = FileNetDeleteDoc(check);
return check;
}
int libimgclientFN()
{
int check = 1;
printf("In lib ");
return check;
}
* main_func.C *
#include <stdio.h>
int libimgclientFN();
int libimgclientFNXXX();
int main()
{
int one = 0;
if (1 == 1)
{
one = libimgclientFN();
}
printf("\n The status is %d \n", one);
}
* mylib.h **
extern int FileNetDeleteDoc (int);
Note that the function libimgclientFNXXX() is never called.
My make file is as below:
xlC -c -g library.C -o library.o -I./
xlC -G -qmkshrobj -o libImgClient.so library.o
xlC -c -g -qpic=small main_func.C -o main_func.o
xlC -brtl main_func.o -L. -lImgClient -o TST
When I run TST, I get the following loading error
$ TST
exec(): 0509-036 Cannot load program TST because of the following errors:
rtld: 0712-001 Symbol FileNetDeleteDoc__Fi was referenced
from module ./libImgClient.so(), but a runtime definition
of the symbol was not found.
Even though the function libimgclientFNXXX() is never called, there is unresolved errors.
The exact same code built in HP works fine with no errors.
Any inputs is appreciated.
Thanks,
Yeah, "not using" a library function may still be an error, even if you are not going to call the code. It MAY defer the loading of some componanent until later, so it MAY not cause an error. Best not to have references to things that don't exist (or manually load the library and get the address, if the function doesn't exist, you'll get an error from the "find the function" call and you can do something sensible in the code).
The loader (the code that loads binary executables) isn't very clever, so it can't know exactly what is being called and what isn't. It's also possible that different compilers have different levels of cleverness for "dead code removal", so the one compiler completely removes the "never called" function, but another compiler doesn't remove it [because it's not got the cleverness to 100% certify that you never call the function - in gcc for example, it would know this if you made the libimgclientFNXXX a static function - because it knows that static functions don't get called outside this module, and this module isn't using it.
AIX requires all symbols to resolve at load-time, so even though it builds OK, because the symbol is referenced the applications will not run.
You need to use lazy linking for the .so (the -blazy link option), which should cause the missing function to be linked only on first use.
You really should not be leaving undefined symbols in a library, though - if it needs symbols from another library, you should be linking to them (unless it's a plug-in, where the symbol is exposed in the app itself).
Related
I have a problem understanding, what exactly happens, when a dynamic library is loaded at runtime and how the dynamic linker recognizes and treats "same symbols".
I've read other questions related to symbolic linking and observed all the typical recommendations (using extern "C", using -fPIC when linking the library, etc.). To my knowledge, my specific problem was not discussed, so far. The paper "How to write shared libraries" https://www.akkadia.org/drepper/dsohowto.pdf does discuss the process of resolving library symbol dependencies, that may explain what's happening in my example below, but alas, it does not offer a workaround.
I found a post where the last (unfortunately) un-answered comment is very much the same as my problem:
Is there symbol conflict when loading two shared libraries with a same symbol
Only difference is: in my case the symbol is being an auto-generated constructor.
Here's the setup (Linux):
program "master" uses some library class declaration "Dummy" with 4 members variables and loads dynamically a shared library via dlopen() and resolves two simple functions with dlsym()
the shared library "slave" uses also the library with the class "Dummy", yet in a newer version with 5 member variables (extra string)
when the shared library's function is called from master, accessing the newly added string member in class Dummy segfaults - apparently the string wasn't initialized correctly
My assumption is: the constructor of class Dummy exists already in memory since master uses this function itself, and when loading the shared library it does not load its own version of the constructor, but simply re-uses the existing version from master. By doing that the extra string variable is not initialized correctly in the constructor, and accessing it segfaults.
When debugging into the assembler code when initializing the Dummy variable d in the slave, indeed Dummy's constructor inside the master's memory space is being called.
Questions:
How does the dynamic linker (dlopen()?) recognize, that the class Dummy used to compile the master should be the same as Dummy compiled into Slave, despite it being provided in the library itself? Why does the symbol lookup take the master's variant of the constructor, even though the symbol table must also contain the constructor symbol imported from the library?
Is there a way, for example by passing some suitable options to dlopen() or dlsym() to enforce usage of the Slave's own Dummy constructor instead of the one from Master (i.e. tweak the symbol lookup/reallocation behavior)?
Code: full minimalistic source code example can be found here:
https://bauklimatik-dresden.de/privat/nicolai/tmp/master-slave-test.tar.bz2
Relevant shared lib loading code in Master:
#include <iostream>
#include <dlfcn.h> // shared library loading on Unix systems
#include "Dummy.h"
int create(void * &data);
typedef int F_create(void * &data);
int destroy(void * data);
typedef int F_destroy(void * data);
int main() {
// use dummy class at least once in program to create constructor
Dummy d;
d.m_c = "Test";
// now load dynamic library
void *soHandle = dlopen( "libSlave.so", RTLD_LAZY );
std::cout << "Library handle 'libSlave.so': " << soHandle << std::endl;
if (soHandle == nullptr)
return 1;
// now load constructor and destructor functions
F_create * createFn = reinterpret_cast<F_create*>(dlsym( soHandle, "create" ) );
F_destroy * destroyFn = reinterpret_cast<F_destroy*>(dlsym( soHandle, "destroy" ) );
void * data;
createFn(data);
destroyFn(data);
return 0;
}
Class Dummy: the variant without "EXTRA_STRING" is used in Master, with extra string is used in Slave
#ifndef DUMMY_H
#define DUMMY_H
#include <string>
#define EXTRA_STRING
class Dummy {
public:
double m_a;
int m_b;
std::string m_c;
#ifdef EXTRA_STRING
std::string m_c2;
#endif // EXTRA_STRING
double m_d;
};
#endif // DUMMY_H
Note: if I use exaktly same class Dummy both in Master and Slave, the code works (as expected).
When debugging into the assembler code when initializing the Dummy variable d in the slave, indeed Dummy's constructor inside the master's memory space is being called.
This is expected behavior on UNIX. Unlike Windows DLLs, UNIX shared libraries are designed to imitate archive libraries, and are not designed to be self-contained isolated units of code.
How does the dynamic linker (dlopen()?) recognize, that the class Dummy used to compile the master should be the same as Dummy compiled into Slave, despite it being provided in the library itself? Why does the symbol lookup take the master's variant of the constructor, even though the symbol table must also contain the constructor symbol imported from the library?
The dynamic loader doesn't care (or know anything) about any classes. It operates of symbols.
By default symbols are resolved to the first definition of any given symbol which is visible to the dynamic loader (the exported symbol).
You can examine the set of symbols which are exported from any given binary with nm -CD Master and nm -CD libSlave.so.
Is there a way, for example by passing some suitable options to dlopen() or dlsym() to enforce usage of the Slave's own Dummy constructor instead of the one from Master (i.e. tweak the symbol lookup/reallocation behavior)?
There are several ways to modify the default behavior.
The best approach is to have libSlave.so use its own namespace. That will change all the (mangled) symbol names, and will completely eliminate any collisions.
The next best approach is to limit the set of symbols which are exported from libSlave.so, by compiling with -fvisibility=hidden and adding explicit __attribute__((visibility("default"))) to the (few) functions which must be visible from that library (create and destroy in your example).
Another possible approach is to link libSlave.so with -Wl,-Bsymbolic flag, thought the symbol resolution rules get pretty complicated really fast, and unless you understand them all, it's best to avoid doing this.
P.S. One might wonder why the Master binary exports any symbols -- normally only symbols referenced by other .sos used during the link are exported.
This happens because cmake uses -rdynamic when linking the main executable. Why it does that, I have no idea.
So another workaround is: don't use cmake (or at least not with the default flags it uses).
I followed the recommendations found in the last answer and Is there symbol conflict when loading two shared libraries with a same symbol :
running 'nm Master' and 'nm libSlave.so' showed the same automatically generated constructor symbols:
...
000000000000612a W _ZN5DummyC1EOS_
00000000000056ae W _ZN5DummyC1ERKS_
0000000000004fe8 W _ZN5DummyC1Ev
...
So, the mangled function signatures match in both the master's binary and the slave.
When loading the library, the master's function is used instead of the library's version. To study this further, I created an even more minimalistic example like in the post referenced above:
master.cpp
#include <iostream>
#include <dlfcn.h> // shared library loading on Unix systems
// prototype for imported slave function
void hello();
typedef void F_hello();
void printHello() {
std::cout << "Hello world from master" << std::endl;
}
int main() {
printHello();
// now load dynamic library
void *soHandle = nullptr;
const char * const sharedLibPath = "libSlave.so";
// I tested different RTLD_xxx options, see text for explanations
soHandle = dlopen( sharedLibPath, RTLD_NOW | RTLD_DEEPBIND);
if (soHandle == nullptr)
return 1;
// now load shared lib function and execute it
F_hello * helloFn = reinterpret_cast<F_hello*>(dlsym( soHandle, "hello" ) );
helloFn();
return 0;
}
slave.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void hello();
#ifdef __cplusplus
}
#endif
slave.cpp
#include "slave.h"
#include <iostream>
void printHello() {
std::cout << "Hello world from slave" << std::endl;
}
void hello() {
printHello(); // should call our own hello() function
}
You notice the same function printHello() exists both in the library and the master.
I compiled both manually this time (without CMake) and the following flags:
# build master
/usr/bin/c++ -fPIC -o tmp/master.o -c master.cpp
/usr/bin/c++ -rdynamic tmp/master.o -o Master -ldl
# build slave
/usr/bin/c++ -fPIC -o tmp/slave.o -c slave.cpp
/usr/bin/c++ -fPIC -shared -Wl,-soname,libSlave.so -o libSlave.so tmp/slave.o
Mind the use of -fPIC in both master and slave-library.
I now tried several combinations of RTLD_xx flags and compile flags:
1.
dlopen() flags: RTLD_NOW | RTLD_DEEPBIND
-fPIC for both libs
Hello world from master
Hello world from slave
-> result as expected (this is what I wanted to achieve)
2.
dlopen() flags: RTLD_NOW | RTLD_DEEPBIND
-fPIC for only the library
Hello world from master
Speicherzugriffsfehler (Speicherabzug geschrieben) ./Master
-> Here, a segfault happens in the line where the iostream libraries cout call is made; still, the printHello()s function in the library is called
3.
dlopen() flags: RTLD_NOW
-fPIC for only the library
Hello world from master
Hello world from master
-> This is my original behavior; so RTLD_DEEPBIND is definitely what I need, in conjunction with -fPIC in the master's binary;
Note: while CMake automatically adds -fPIC when building shared libraries, it does not generally do this for executables; here you need to manually add this flag when building with CMake
Note2: Using RTLD_NOW or RTLD_LAZY does not make a difference.
Using the combination of -fPIC on both executable and shared lib, with RTLD_DEEPBIND lets the original example with the different Dummy classes work without problems.
I am doing an example drill in the textbook I am using to learn from. All I need to do is compile, link and run the following 3 files:
//file my.h
extern int foo;
void print_foo();
void print(int);
my.h is a simple header file that declares the two functions and a 'global' int foo, with no initial value.
//file my.cpp
#include "my.h"
#include "std_lib_facilities.h" //not included but not source of error
void print_foo()
{
cout << foo << endl;
}
void print(int i)
{
cout << i << endl;
}
my.cpp contains the implementation of the functions included from my.h. std_lib_facilities.h is a file from the textbook, and is not the source of error (according to g++). I can edit it into the body of the question if needed.
//file use.cpp
#include "my.h"
#include <iostream>
int main() {
foo = 7;
print_foo();
print(99)
char cc; cin >> cc;
return 0;
}
use.cpp serves as the main implementation file in this program, and tries to use all three declared & defined objects.
I took the two step command approach to build using g++. First, I compiled both .cpp files:
g++ -c my.cpp use.cpp
which created two object files, my.o and use.o. I used the following command to link them:
g++ -o myprog my.o use.o
giving me this error:
Undefined symbols for architecture x86_64:
"_foo", referenced from:
print_foo() in my.o
_main in use.o
(maybe you meant: __Z9print_foov)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I have tried putting
int foo;
into my.h instead of
extern int foo;
which gave me the same error.
I have tried using the
-std=c++11
flag as well which resulted in the same error.
I am using a MacBook Pro with the latest macOS (just updated in fact), if that helps with interpreting the error message.
I have tried to initialize foo, which didn't change anything.
In addition, I have tried updating the command line tools, same error.
From what I understand, the error is telling me that, even though my.h is included in both files, neither one can actually implement any function using the foo variable (which it calls _foo), despite it being explicitly declared in my.h. My guess is that the linker is using the wrong names under the hood, which make it impossible to link into an executable. This comes from the fact that the error mentioned a
__Z9print_foov
which exists nowhere in any of the files.
It almost seems like a g++ or macOS/Command Line Tools bug at this point. I don't want to add the declarations each time, because that creates duplicate symbol errors anyway. Putting my.cpp and use.cpp into one file would probably link properly, but I need to make sure that I can actually link multiple cpp files, because I will eventually (hopefully) be working with multiple cpp files that need to be linked. Any help is appreciated!
Here you declare a variable:
extern int foo;
and you use the variable:
cout << foo << endl;
but you did not define the variable anywhere. The linker error says that the linker could not find the variable's definition. To fix this, put int foo; at file scope in one of the .cpp files.
In the question you say that changing extern int foo; to int foo; gives the same error. However if you look more carefully at the error message I think you will find that it gives a different one, about multiple definitions.
I suggest to compile in two commands g++ -Wall -c my.cpp (that gives a my.o) and g++ -Wall -c use.cpp (giving use.o), then link a program with g++ my.o use.o -o myprog. Actually you should write a Makefile (see this for inspiration) and simply run make
Your translation units my.cpp and use.cpp are both declaring some extern int foo; variable which is never defined. So you need to define it in one single file (but not in others!), probably by adding (into my.cpp alone for example)
int foo;
(without the extern) or even with some explicit initial value e.g. int foo = 34;
This comes from the fact that the error mentioned a __Z9print_foov which exists nowhere
It is a mangled name, which is referenced (but not defined) in both object files (see also this).
It almost seems like a g++ or macOS/Command Line Tools bug at this point
You are very unlikely to find bugs in compiler tools (both GCC & Clang/LLVM are extremely well tested; since they are multi-million lines free software, they do have residual bugs, but you have more chances to win at the lottery than to be affected by a compiler bug). I'm coding since 1974, and it happened to me only once in my lifetime. A more realistic attitude is to be more humble, and question your own code (and knowledge) before suspecting the compiler or build chain.
BTW, always compile first with all warnings and debug info (e.g. g++ -Wall -g and perhaps also -Wextra). Use the gdb debugger. When you are convinced that your code has no bugs, you might benchmark it by asking the compiler to optimize (so use g++ -Wall -O2 perhaps also with -g to compile).
Read also the linker wikipage. Dive into your C++ textbook (see also this site and the C++11 standard, e.g. n3337 draft) to understand the difference between declaring and defining some variable or function. You generally declare a global extern variable in some common header (included in several translation units), and define it once somewhere else, but the good practice is to avoid having lots of global variables. See also C++17 new inline variables.
Here is an small example I did with clang++ :
===filename===
calc_mean.cpp
===filename===
===filecontent===
double mean(double a, double b) {
return (a+b) / 2;
}
===filecontent===
===filename===
calc_mean.h
===filename===
===filecontent===
double mean(double, double);
===filecontent===
===filename===
commands.sh
===filename===
===filecontent===
#/usr/bin/env bash
clang++ -c calc_mean.cpp -o calc_mean.o
ar rcs libmean.a calc_mean.o
clang++ -c -fPIC calc_mean.cpp -o calc_mean.o
gcc -shared -W1,-soname,libmean.so.1 -o libmean.so.1.0.1 calc_mean.o
clang++ main.cpp -L. -lmean -o dynamicmain -v
===filecontent===
===filename===
main.cpp
===filename===
===filecontent===
#include <stdio.h>
#include "calc_mean.h"
int main(int argc, char const* argv[])
{
double v1, v2, m;
v1 = 5.0;
v2 = 6.0;
m = mean(v1, v2);
printf("Mean: %f\n", m);
return 0;
}
===filecontent===
It worked perfectly. Now turn to eclipse, I created a project with the dynamic lib generated above in the libs folder:
(source: p.im9.eu)
Adjusted -L and -l settings accordingly:
(source: p.im9.eu)
Got these errors:
(source: p.im9.eu)
Other things I have tried:
(source: p.im9.eu)
(source: p.im9.eu)
The errors stayed the same. I almost want to bang my head against a wall now. Should I start learning cmake already?
update
I added the header file also this time, but eclipse still can't resolve the function mean (through code analysis).
It compiles without an error though, but when I run the output binary, it says:
dyld: Library not loaded: libmean.so
Referenced from: /Users/kaiyin/personal_config_bin_files/workspace/testuselib/Debug/testuselib
Reason: image not found
Edit2:
It hit me that you're on Mac, and I remembered that there's something funny about library loading. So, there are a couple reasons why you'd get Image Not Found. The below still applies, but there's another reason it could be failing. See dyld: Library not loaded: libqscintilla2.5.dylib
I don't know if eclipse on Mac even ships with GCC, or if it's clang only on that platform, but try setting DYLD_LIBRARY_PATH as a quick test to see if it's just Mac Being Special. https://superuser.com/questions/282450/where-do-i-set-dyld-library-path-on-mac-os-x-and-is-it-a-good-idea
Edit:
Yay it compiles! Now we're hitting a linking error. This one is actually pretty fun, and isn't the "common" one I listed below (namely, Unresolved Symbols). This error, "Image Not Found" usually means that the Linker found the library, but could not use it because it was compiled in an incompatible manner.
Why is it in any incompatible format? Welcome to the one feature of C++ that I hate is missing, and one of the reasons pretty much every library out there provides a C interface instead of a C++ interface.
C++ Does Not Provide a stable ABI (Application Binary Interface). This means that libraries compiled with different compilers (or even just different versions of the same compiler may not work together. 99/100 they will just outright refuse to link/work, but even if they do link, you'll get very weird, hard-to-impossible to track down bugs, etc.
Here's the tl;dr: If you want your static lib to be C++ (which i recommend) and have a C++ interface, you need to make sure the exact same version of the compiler is used to compile both your application and the static library. The easiest way to do this is to have eclipse build both the static library and the application.
This is hopefully changing with the next version of C++, as Herb Sutter has put forward a proposal to create a platform defined C++ ABI.
Original:
You need to add the folder containing calc_mean.h to the "Additional Includes" for c++ generation. You can think of include statement as cutting and pasting the contents of the file at that exact line. The error is saying "hey, i went looking for a file called calc_mean.h and couldn't find it." You need to link the library and the header (so main.cpp knows the function)
If it was an error saying "unresolved symbols", with the symbols being in your library, then you would know you've messed up with adding the library or library path (-L).
Cmake is a good tool, but it is nice to know how to use an ide. The basic steps (add library name, add library path, add directory containing library headers) are the same in eclipse, netbeans, visual studio, xcode, etc)
My first post on this site with huge hope::
I am trying to understand static linking,dynamic linking,shared libraries,static libraries etc, with gcc. Everytime I try to delve into this topic, I have something which I don't quite understand.
Some hands-on work:
bash$ cat main.c
#include "printhello.h"
#include "printbye.h"
void main()
{
PrintHello();
PrintBye();
}
bash$ cat printhello.h
void PrintHello();
bash$ cat printbye.h
void PrintBye();
bash$ cat printbye.c
#include <stdio.h>
void PrintBye()
{
printf("Bye bye\n");
}
bash$ cat printhello.c
#include <stdio.h>
void PrintHello()
{
printf("Hello World\n");
}
gcc -Wall -fPIC -c *.c -I.
gcc -shared -Wl,-soname,libcgreet.so.1 -o libcgreet.so.1.0 *.o
ln -sf libcgreet.so.1.0 libcgreet.so
ln -sf libcgreet.so.1.0 libcgreet.so.1
So I have created a shared library.
Now I want to link this shared library with my main program to create an executable.
gcc -Wall -L. main.c -lcgreet -o greet
It very well works and if I set the LD_LIBRARY_PATH before running greet( or link it with rpath option) I can make it work.
My question is however different:
Since I am anyway using shared library, is it not possible to force symbol resolution at runtime (not sure about the terminology but perhaps called dynamic linking as per the book "Linkers and Loaders"). I understand that we may not want to do it because this makes the program run slow and has overhead everytime we want to run the program, but I am trying to understand this to clear my concepts.
Does gcc linker provide any option to delay symbol resolution at runtime? (to do it with the library we are actually going to run the program with)(as library available at compile time may be different than the one available at runtime if any changes in the library)
I want to be able to do sth like:
bash$ gcc main.c -I.
(what option needed here?)
so that I don't have to give the library name, and just tell it that I want to do symbol resolution at runtime, so headers are good enough for now, actual library names are not needed.
Thanks,
Learner For Ever.
Any linker (gcc, ld or any other) only resolves links at compile-time. That is because the ELF standard (as most others) do not define 'run-time' linkage as you describe. They either link statically (i.e. lib.a) or at start-up time (lib.so, which must be present when the ELF is loaded). However, if you use a dynamic link, the linker will only put in the ELF the name of the file and the symbols it must find, it does not link the file directly. So, if you want to upgrade the lib to a newer version later, you can do so, as long as system can find the same filename (the path can actually be different) and the same symbol names.
The other option, to get symbols at run-time, is to use dlopen, which has nothing to do with gcc or ld. dlopen simply put, opens a dynamic link library, just like fopen might, and returns you a handle, which then you pass to dlsym with the name of the symbol you want, which might be a function name for example. dlsym will then pass you a pointer to that symbol, which you can then use to call the function or use as a variable. This is how plugins are implemented.
I think you are looking for ld option '--unresolved-symbols=ignore-all', yes it can actually do it (ignore prev answer). Imagine scenario where a shared library loaded late (when program is already running), it can use all symbols that are already resolved/loaded by the main process, no need to bother to do it again . btw it does not nervelessly makes it slow , at least on Linux
I'm trying to write some small tests for a fairly small part of a fairly large project. Attempting to link this beast is unfortunately fairly impossible without linking the entire project together, which I don't want to do (it's a pretty complex system for finding all the dependencies and stuff, and I perfer not to meddle with it).
Now, I know for certain that the functions that the referenced functions won't be called during my test, the just happen to be part of functions which share file with stuff that I do test.
Is there any way to simply link these unresolved references to, let's say, abort, or something? Or is there a tool which creates the appropriate stub object file where all calls result in abort, given the set of object files that I have?
I use gcc (g++) for compiling/linking, version 3.4.4. Platform is unix (solaris/sparc if that's important).
You can just tell linker to ignore unresolved symbols. I couldn't find option that links them to abort or something like that.
The policy to ignore unresolved symbols in object files only is the most natural, I suppose:
gcc -Wl,--unresolved-symbols=ignore-in-object-files obj.o another.o etc.o
Other options include (quoting man ld):
--unresolved-symbols=method
Determine how to handle unresolved symbols. There are four possi-
ble values for method:
ignore-all
Do not report any unresolved symbols.
report-all
Report all unresolved symbols. This is the default.
ignore-in-object-files
Report unresolved symbols that are contained in shared
libraries, but ignore them if they come from regular object
files.
ignore-in-shared-libs
Report unresolved symbols that come from regular object files,
but ignore them if they come from shared libraries. This can
be useful when creating a dynamic binary and it is known that
all the shared libraries that it should be referencing are
included on the linker's command line.
The behaviour for shared libraries on their own can also be con-
trolled by the --[no-]allow-shlib-undefined option.
Normally the linker will generate an error message for each
reported unresolved symbol but the option --warn-unresolved-sym-
bols can change this to a warning.
On my Linux system attempts to call the unresolved function result in "Segmentation fault".
Trying to compile the following program
#include <iostream>
extern int bar();
int foo()
{
return bar() + 3;
}
int main()
{
std::cout << "Hello, world!" << std::endl;
// std::cout << foo() << std::endl;
return 0;
}
results in
$ g++ -o main main.cc
/tmp/ccyvuYPK.o: In function `foo()':
main.cc:(.text+0x5): undefined reference to `bar()'
collect2: ld returned 1 exit status
But we can tell the linker to ignore unresolved symbols and run it just fine:
$ g++ -Wl,--unresolved-symbols=ignore-all -o main main.cc
$ ./main
Hello, world!
Say some unresolved function is called by your test harness (simulate this by uncommenting the call to foo), it will compile and link fine, but you'll get a segfault when you execute the program. Be sure to ulimit -c unlimited so you get a core.
Well one way I can think of is to first compile the .o files for you library.
Then use a tool like nm (common on *nix systems) to get all of the symbols, in nm, all "external" (aka ones which are not found in this .o) are of type U (it may be different for non-GNU versions of nm see your documentation).
If your library is all one source file, then it is simple, pretty much all symbols of type U will be either a function found in another library or will be unresolved at link time. It is slightly more complicated if your library is going to be more than one source file since you will have inter-source file dependencies.
So now you have a a means to create a potential list of unresolved externals, then you can create a "test_stub.c" which has a stub symbols for each one, which you could fill with something like this:
void some_func() { abort(); }
where some_func is a would be unresolved external. Compile and link this with your library and all calls should result in an abort.
Try GCC alias attribute:
/* cannot directly alias to yet undefined symbols,
* so need an intermediate function.
*/
static void do_abort() { abort(); }
void func0() __attribute__ ((weak, alias ("do_abort")));
void func1() __attribute__ ((weak, alias ("do_abort")));
...