Using 'LIBS' in scons 'Program' command failed to find static library, why? - linux

I've got a 'n.c' as main function, and 'o.c' as import function, like below:
$ cat n.c o.c
int f();
int main(){
f();
return 0;
}
#include<stdio.h>
int f(){
printf("hello\n");
return 2;
}
Then scons file like below:
Library('o.c')
Program('n.c',LIBS=['o'])
What I hope here is to compile o.c and generate libo.a(OK), and n.c will use this '.a' to generate final executable. So I specified LIBS=['o'], in hoep that it will specify an archive file to find libo.a library. But:
$ scons -Q
gcc -o n n.o -lo
/usr/bin/ld: cannot find -lo
collect2: error: ld returned 1 exit status
scons: *** [n] Error 1
Actually, scons interpreted my command to be '-lo', which is to find a dynamic shared library.
This is not what I wanted, because during linking, archive is used like object files. Does '-l' work with archive files, and why scons interprets LIBS to use dynamic link shared libraries?
Thanks.

You also need to specify the path where to search for libraries, in this case:
Program('n.c',LIBS=['o'], LIBPATH=['.'])
Please also check chapter 4 "Building and Linking with Libraries" of our UserGuide, which does not only explain how to create and work with Libraries, it further states that your claim from above "SCons interprets LIBS to use dynamic link shared libraries" is plain wrong. Otherwise the object files would end with *.os instead...

Related

why linux rpath only take affect on some library

I link my program with -Wl,-rpath=./
but it failed to search the library. the debug shows run path only take affect on some library. log:
find library=libepsverifier.so [0]; searching
search path=./tls/x86_64/x86_64:./tls/x86_64:./tls/x86_64:./tls:./x86_64/x86_64:./x86_64:./x86_64:. (RUNPATH from file ./sim)
trying file=./tls/x86_64/x86_64/libepsverifier.so
trying file=./tls/x86_64/libepsverifier.so
trying file=./libepsverifier.so
find library=libc.so.6 [0]; searching
search path=./tls/x86_64/x86_64:./tls/x86_64:./tls/x86_64:./tls:./x86_64/x86_64:./x86_64:./x86_64:. (RUNPATH from file ./sim)
trying file=./tls/x86_64/x86_64/libc.so.6
...
trying file=./x86_64/libc.so.6
trying file=./libc.so.6
search cache=/etc/ld.so.cache
trying file=/lib/x86_64-linux-gnu/libc.so.6
find library=libepsbase.so [0]; searching
search cache=/etc/ld.so.cache
search path=/lib/x86_64-linux-gnu/tls/x86_64/x86_64:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/x86_64/x86_64:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/x86_64/x86_64:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/x86_64/x86_64:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/x86_64/x86_64:/lib/tls/x86_64:/lib/tls/x86_64:/lib/tls:/lib/x86_64/x86_64:/lib/x86_64:/lib/x86_64:/lib:/usr/lib/tls/x86_64/x86_64:/usr/lib/tls/x86_64:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/x86_64/x86_64:/usr/lib/x86_64:/usr/lib/x86_64:/usr/lib (system search path)
trying file=/lib/x86_64-linux-gnu/tls/x86_64/x86_64/libeps
。。。
trying file=/usr/lib/x86_64/libepsbase.so
trying file=/usr/lib/x86_64/libepsbase.so
trying file=/usr/lib/libepsbase.so
./sim: error while loading shared libraries: libepsbase.so: cannot open shared object file: No such file or directory
why libepsverifier is searched in .
but linker don't search libepsbase under .
I am not entirely sure on what you want to achieve, honestly I never tried rpath in the way you are trying to use it. Am I right if your expect the compiler to scan the entire file system for it? This is not what it is meant for.
Probably the other libraries are found because they are referenced in the LD_LIBRARY_PATH of your environment or because they are in locations recognized by your environment as "the place where libraries are".
The sequence should be
DT_RPATH section of the executable
LD_LIBRARY_PATH
DT_RUNPATH section of the executable
/etc/ld/so/cache
default directories /lib then /usr/lib
To have a proper test, either include the path of the library in the LD_LIBRARY_PATH or pass the rpath with a full path of the library. If it works, this is the reason why it fails.
You should use the same -Wl,-rpath=/some/path option when compiling a shared library if you want that path to be also used when searching the shared libraries that library itself depends upon.
Example:
$ echo 'void bar(){printf("bar\n");}' > bar.c
$ echo 'extern void bar(); void foo(){bar();}' > foo.c
$ echo 'extern void foo(); int main(){foo();}' > main.c
$ mkdir -p dir
$ cc bar.c -shared -o dir/libbar.so -include stdio.h
$ cc foo.c -Ldir -lbar -shared -o dir/libfoo.so
$ cc main.c -Ldir -lfoo -o main -Wl,-rpath=./dir
$ ./main
./main: error while loading shared libraries: libbar.so: cannot open shared object file: No such file or directory
$ cc foo.c -Ldir -lbar -shared -o dir/libfoo.so -Wl,-rpath=./dir
#^^^^^^^^^^^^^^^^
$ ./main
bar

