Static vs. dynamic linking conflicts and duplication - linux

I have a code A that is statically linked against one version of mpich. Now comes library B, which is used by A via dlopen(). B depends on mpich as well, but is linked dynamically against it.
The problem is that now, in order for B to take advantage of mpi distribution, needs to access the communicator currently handled by A. This communicator has been created by A static version of mpich, When B invokes MPI routines, it will use a dynamic version of MPI which is not guarateed to be compatible with the static version attached to A.
This is the overall picture. I think that the only solution is to have mpich dynamically linked for both A and B. What I am not fully understanding is however the following:
how does the linker handle shared objects dependencies when dlopening? Will I have two instances of mpich in VM also with dynamic linking, or is the linker smart enough to realize that the symbols required by the dlopened B are already in the address space and will resolve against those.
Is it possible to tell the linker: when you dlopen this library, don't go fetch the dynamic dependency, but resolve it with the static symbols that are already provided by A

In short: it depends on dlopen options. By default, if a symbol needed by the requested library already exists in the global scope, it will be reused (this is what you want). But you can bypass this behavior with RTLD_DEEPBIND, with this flag, the dependencies won't be reused from the global scope, and will be loaded a second time.
Here is some code to reproduce your situation and demo the effect of this flag.
Let's make a common library that will be used by both lib A and program B. This library will exist in two versions.
$ cat libcommon_v1.c
int common_func(int a)
{
return a+1;
}
$ cat libcommon_v2.c
int common_func(int a)
{
return a+2;
}
Now let's write lib A that uses libcommon_v2:
$ cat liba.c
int common_func(int a);
int a_func(int a)
{
return common_func(a)+1;
}
And finally program B that dynamically links to libcommon_v1 and dlopens lib A:
$ cat progb.c
#include <stdio.h>
#include <dlfcn.h>
int common_func(int a);
int a_func(int a);
int main(int argc, char *argv[])
{
void *dl_handle;
int (*a_ptr)(int);
char c;
/* just make sure common_func is registered in our global scope */
common_func(42);
printf("press 1 for global scope lookup, 2 for deep bind\n");
c = getchar();
if(c == '1')
{
dl_handle = dlopen("./liba.so", RTLD_NOW);
}
else if(c == '2')
{
dl_handle = dlopen("./liba.so", RTLD_NOW | RTLD_DEEPBIND);
}
else
{
printf("wrong choice\n");
return 1;
}
if( ! dl_handle)
{
printf("dlopen failed: %s\n", dlerror());
return 2;
}
a_ptr = dlsym(dl_handle, "a_func");
if( ! a_ptr)
{
printf("dlsym failed: %s\n", dlerror());
return 3;
}
printf("calling a_func(42): %d\n", (*a_ptr)(42));
return 0;
}
Let's build and run all the things:
$ export LD_LIBRARY_PATH=.
$ gcc -o libcommon_v1.so -fPIC -shared libcommon_v1.c
$ gcc -o libcommon_v2.so -fPIC -shared libcommon_v2.c
$ gcc -Wall -g -o progb progb.c -L. -lcommon_v1 -ldl
$ gcc -o liba.so -fPIC -shared liba.c -L. -lcommon_v2
$ ./progb
press 1 for global scope lookup, 2 for deep bind
1
calling a_func(42): 44
$ ./progb
press 1 for global scope lookup, 2 for deep bind
2
calling a_func(42): 45
We can clearly see that with default options, dlopen reuses the symbol common_func that was present in program B and that with RTLD_DEEPBIND, libcommon was loaded again and library A got its own version of common_func.

You did not say which Toolchain (GCC, LLVM, MSC, etc.) you are using, the most helpful answer will depend upon this information.
May I suggest you look at "GCC Exception Frames" http://www.airs.com/blog/archives/166 .
If that is helpful then the Gold Linker, which is available for GCC and LLVM, supports 'Link Time Optimization' and can run in "Make" with DLLTool http://sourceware.org/binutils/docs/binutils/dlltool.html .
Indeed it is possible to have both Static and Dynamic Code call each other, the Computer does not care; it will 'run' anything it is fed -- whether that ends up working exactly the way you want or HCFs depends on correct Code and correct Linker commands.
Using a Debugger will not be fun. It would be best to mangle the names prior to Linking so that when debugging you can see from which module the Code came. Once it is up and running you can undef the Mangle and have the same-named Functions Link (to ensure it still functions).
Compiler / Linker Bugs will not be your friend.
This sort of scenario (Static and Dynamic Linking) occurs more often with MinGW and Cygwin where some of the Libraries are Static yet a Library you download from the Internet is only available in Dynamic form (without Source).
If the Library is from two different Compiler Toolchains then other issues arise, see this StackOverflow Thread: " linking dilemma (undefined reference) between MinGW and MSVC. MinGW fails MSVC works ".
It would be best to simply get the newest version of the Library from the source and compile the whole thing yourself, rather than rely on trying to cobble together bits and pieces from different sources (though it is possible to do that).
You can even load the Dynamic Library and call it (statically) and then reload portions of it later.
How tight are you on Memory and how fast do you want Functions to run, if everything is in Memory your Program can transfer execution to called Functions straight away, if you are swapping a portion of your Code to VM your execution times will really take a hit.
Running a Profiler on your Code will help decide what portions of a Library to load if you want to do 'dynamic dynamic linking' (full control of your dyna-linking by loading a Dynamic Library so it can be used Statically). This is the stuff that headaches and nightmare are made of. GL.

