versioned symbols with Linux ld-linux.so - linux

I am trying to understand how ld-linux.so resolves references to versioned symbols on Linux. I have the following files:
test.c:
void f();
int main()
{
f();
}
a.c and b.c:
void f() {}
symbols.txt:
ABC {
global:
*;
};
Makefile:
all: liba.so libb.so test
liba.so: a.c
gcc -g -shared $^ -o $# -Wl,--version-script=symbols.txt
libb.so: b.c
gcc -g -shared $^ -o $#
test: test.c liba.so
gcc -g test.c -la -L. -o $#
clean:
rm -f liba.so libb.so test
I then ran the following command
LD_PRELOAD=./libb.so LD_LIBRARY_PATH=. ./test
I find that f() from b.c is invoked even though the symbol f in libb.so does not have the version required by test (f#ABC). Why does this happen ?

version-script is used to confine the symbols to be exported in shared libraries. this means faster link speed and few change for symbols conflict.
By default, most function name will be exported, so you can link with libb.so without any problem.
If you want to determine which version of function should be used, You need to specify it in your program. This need some assembly code to specify .symver.
For more details, please read Ulrich Drepper’s paper https://www.akkadia.org/drepper/dsohowto.pdf

Related

Linking issue in Cross Compilation for arm in linux using aarch64-linux-gnu-

I got an error when try to link (-aarch64-linux-gnu-ld) (the script containing the Makefile was downloaded from https://github.com/Icenowy/aw-el2-barebone). Error is "aarch64-linux-gnu-ld: Error: unable to disambiguate: -nostartfiles (did you mean --nostartfiles ?)
make: *** [Makefile:31: el2-bb.elf] Error 1" How to recode the line 31? of the Makefile
CROSS_COMPILE = /usr/bin/aarch64-linux-gnu-
DEBUG = 1
CC = $(CROSS_COMPILE)gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
INCLUDES = -I ./include -I ./uboot-headers -ffreestanding
DEFINES = -DSOC_HEADER="\"h6.h\""
ifneq ($(DEBUG),1)
DEFINES += -DNDEBUG
endif
ASFLAGS = $(INCLUDES) $(DEFINES) -D__ASSEMBLY__
CFLAGS = $(INCLUDES) $(DEFINES) -O2
LDSCRIPT = ldscripts/a64.ld
LDSCRIPTS = ldscripts/a64.ld ldscripts/common.ld
LDFLAGS = -nostdlib -nostartfiles -static -T $(LDSCRIPT)
OBJS = start.o init.o uart.o stack.o exceptions.o exception_funcs.o panic.o pgtables.o trapped_funcs.o
all: el2-bb.bin
el2-bb.elf: $(OBJS) $(LDSCRIPTS)
$(LD) $(LDFLAGS) $(OBJS) -o $#
el2-bb.bin: el2-bb.elf
$(OBJCOPY) -O binary $< $#
.PHONY: clean
clean:
rm -f el2-bb.* *.o
-nostartfiles is a gcc option but not an ld option.
If you use gcc to invoke the linker (say with LD=gcc in your Makefile) for making a standalone program that does not use standard startup files, you should use -nostartfiles because gcc links the startup files by default and -nostartfiles disables this.
ld doesn't links any startup files by default, so there is no option to disable linking them. You always pass startup files explicitly to ld. If you don't want startup files, just don't pass them to ld.
In order to see what startup files on your system are create an empty C program:
int main(){}
and compile it:
gcc -c empty.c
gcc -v empty.o
You are likely to see an invocation of ld (or perhaps of collect2, which calls ld) with a long, long list of options and object files.
Now run
gcc -nostartfiles -v empty.o
The .o files are now gone. That's exactly what -nostartfiles does.
When you invoke ld empty.o, these files and options are not there to begin with. In order to make a working program for say a Linux system, you need to pass most of them to ld explicitly. If you are building a program for something other system, you may not need some or all of them. So just don't pass them in.

Exclude dynamic dependencies from build command?

Let's suppose we have a library libutils.so:
ldd libutils.so
...
libdependency.so
...
Let's further suppose that we need to build an application:
g++ appliation.cpp -lutils -o application
May we omit -ldependency in the above command or must we write:
g++ appliation.cpp -lutils -ldependency -o application
May we omit -ldependency in the above command
If you control the linkage of libutils.so itself, yes you can. An illustration:
main.c
extern void foo(void);
int main(void)
{
foo();
return 0;
}
foo.c
extern void bar(void);
void foo(void)
{
bar();
}
bar.c
#include <stdio.h>
void bar(void)
{
puts(__func__);
}
We'll make a program that depends on libfoo.so, which depends on libbar.so.
Make the object files:
$ gcc -Wall -c -fPIC foo.c bar.c
gcc -Wall -c main.c
Now link libbar.so the No Frills way:
$ gcc -shared -o libbar.so bar.o
Next link libfoo.so like this:
$ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)
The effect of the -rpath linker option is:
-rpath=dir
Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects.
All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime.
The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link;
see the description of the -rpath-link option. If -rpath is not used when linking an ELF executable,
the contents of the environment variable LD_RUN_PATH will be used if it is defined.
The result is that:
$ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
NEEDED libbar.so
RUNPATH /home/imk/develop/so/scrap
libfoo.so has a NEEDED entry inscribed in its .dynamic section saying that
the library has a runtime dependency on libbar.so. Likewise it has
a RUNPATH entry there saying that runtime dependencies may be searched for in /home/imk/develop/so/scrap
That's just the pwd where I did the linkage: it doesn't have to be that, as long as is indeed
a directory where libbar.so can be found when the linker or loader comes looking for it.
This information can be read by the linker, when libbar.so is linked with something else,
and by the loader at runtime. So finally I can link prog like this:
$ gcc -o prog main.o -L. -lfoo -Wl,-rpath=$(pwd)
I don't need to mention -lbar, because libfoo.so itself provides the linker with
the information that libfoo.so depends on libbar.so, and where to look for it.
Since I also passed -rpath=$(pwd) in the linkage of prog, we see that prog
will provide this information
$ objdump -x -j .dynamic prog | egrep '(RUNPATH|NEEDED)'
NEEDED libfoo.so
NEEDED libc.so.6
RUNPATH /home/imk/develop/so/scrap
to the runtime loader: prog needs libfoo.so, and it can be looked for
in /home/imk/develop/so/scrap. When the loader finds libfoo.so and loads it, it will
discover from it that:
NEEDED libbar.so
RUNPATH /home/imk/develop/so/scrap
and will in turn find and load libbar.so, which will enable it to resolve all
symbols referred to in the process under construction. Consequently, prog can be run immediately:
$ ./prog
bar
I didn't have to pass -rpath=$(pwd) in the linkage of prog. But if I hadn't:
$ gcc -o prog main.o -L. -lfoo
$ ./prog
./prog: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
the loader wouldn't know where to find libfoo.so. See:
$ ldd prog
linux-vdso.so.1 (0x00007ffffcc35000)
libfoo.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d1aff9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4d1b5ec000)
And then I'd have to resort to:
$ export LD_LIBRARY_PATH=.
$ ldd prog
linux-vdso.so.1 (0x00007fff964dc000)
libfoo.so => ./libfoo.so (0x00007fc2a7f35000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc2a7b44000)
libbar.so => ./libbar.so (0x00007fc2a7942000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc2a8339000)
$ ./prog
bar
Later
it is still a little bit unclear whether the presence of libdependency.so among the output of ldd libutils.so is enough to omit -ldependencny during linkage
You would need to ask at least one and at most two questions about the output of ldd utils.so:-
Does the ldd libutils.so output report the so-name libdependency.so at all?
If Yes to 1, does it also resolve that so-name to an actual file?
If No to 1 then libdutils.so contains no informatation about its dependency of libdependency.so
and you must specify -lutils -ldependency in any further linkage.
If Yes to 1 but No to 2 (i.e.ldd libutils.so reports libdependency.so => not found) then libutils.so has
a NEEDED entry for the so-name libdependency.so but not a RUNPATH entry by which the linker or
loader can resolve that so-name to any actual file. In that case again, you must link -lutils -ldependency if you link -lutils, so that the linker will then search for a file that resolves -ldependency. At least, you must do so as long as ldd libutils.so still reports libdependency.so => not found when you do the linkage. Read on...
If Yes to 1 and Yes to 2 then you can drop -ldependency in a further linkage provided it is
run in the same environment in which you ran ldd libutils.so
That caveat is needed because if ldd libutils.so resolves libdependency.so, all you know
is that ldd was able to resolve libdependency.so using the loader's search algorithm:-
The LD_LIBRARY_PATH environment variable (in the active shell), lists a directory
in which libdependency.so is found, or
libutils.so provides a RUNPATH in which libdependency.so is found, or
libdependency.so is found in one of the directories listed in /etc/ld.so.conf (or the recursive include-expansion thereof), or
libdependency.so is found in one of the loader's trusted search directories, /lib and /usr/lib
If ldd can resolve libdependency.so in one of those four ways, then the linker will be able
to do it the same way, as long as that way still succeeds when you do the linkage.
So going back to my example, and my linkage:
$ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)
After that, thanks to -rpath=$(pwd). I can link prog like:
$ gcc -o prog main.o -L. -lfoo
without mentioning -lbar, and it succeeds. Now I link libfoo.so instead without
an -rpath:
$ gcc -shared -o libfoo.so foo.o -L. -lbar
after which:
$ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
NEEDED libbar.so
there's no RUNPATH anymore, and consequently:
$ ldd libfoo.so
linux-vdso.so.1 (0x00007ffda05e6000)
libbar.so => not found
because the loader can't resolve libbar.so in any other way either.
Now I can no longer link prog without -lbar:
$ gcc -o prog main.o -L. -lfoo
/usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
./libfoo.so: undefined reference to `bar'
But if I do:
$ export LD_LIBRARY_PATH=$(pwd)
then:
$ ldd libfoo.so
linux-vdso.so.1 (0x00007ffe56d1e000)
libbar.so => /home/imk/develop/so/scrap/libbar.so (0x00007fd2456e8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd2452f7000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd245aec000)
libfoo.so's dependency libbar.so is resolved by the loader, using the LD_LIBRARY_PATH, and in the
same way by the linker:
$ gcc -o prog main.o -L. -lfoo; echo Done
Done
And if I clear LD_LIBRARY_PATH again:
$ unset LD_LIBRARY_PATH
$ gcc -o prog main.o -L. -lfoo; echo Done
/usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
./libfoo.so: undefined reference to `bar'
collect2: error: ld returned 1 exit status
Done
back to failure.