linux gcc linking, duplicate symbols? [duplicate]

Is there any way we can get gcc to detect a duplicate symbol in static libraries vs the main code (Or another static library ?)
Here's the situation:
main.c erroneously contained a function definition, e.g. with the signature uint foohash(const char*)
foo.c also contains a function definition with the signature uint foohash(const char*)
foo.c and other source files are compiled to a static util library, which the main program links in, i.e. something like:
gcc -o main main.o util.o -L ./libs -lfooutils
So, now main.o and libs/libfooutils.a both contain a foohash function. Presumably the linker found that symbol in main.o and doesn't bother looking for it elsewhere.
Is there any way we can get gcc to detect such a situation ?
Indeed as Simon Richter stated, --whole-archive option can be useful. Try to change your command-line to:
gcc -o main main.o util.o -L ./libs -Wl,--whole-archive -lfooutils -Wl,--no-whole-archive
and you'll see a multiple definition error.
gcc calls the ld program for linking. The relevant ld options are:
--no-define-common
--traditional-format
--warn-common
See the man page for ld. These should be what you need to experiment with to get the warnings sought.
Short answer: no.
GCC does not actually do anything with libraries. It is the task of ld, the linker (called automatically by GCC) to pull in symbols from libraries, and that's really a fairly dumb tool.
The linker has lots of complex jiggery pokery for combining different types of data from different sources, and supporting different file formats, and all the evil little details of binary executables, but in the end, all it really does is look for undefined symbols and find the definitions.
What you can do is a link trace (pass -t to gcc) to see what comes from where. Or else run nm on all the object files and libraries in your system, and write a script to detect duplicates.

Building a simple (hello-world-esque) example of using ld's option -rpath with $ORIGIN