Related

How to add own custom function in standard shared library (C) in linux?

I have downloaded libgcrypt library source code and I want to customize this standard shared library by adding my function inside one particular
source code file .
Although compilation/build process of customized shared library is successful, but it shows error at linking time.
Here is what I have done .
inside /src/visibility.c file, I have added my custom function,
void MyFunction(void)
{
printf("This is added just for testing purpose");
}
I have also include function prototype inside /src/gcrypt.h
void MyFunction(void);
#build process
./configure --prefix=/usr
sudo make install
nm command find this custom function.
nm /usr/lib/libgcrypt.so | grep MyFunction
000000000000dd70 t MyFunction
Here is my sample code to access my custom function.
//aes_gcrypt_example.c
#include <stdio.h>
#include <gcrypt.h>
#include <assert.h>
int main()
{
MyFunction();
return 0;
}
gcc aes_gcrypt_example.c -o aes -lgcrypt
/tmp/ccA0qgAB.o: In function `main':
aes_gcrypt_example.c:(.text+0x3a2): undefined reference to `MyFunction'
collect2: error: ld returned 1 exit status
I also tried by making MyFunction as extern inside gcrypt.h, but in that case also I am getting same error.
Why is this happening ?
Is the customization of standard library is not allowed ?
If YES, then is there any FLAG to disable to allow customization ?
If NO, what mistake I am making ?
It would be great help if someone provide some useful link/solution for the above mentioned problem. I am using Ubuntu16.04 , gcc 4.9.
Lower-case t for the symbol type?
nm /usr/lib/libgcrypt.so | grep MyFunction
000000000000dd70 t MyFunction
Are you sure that's a visible function? On my Ubuntu 16.04 VM, the linkable functions defined in an object file have T (not t) as the symbol type. Is there a stray static kicking around and causing confusion? Check a couple of other functions defined in libgcrypt.so (and documented in gcrypt.h) and see whether they have a t or a T. They will have a T and not t. You'll need to work out why your function gets a t — it is not clear from the code you show.
The (Ubuntu) man page for nm includes:
The symbol type. At least the following types are used; others
are, as well, depending on the object file format. If lowercase,
the symbol is usually local; if uppercase, the symbol is global
(external).
The line you show says that MyFunction is not visible outside its source file, and the linker agrees because it is not finding it.
Your problem now is to check that the object file containing MyFunction has a symbol type T — if it doesn't the problem is in the source code.
Assuming that the object file shows symbol type T but the shared object shows symbol type t, you have to find what happens during the shared object creation phase to make the symbol invisible outside the shared object. This is probably because of a 'linker script' that controls which symbols are visible outside the library (or maybe just compilation options). You can search on Google with 'linker script' and various extra words ('tutorial', 'provide', 'example', etc) and come up with links to the relevant documentation.
You may need to research documentation for LibTool, or for the linker BinUtils. LibTool provides ways of manipulating shared libraries. In a compilation command line that you show in a comment, there is the option -fvisibility=hidden. I found (mostly by serendipitous accident) a GCC Wiki on visibility. See also visibility attribute and code generation options.

riscv/gcc/ld - "Undefined reference to printf" using own script to link