What is the equivalent of #loader_path for rpath specification on linux?

On osx loader, #loader_path resolves to the position of the generic binary object, and #executable_path to the position of the executable. On Linux, apparently there's only $ORIGIN, which resolves to the executable path. Is there a hidden feature in the linux loader to specify a dynamic search path for a generic ELF object? Or maybe $ORIGIN behaves differently for so objects?
Linux also has $LIB and $PLATFORM, but they don't provide what I need.
$ORIGIN is the location of the object being loaded, so it is different in the executable and shared libraries loaded by the executable.
Edit: Here's a small test I performed to check:
~$ mkdir /tmp/tests
~$ cd /tmp/tests
tests$ mkdir good bad
tests$ gcc -fPIC -shared -o good/libtest.so -Wl,-rpath,\$ORIGIN -x c - <<< 'int puts(const char*); void foo() { puts("good"); }'
tests$ gcc -fPIC -shared -o bad/libtest.so -Wl,-rpath,\$ORIGIN -x c - <<< 'int puts(const char*); void foo() { puts("bad"); }'
tests$ gcc -fPIC -shared -o good/libtest2.so -Wl,-rpath,\$ORIGIN -x c - -ltest -Lgood <<< 'void foo(); void bar() { foo(); }'
tests$ gcc -o bad/a.out good/libtest2.so -x c - -Wl,-rpath,\$ORIGIN -Wl,-rpath-link,good <<< 'void bar(); int main() { bar(); }'
tests$
tests$ readelf -d bad/* good/* | grep RPATH
0x000000000000000f (RPATH) Library rpath: [$ORIGIN]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN]
tests$
tests$ ldd bad/a.out
linux-vdso.so.1 => (0x00007faf2f295000)
good/libtest2.so (0x00007faf2f092000)
libc.so.6 => /lib64/libc.so.6 (0x0000003949800000)
libtest.so => /tmp/tests/good/libtest.so (0x00007faf2ee66000)
/lib64/ld-linux-x86-64.so.2 (0x0000003949400000)
tests$ bad/a.out
good
I think that demonstrates it works, everything has RPATH=$ORIGIN, the executable is explicitly linked to libtest2.so, which picks up libtest.so in its own directory not the executable's.
Using LD_DEBUG=libs bad/a.out shows:
[...]
17779: find library=libtest.so [0]; searching
17779: search path=/tmp/tests/good/tls/x86_64:/tmp/tests/good/tls:/tmp/tests/good/x86_64:/tmp/tests/good (RPATH from file good/libtest2.so)
[...]
i.e. when looking for the libtest.so dependency of good/libtest2.so the search path uses the RPATH from good/libtest2.so, which expands to /tmp/tests/good which is the $ORIGIN from good/libtest2.so not the $ORIGIN of the executable.
The linux loader searches in following order:
DT_RPATH (specified on gcc command line, ignored si DT_RUNPATH exist)
LD_LIBRARY_PATH (specified in the environment)
DT_RUNPATH (specified on gcc command line)
ld.so.conf (for directories specified on lines and compiled with ldconfig)
/lib and /usr/lib

undefined reference while linking with a shared object

I'm a dumb newbie.
I've got a file named file.c with the functions my_putstr(char *) and my_strlen(char *)
my_putstr() writes the parameter with write() (unistd.h)
I wanted to create a library from file.c so I did :
gcc -fPIC -c file.c
gcc -shared -o libfile.so file.o
Then I created a main.c file and called my_putstr() from it.
I tried to compile and link my .so
gcc -L. -lfile main.c -o test
But I got an undefined reference to my_putstr()
I tried to create a .h with my_putstr() and my_strlen() in it, and include it to the main but I got the same error.
Sorry for stupid questions.
Havaniceday.
Your question suffers lack of information, but I can suggest you at first try
gcc main.c ./libfile.so -Wl,-rpath . -o test
If this will fail, you have something wrong with your sources.
If everything is ok at this point, then try
gcc main.c -L . -lfile -Wl,-rpath . -o test
If this will output undefined reference, then probably you already have something like libfile.a without my_putstr(may be from previous experiments) in your lib path.
If everything is ok with it, then your linker is sensible to order in which libraries is supplied to command string, and you must remember, then library always comes after object, that uses this library.

How to ignore undefined symbols at runtime in Linux?

I know that by default undefined symbols are ignored at compile time. However, I would also like them to be ignored at run-time. I need to distribute a .so that will run with and without MPI. I will know ahead of time if it is an MPI job and if it is not I won't make any MPI_* calls. If it's not an MPI run I need the application to not care that it cannot resolve the MPI_* symbols.
Is this possible? I could have sworn I've done this before but I can't get it working. Everytime I run I immediately get the following even though the logic in my code will never allow that symbol to be referenced:
undefined symbol: hpmp_comm_world
For what it's worth I am using the Intel Fortran Compiler to build the .so file.
EDIT
I found the linker flag: "-z lazy" which is supposed to resolve references to functions when the function is called which is what I want. That doesn't fix my problem, but hpmp_comm_world is a variable - not a function. Should that make a difference?
You can define a symbol to be a weak reference to its definition. Then, the symbol's value will be zero if the definition is not present.
For example, suppose the following is ref.c, which references a function and variable that may or may not be present; we'll use it to build libref.so (corresponding to your library, in your question):
#include <stdio.h>
void global_func(void);
void global_func(void) __attribute__ ((weak));
extern int global_variable __attribute__((weak));
void ref_func() {
printf("global_func = %p\n", global_func);
if (&global_variable)
global_variable++;
if (global_func)
global_func();
}
Here, global_func and global_variable are the weak references to the possibly-available function and variable. This code prints the function's address, increments the variable if it is present, and calls the function if it is present. (Note that the function's and variable's addresses are zero when they are not defined, so it is &global_variable that you must compare with zero.)
And suppose this is def.c, which defines global_func and global_variable; we'll use it to build libdef.so (corresponding to MPI, in your question):
#include <stdio.h>
int global_variable;
void global_func(void) {
printf("Hi, from global_func! global_variable = %d\n", global_variable);
}
And finally, suppose we have a main program, main.c, which calls ref_func from libref.so:
#include <stdio.h>
extern void ref_func(void);
int main(int argc, char **argv) {
printf("%s: ", argv[0]);
ref_func();
return 0;
}
Here's the Makefile that builds libref.so and libdef.so, and then builds two executables, both of which link against libref.so, but only one of which links against libdef.so:
all: ref-absent ref-present
ref-absent: main.o libref.so
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $#
ref-present: main.o libref.so libdef.so
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $#
lib%.so: %.o
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ -o $#
ref.o def.o: CFLAGS += -fpic
clean:
rm -f *.o *.so ref-absent ref-present
Do the build:
$ make
cc -c -o main.o main.c
cc -fpic -c -o ref.o ref.c
cc -shared ref.o -o libref.so
cc main.o libref.so -o ref-absent
cc -fpic -c -o def.o def.c
cc -shared def.o -o libdef.so
cc main.o libref.so libdef.so -o ref-present
$
Note that both ref-absent and ref-present linked without problems, even though there is no definition for global_name in ref-absent.
Now we can run the programs, and see that ref-absent skips the function call, while ref-present uses it. (We have to set LD_LIBRARY_PATH to allow the dynamic linker to find our shared libraries in the current directory.)
$ LD_LIBRARY_PATH=. ./ref-absent
./ref-absent: global_func = (nil)
$ LD_LIBRARY_PATH=. ./ref-present
./ref-present: global_func = 0x15d4ac
Hi, from global_func! global_variable = 1
$
The trick for you will be getting the ((weak)) attribute attached to every declaration of every MPI function your library references. However, as ref.c shows, there can be multiple declarations, and as long as one of them mentions the weak attribute, you're done. So you'll probably have to say something like this (I don't really know MPI):
#include <mpi.h>
mpi_fake_type_t mpi_function_foo(mpi_arg_type_t) __attribute__((weak));
mpi_fake_type_t mpi_function_bar(mpi_other_arg_type_t) __attribute__((weak));
Every reference to an MPI function needs to be in the scope of a ((weak)) declaration for that function; that's how the compiler decides what sort of symbol reference to put in the object file. You'll want to have automated tests to verify that you haven't accidentally generated any non-weak references.

Resources