Note: Full working example now below. Original question follows:
I'm having problems using ld's -rpath parameter with $ORIGIN.
As I couldn't find a complete example, I thought I'd try to write one myself, so that I and others can use it later. Once I get it working I'll tidy it up.
I asked about this before, but I think my post was a bit confusing.
The example project builds one shared library and one executable that links to said library.
It's very small (3 files, 22 lines incl buildscript).
You can download the project from here
File structure (before building):
project/
src/
foo.cpp
main.cpp
make.sh
project/src/foo.cpp
int foo()
{ return 3; }
project/src/main.cpp
int foo();
#include <iostream>
int main()
{
std::cout << foo() << std::endl;
return 0;
}
project/make.sh
# Make directories:
mkdir -p -v obj
mkdir -p -v lib
mkdir -p -v run
# Build the library:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/foo.sh obj/foo.o
# Build the executable:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../../lib' -Llib -l:foo.sh
From the project directory, run make.sh (make sure it's executable).
File structure (after building):
project/
src/
foo.cpp
main.cpp
obj/
foo.o
main.o
lib/
foo.so
run/
main.run
make.sh
run/main.run should now load lib/foo.sh on execution, from anywhere.
Problems
Currently, this only partly works.
The files compile and link OK, but it fails to link when run from any directory except project (which is the point of the exercise).
Inspecting main.run with readelf -d shows:
0x0000000000000001 (NEEDED) Shared library: [lib/foo.sh]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../../lib]
Which looks close (I'd rather have [foo.sh] than [lib/foo.sh] but I'll fix that later).
AFAICT the $ORIGIN in -Wl,-rpath,'$ORIGIN/../../lib' means project/run/main.run so this rpath should become project/lib.
I have tried $ORIGIN/.., $ORIGIN/../lib, $ORIGIN/../.., $ORIGIN/../../lib to no avail.
Note: I'm using -l: which requires the complete library filename (amongst other reasons, it's easier to script with variables when all functions take the same name format).
Does anyone know why this isn't working?
Or alternately, does anyone have or know of a complete working example?
(I'd rather have [foo.sh] than [lib/foo.sh] but I'll fix that later).
There's most of your problem: the / in the name stops the dynamic linker from doing the rpath magic.
(Your rpath is wrong too. Think about it: from the shell, if you were currently in the directory where your executable is, how would you get to the directory where your library is? Here, you'd need to cd ../lib. So your rpath should be $ORIGIN/../lib.)
If you built your object as libfoo.so and linked with -Llib -lfoo, the linker would work out what you were intending, and do the right thing. But if you're going to use unusual naming conventions, you'll have to help it out:
Change the link line for the library to explicitly set the SONAME for your library to just foo.sh:
g++ -shared -Wl,-soname,foo.sh -o lib/foo.sh obj/foo.o
Fix the rpath:
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib' -Llib -l:foo.sh
It's useful to run ldd main/main.run to see what's going on. In your original failing case, you'll see something like:
lib/foo.sh (0xNNNNNNNN)
(the lack of any => /some/resolved/path showing that it's not done any path resolution). In the fixed case, you'll see something like:
foo.sh => /your/path/to/run/../lib/foo.sh (0xNNNNNNNN)
This is a example of relative-path linking (with ld) by using $ORIGIN in an rpath.
rpath is a path (or set of paths) embedded in binary files (shared libraries (.so) and executables).
These paths are the foremost search paths for the shared libraries the binary must be linked with at runtime.
$ORIGIN is a potential start directory for an rpath path.
It resolves to the directory containing the executing file. (eg: $ORIGIN/lib)
The example project builds one shared library and one executable that links to said library using rpath and $ORIGIN.
You can download the project from here.
File structure (before building):
project/
src/
foo.cpp
main.cpp
make.sh
project/src/foo.cpp
int foo()
{ return 3; }
project/src/main.cpp
int foo();
#include <iostream>
int main()
{
std::cout << foo() << std::endl;
return 0;
}
project/make.sh
# Make directories:
mkdir -p -v obj
mkdir -p -v lib/dir
mkdir -p -v run
# Build the library:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so -Wl,-soname,foo.so obj/foo.o
# Build the executable:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Llib/dir -l:foo.so
From the project directory, run make.sh (if it won't run, ensure make.sh has execute permissions).
If all went OK, main.run should now load lib/dir/foo.so on execution, regardless of the absolute path to project (you can move it to anywhere), and regardless of the current working directory (you can run it from anywhere).
Notes:
-fPIC instructs the compiler to build relocatable object files (object files build into shared libraries must be relocatable).
-Wl,-soname,<NAME> embeds <NAME> into the generated library. This should match the name you supply for the -l or -l: options when linking to this library.
-Wl,-rpath,'<PATH>' embeds <PATH> into the generated library as a runtime library search path (or rpath - see above).
-L adds a path to the build-time library search path list. (Note: rpath is irrelevant at build-time, -L is irrelevant at runtime).
-l: adds the filename (without path) of a library to link against. (Similar to -l, except -l: requires the complete filename.
File structure (after building):
project/
src/
foo.cpp
main.cpp
obj/
foo.o
main.o
lib/
dir/
foo.so
run/
main.run
make.sh
Note: I'm using -l: which requires the complete library filename (amongst other reasons, it's easier to script with variables when all functions take the same name format).
It is more common to use -l, whereby -l<NAME> denotes lib.so.
Limitations
As far as I'm aware (correct me if I'm wrong) there's no way to add a library inside a subdirectory within a search path (except for adding that directory as a sub-path). This is true for both build-time (-L) and run-time (-rpath) search paths.
So if you have two libraries with the same name but different locations, you won't be able to link them both. (I hope I'm wrong or that this gets fixed).

Symbols from convenience library not getting exported in executable

I have a program, myprogram, which is linked with a static convenience library, call it libconvenience.a, which contains a function, func(). The function func() isn't called anywhere in myprogram; it needs to be able to be called from a plugin library, plugin.so.
The symbol func() is not getting exported dynamically in myprogram. If I run
nm myprogram | grep func
I get nothing. However, it isn't missing from libconvenience.a:
nm libconvenience/libconvenience.a | grep func
00000000 T func
I am using automake, but if I do the last linking step by hand on the command line instead, it doesn't work either:
gcc -Wl,--export-dynamic -o myprogram *.o libconvenience/libconvenience.a `pkg-config --libs somelibraries`
However, if I link the program like this, skipping the use of a convenience library and linking the object files that would have gone into libconvenience.a directly, func() shows up in myprogram's symbols as it should:
gcc -Wl,--export-dynamic -o myprogram *.o libconvenience/*.o `pkg-config --libs somelibraries`
If I add a dummy call to func() somewhere in myprogram, then func() also shows up in myprogram's symbols. But I thought that --export-dynamic was supposed to export all symbols regardless of whether they were used in the program or not!
I am using automake 1.11.1 and gcc 4.5.1 on Fedora 14. I am also using Libtool 2.2.10 to build plugin.so (but not the convenience library.)
I didn't forget to put -Wl,--export-dynamic in myprogram_LDFLAGS, nor did I forget to put the source that contains func() in libconvenience_a_SOURCES (some Googling suggests that these are common causes of this problem.)
Can somebody help me understand what is going on here?
I managed to solve it. It was this note from John Calcote's excellent Autotools book that pointed me in the right direction:
Linkers add to the binary product every object file specified explicitly on the command line, but they only extract from archives those object files that are actually referenced in the code being linked.
To counteract this behavior, one can use the --whole-archive flag to libtool. However, this causes all the symbols from all the system libraries to be pulled in also, causing lots of double symbol definition errors. So --whole-archive needs to be right before libconvenience.a on the linker command line, and it needs to be followed by --no-whole-archive so that the other libraries aren't treated that way. This is a bit difficult since automake and libtool don't really guarantee keeping your flags in the same order on the command line, but this line in Makefile.am did the trick:
myprogram_LDFLAGS = -Wl,--export-dynamic \
-Wl,--whole-archive,libconvenience/libconvenience.a,--no-whole-archive
If you need func to be in plugin.so, you should try and locate it there if possible. Convenience libraries are meant to be just that -- a convenience to link to an executable or lib as an intermediate step.

gcc compiled binaries give "cannot execute binary file"

I compile this program:
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
}
With this command:
gcc -c "hello.c" -o hello
And when I try to execute hello, I get
bash: ./hello: Permission denied
Because the permissions are
-rw-r--r-- 1 nathan nathan 856 2010-09-17 23:49 hello
For some reason??
But whatever... after changing the permissions and trying to execute again, I get
bash: ./hello: cannot execute binary file
I'm using gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
What am I doing wrong here? It's gotta be obvious... it's just too late for me to keep using my tired eyes to try and figure out this simple problem....
P.S. I do (sometimes) work on programs more sophisticated than Hello World, but gcc is doing this across the board...
Take the -c out. That's for making object files, not executables.
The -c flag tells it not to link, so you have an object file, not a binary executable.
In fact, if you ran this without the -o flag, you would find that the default output file would be hello.o.
For reference (and giggles), the man entry on the -c flag:
-c Compile or assemble the source files, but do not link. The linking stage simply is not done.
The ultimate output is in the form of an object file for each source file.
By default, the object file name for a source file is made by replacing the suffix .c, .i, .s,
etc., with .o.
Unrecognized input files, not requiring compilation or assembly, are ignored.
Compile with: gcc hello.c -o hello

Resources