Currently, I'm learning RISC-V, use the RISC-V toolchain, and edit a new ld script for my embedded. I write a example, and compile to watch the opcode.
Example:
#include <stdio.h> //float.c
int main()
{
float a=1.04;
printf("a=%f\n",a);
return 0;
}
My steps is:
1. riscv64-unknown-elf-gcc -S float.c  *//generate assembly code*
2. riscv64-unknown-elf-as float.s -o float.o  *//generate obj file*
3. riscv64-unknown-elf-ld -T elf64lriscv1.x float.o *//use own script to link, -T is using other script*
and then,it will show "float.c:(.text+0x50): undefined reference to `printf'
I'm try
Add -lc parameter, but doesn't working, it will show more undefined message.
My ld script
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv","elf64-littleriscv")
OUTPUT_ARCH(riscv)
ENTRY(_start)
SEARCH_DIR("/path/to/install/riscv/toolchain/riscv64-unknow-elf/lib");
/*MEMORY{ //for my embedded allocation
flash : org = 0x0, l = 0x10000
ram : org= 0x10000, l = 512
}*/
SECTIONS{
_start =0x10000;
.text :
{
*(.text)
}
.bss :
{
*(.bss)
*(.sbss)
}}
Also, i'm trying to use the default script, like this command:
$ riscv-unknown-elf-ld float.o -o float
but it is same result....
please help me!
Regards!
printf is provided by the C standard library (and is very difficult to implement). You need to link it (perhaps by adding -lc to your riscv-unknown-elf-ld command, or by giving the complete path of that library)
You might pass additional options to your riscv-unknown-elf-ld perhaps -M, -L search-dir, --trace, --verbose, etc. Read also more carefully the chapter about ld scripts.
Since you are cross-compiling, you might need to cross-compile some libc from source code.
You need to spend more time understanding the behavior of linkers and ELF object files and executable format (see also elf(5)). Consider reading Linkers and Loaders
You could use other cross- binutils programs (like cross- objdump, nm, readelf etc...) to explore the relevant object files.
If you are coding for a bare metal system, you might want to compile in freestanding mode (passing -ffreestanding to your GCC) and provide so implement your own printf (or other output) function. Existing free software C libraries could inspire you (but you need to find one or work many months to develop some equivalent). Study for inspiration the source code of GNU libc or of musl-libc.
I recommend also reading about OSes, e.g. Operating Systems: Three Easy Pieces (since the OS concepts are relevant to you, because an embedded system on the bare metal share features with OSes). OSDEV wiki might also be helpful (but is not about RISC-V).
You could need several months of work (or even years), so budget it appropriately.
BTW, I am surprized that you use a float in your example. Floating point is difficult. See floating-point-gui.de ; For a first try, I would consider using integer only.

Enumerating shared libraries loaded via dlopen(RTLD_GLOBAL)

Is there a way to determine at runtime which shared libraries have been loaded into the global symbol namespace of the current process? I'm primarily interested in anything that was loaded as a result of a dlopen() call that used the RTLD_GLOBAL flag.
I'm wanting to do this for auditing purposes -- it's important for an application I work on that dynamically-loaded shared libraries are loaded with dlopen's RTLD_LOCAL wherever possible so as not to conflict with third-party code; anything that's loaded into the global symbol namespace needs to be tightly controlled.
I've looked at the dl_iterate_phdr() API, but it doesn't seem to include this information.
You can try with
#define _GNU_SOURCE
#include <dlfcn.h>
typedef void *(*orig_dl)(const char *file, int mode);
void *dlopen(const char *file, int mode)
{
orig_dl o_dlopen;
o_dlopen = (orig_dl)dlsym(RTLD_NEXT, "dlopen");
return o_dlopen(file, mode);
}
Compile it using gcc -shared -fPIC dlo.c -o dlo.so -ldl
add LD_PRELOAD=dlo.so and here you go. You can log/trace/print any dlopen usage with specific mode
I think the suggestion to replace dlopen() using LD_PRELOAD is only a partial solution -- you won't catch dependencies of a library loaded with dlopen() that way.
In the end, I couldn't see any way of doing this without scraping the internal state of the dynamic linker itself. It found that there's a _rtld_global symbol exported from ld.so that has the information, but that you have to use private Glibc headers to interpret it.
The following is a Python snippet that will (assuming my reading of the Glibc sources is correct) print all the shared libraries in the global namespace in the order that they will be searched. Libraries loaded with RTLD_LOCAL will not be printed.
The fact that it relies on implementation details of Glibc means this approach is fraught with peril, but for my testing/auditing purposes I think it'll do nicely.
import ctypes
# Abridged type declarations pillaged from Glibc. See:
# - https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/generic/ldsodefs.h
# - https://sourceware.org/git/?p=glibc.git;a=blob;f=include/link.h
class link_map(ctypes.Structure):
_fields_ = [
("l_addr", ctypes.c_size_t),
("l_name", ctypes.c_char_p),
]
class r_scope_elem(ctypes.Structure):
_fields_ = [
("r_list", ctypes.POINTER(ctypes.POINTER(link_map))),
("r_nlist", ctypes.c_uint),
]
class rtld_global(ctypes.Structure):
_fields_ = [
("_ns_loaded", ctypes.POINTER(link_map)),
("_ns_nloaded", ctypes.c_uint),
("_ns_main_searchlist", ctypes.POINTER(r_scope_elem)),
]
_rtld_global = rtld_global.in_dll(ctypes.CDLL(None), "_rtld_global")
searchlist = _rtld_global._ns_main_searchlist[0]
print [searchlist.r_list[n][0].l_name for n in xrange(searchlist.r_nlist)]
On my CentOS 7 system, this prints:
['', '/lib64/libpython2.7.so.1.0', '/lib64/libpthread.so.0', '/lib64/libdl.so.2',
'/lib64/libutil.so.1', '/lib64/libm.so.6', '/lib64/libc.so.6',
'/lib64/ld-linux-x86-64.so.2']

Shared object in Linux without symbol interposition, -fno-semantic-interposition error

Shared objects (*.so) in Unix-like systems are inefficient because of the symbol interposition: Every access to a global variable inside the .so needs a GOT lookup, and every call from one function to another inside the .so needs a PLT lookup. I was therefore happy to see that gcc version 5.1 has added the option -fno-semantic-interposition. However, when I try to make a .so where one function calls another without using a PLT, I get the error message:
relocation R_X86_64_PC32 against symbol `functionname' can not be used when making a shared object; recompile with -fPIC
I expected the option -fno-semantic-interposition to eliminate this error message, but it doesn't. -mcmodel=large doesn't help either.
The reference to the function is indeed position-independent, which the error message actually confirms (R_X86_64_PC32 means PC-relative 32-bit relocation in 64-bit mode). -fPIC does not really mean position-independent, as the name would imply, it actually means use GOT and PLT.
I cannot use __attribute__((visibility ("hidden"))) because the called function and the caller are compiled in seperate files (caller is in C++, called function is in assembly).
I tried to make an assembly listing to see what the option -fno-semantic-interposition does. I found out that it makes a reference to a local alias when one function calls another in the same file, but it still uses a PLT when calling a function in another file.
(g++ version is 5.2.1 Ubuntu, 64-bit mode).
Is there a way to make the linker accept a cross-reference inside a .so without the GOT/PLT lookup?
Is there a way to make the linker accept a cross-reference inside a .so without the GOT/PLT lookup?
Yes: attribute((visibility("hidden"))) is exactly the way to do it.
I cannot use attribute((visibility ("hidden"))) because the called function and the caller are compiled in seperate files
You are confused: visibility("hidden") means that the symbol will not be exported from the shared library, when it is finally linked. But the symbol is global and visible across multiple translation units before that final link.
Proof:
$ cat t1.c
extern int foo() __attribute__((visibility("hidden")));
int main() { return foo(); }
$ cat t2.c
int foo() __attribute__((visibility("hidden")));
int foo() { return 42; }
$ gcc -c -fPIC t1.c t2.c
$ gcc -shared t1.o t2.o -o t.so
$ nm -D t.so | grep foo
$
I tried to make an assembly listing to see what the option -fno-semantic-interposition does. I found out that it makes a reference to a local alias when one function calls another in the same file, but it still uses a PLT when calling a function in another file.
If you read the discussion in gcc-patches, you'll see that the -fno-semantic-interposition is about allowing inlining of possibly interposable functions, not about the way they are actually called when not inlined.

Strange symbols while linking shared library g++

I have a file with the void initGui() function in it. It does stuff.
I also have a .so shared library made with that file.
The problem is, when I try to launch a dlsym(..., "initGui"), dlerror() tells me that it didn't found the symbol (of course, I used dlopen to open it). So I tried to nm my shared lib. I "understood" that _Z7initGuiiii might be what I'm looking for. So I tried to dlsym it ... And it worked.
Please can someone tell me how to have clean symbols in my shared object library ?
I compile with g++ -Wall -Wextra -Werror -c -fPIC.
The usual practice when dlsym-ing inside some dlopen-ed shared library coded in C++ is to have the convention that those seeked symbols (those that you are dlsym-ing) are declared extern "C". Then their name is easily visible with dlsym. So you need to declare
extern "C" void initGui(void);
and then to do
typedef void initguiroutine_sig_t(void);
initguiroutine_sig_t* initguiptr = dlsym(dlhandle,"initGui");
if (!initguiptr) {
fprintf(stderr, "initGui not found: %s\n", dlerror());
exit (EXIT_FAILURE);
};
// later, call initguiptr like
(*initguiptr) ();
I don't recommend understanding in great details how your C++ name mangling works, it is not very well defined, and details depend upon particular version of the C++ libraries (notably the standard C++ library) and of the compiler version.

Resources