Haskell linking with dynamic libraries on Ubuntu - haskell

I am having issues linking to a Haskell library we wrote. It goes wrong on Ubuntu, but not on Arch Linux. The error on Ubuntu we get is this:
/usr/bin/ld: warning: libHSdeepseq-1.3.0.0-ghc7.4.1.so, needed by /usr/lib/ghc/containers-0.4.2.1/libHScontainers-0.4.2.1-ghc7.4.1.so, not found (try using -rpath or -rpath-link)
/usr/lib/ghc/containers-0.4.2.1/libHScontainers-0.4.2.1-ghc7.4.1.so: undefined reference to 'deepseqzm1zi3zi0zi0_ControlziDeepSeq_zdfNFDataArrayzuzdcrnf1_info'
The issue seems to be caused by the fact that libHScontainers-0.4.2.1-ghc7.4.1.so is incorrectly linked as one can see by the output of ldd:
ldd /usr/lib/ghc/containers-0.4.2.1/libHScontainers-0.4.2.1-ghc7.4.1.so
linux-vdso.so.1 => (0x00007fffe95a2000)
libHSdeepseq-1.3.0.0-ghc7.4.1.so => not found
libHSbase-4.5.0.0-ghc7.4.1.so => not found
libHSghc-prim-0.2.0.0-ghc7.4.1.so => not found
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f89a5a59000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f89a569a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f89a5fd8000)
Apparently the dependent libraries can not be found. They are installed. However if I do the same on Arch:
ldd /usr/lib/ghc-7.8.3/deepseq-1.3.0.2/libHSdeepseq-1.3.0.2-ghc7.8.3.so
linux-vdso.so.1 (0x00007fff09dfe000)
libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007fb8d3e96000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007fb8d3b91000)
librt.so.1 => /usr/lib/librt.so.1 (0x00007fb8d3988000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fb8d3784000)
libffi.so.6 => /usr/lib/libffi.so.6 (0x00007fb8d357b000)
libHSarray-0.5.0.0-ghc7.8.3.so => /usr/lib/ghc-7.8.3/deepseq-1.3.0.2/../array-0.5.0.0/libHSarray-0.5.0.0-ghc7.8.3.so (0x00007fb8d32e1000)
libHSbase-4.7.0.1-ghc7.8.3.so => /usr/lib/ghc-7.8.3/deepseq-1.3.0.2/../base-4.7.0.1/libHSbase-4.7.0.1-ghc7.8.3.so (0x00007fb8d2967000)
libHSinteger-gmp-0.5.1.0-ghc7.8.3.so => /usr/lib/ghc-7.8.3/deepseq-1.3.0.2/../integer-gmp-0.5.1.0/libHSinteger-gmp-0.5.1.0-ghc7.8.3.so (0x00007fb8d274c000)
libHSghc-prim-0.3.1.0-ghc7.8.3.so => /usr/lib/ghc-7.8.3/deepseq-1.3.0.2/../ghc-prim-0.3.1.0/libHSghc-prim-0.3.1.0-ghc7.8.3.so (0x00007fb8d24cf000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fb8d212c000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fb8d1f10000)
/usr/lib64/ld-linux-x86-64.so.2 (0x00007fb8d435f000)
The libraries are found.
As suggested I can solve this on Ubuntu by using -rpath in the application we try to link to the Haskell library. But this means that we have to do this for every Haskell package which seems wrong to me.
We can also fix this by adding a line to /etc/ld.so.conf.d/ghc.conf. But this also has to be done for every package and is not user-friendly.
A few questions I have:
What is the correct way to fix this?
Why are the packages in ghc-dynamic incorrectly linked?
Why is the linker able to find libHScontainers-0.4.2.1-ghc7.4.1.so but not libHSdeepseq-1.3.0.0-ghc7.4.1.so?

I strongly suspect that this is because the Haskell libraries installed by ghc have the locations of their dependencies (the RPATH field of their ELF header; you can verify using readelf -d) defined in terms of $ORIGIN. When library X depends on library Y, library X can indicate that library Y should be found in a location relative to its own location by using $ORIGIN. This is supported by the dynamic linker, but is not supported by the static linker.
(I'm speculating here:) Your library will define the location of its direct dependencies (in your case, I'm guessing, this includes containers) in terms of its own RPATH, which is not in terms of $ORIGIN. This is why the linker can find those, but not its transitive dependencies (again, I'm guessing, this includes deepseq in your case).
So why the difference between Arch Linux and Ubuntu? (Speculating further.) This is because unlike on Arch Linux, Ubunbu's linker uses --as-needed by default. You see, ghc will link your library against all its dependencies (including transitive ones), but then the linker will omit some of those dependencies because it doesn't directly depend on them. You could verify this by relinking with --no-as-needed.
Note that these errors by the static linkers really aren't errors, but warnings: it tries to resolve symbols, but it can't; but the dynamic linker will be able to anyway. So you can instruct the linker to ignore these errors (--unresolved-symbols=ignore-all) and all should be well.
I've been battling with adding explicit support in Cabal for generating Haskell libraries for use in C programs, and found the same problem. See https://github.com/haskell/cabal/pull/2540#issuecomment-95984067 for details.

Related

Rust build.rs: Static linkage of library results in issues when trying to find the c++ std lib

I'm developing a libfoo-sys crate with low level bindings for an third-party C-library and a libfoo-crate on top of that. In the build-script of libfoo-sys I check out the original source from git.
I want to prevent that users have to globally install stuff into their system.
I build the library in the directory of the git submodule, and install the library locally into ./lib-out. Inside ./lib-out After building, I get the shared object file that has the following dependencies:
linux-vdso.so.1 (0x00007ffc9eff6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f07870b0000)
libjpeg.so.8 => /lib/x86_64-linux-gnu/libjpeg.so.8 (0x00007f078702b000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f0786e49000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0786c57000)
/lib64/ld-linux-x86-64.so.2 (0x00007f078734f000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0786c3c000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0786aeb000)
My build-script tells Rustc to use static linkage:
println!("cargo:rustc-link-lib=static=foo");
However, as soon as I use static linkage, Rustc can't find C++ symbols. etc. I guess I have to also link all the dependencies manually. However, I don't know a generic way how println!("cargo:rustc-link-lib=static=stdc++"); will work in the end. I can't find a libstdc++.so in my file system but only a libstdc++.so.6 which is not compatible with cargo:rustc-link-lib (or at least I don't know how). Any advices?
PS: I'm aware of that you should refrain from linking glibc statically and I guess the same applies for the c++ std as well. However, primarily I'd like to figure out if it makes sense and brings a benefit to statically link the library and use the self compiled version instead of a globally installed version

Forcing ELF binary to use another libc.so [duplicate]

This question already has answers here:
Multiple glibc libraries on a single host
(11 answers)
Closed 6 years ago.
I need to make ELF binary file to use another version of libc.so for scientific purposes. I tried to do it with LD_PRELOAD and patchelf utility, but then binary does not run with an errors like:
./a.out: error while loading shared libraries: libc-2.15.so: cannot open shared object file: No such file or directory or Segmentation fault (core dumped).
I think that it is because I need another version of ld.so.
What is most efficient way to make binary use my version of libc?
EDIT: I do not have source code of binary.
EDIT: Error message edited. No SELinux, no AppArmor.
For me it looks that you did not spend the whole path to the new libc variant.
I did the following:
ldd example
linux-vdso.so.1 (0x00007ffe9c087000)
libstdc++.so.6 => /opt/linux-gnu_6.1.0/lib64/libstdc++.so.6 (0x00007f0cef872000)
libm.so.6 => /lib64/libm.so.6 (0x00007f0cef56f000)
libgcc_s.so.1 => /opt/linux-gnu_6.1.0/lib64/libgcc_s.so.1 (0x00007f0cef359000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0ceef98000)
/lib64/ld-linux-x86-64.so.2 (0x000055ca3cb92000)
LD_PRELOAD=/tmp/bug_libc.so ldd example
linux-vdso.so.1 (0x00007ffc2cff8000)
/tmp/bug_libc.so (0x00007f56a1358000)
libstdc++.so.6 => /opt/linux-gnu_6.1.0/lib64/libstdc++.so.6 (0x00007f56a0f9a000)
libm.so.6 => /lib64/libm.so.6 (0x00007f56a0c98000)
libgcc_s.so.1 => /opt/linux-gnu_6.1.0/lib64/libgcc_s.so.1 (0x00007f56a0a82000)
/lib64/ld-linux-x86-64.so.2 (0x00005605c8a7a000)
If I replace with a non valid libc a got a different error message. Only if I gave the wrong path like:
LD_PRELOAD=/tmp/bug_libc.so2 ldd go
ERROR: ld.so: object '/tmp/bug_libc.so2' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
ERROR: ld.so: object '/tmp/bug_libc.so2' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
linux-vdso.so.1 (0x00007ffedcde4000)
libstdc++.so.6 => /opt/linux-gnu_6.1.0/lib64/libstdc++.so.6 (0x00007f3ae2188000)
libm.so.6 => /lib64/libm.so.6 (0x00007f3ae1e85000)
libgcc_s.so.1 => /opt/linux-gnu_6.1.0/lib64/libgcc_s.so.1 (0x00007f3ae1c6f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f3ae18ae000)
/lib64/ld-linux-x86-64.so.2 (0x000055df54aae000)
Maybe you have some other problem by accessing your replacement libc-file.
Check the access flags on the file and also check if SELinux or AppArmor or other protection stops loading libc in your environment. Because replacing libc opens a security hole it is a candidate for SELinux & Co!
And you should check always with ldd first. Maybe your new libc requires some more (older) variants of other libs which can not be found on your system. Normaly libc did not require other libs, but I have no idea what game you are playing. Whatever: ldd gives you a more detailed answer to the things which are going on in the library loading phase.
EDIT: segfault
If you get segfault, you typically have compiled your application with incompatible header files. You have to compile with the headers which comes with the libc version you want to use. If you compile against your system headers for system libc and run any incompatible version of precompiled libc, you get any kind of memory errors by accessing wrong data structures.
This answer explains why LD_PRELOAD can not work, and suggests solutions.
I tried to do it with LD_PRELOAD and patchelf utility
It's not clear whether your use of patchelf touched only DT_RPATH, only PT_INTERP, or both. You need to do both.

How to build a './configure && make && make install' software against a custom library which I also build?

I am building tmux-2.0 from sources on a pretty regular Linux host. First attempt failed as it turned out that the version of libevent installed is older than required, so I proceeded to download and build libevent-2.0.22 from sources (current at the time of writing) first.
Building of libevent succeeded flawlessly, and I thought I could then retry building tmux with the following:
PKG_CONFIG_PATH=$PATH_TO_MY_BUILT_LIBEVENT/lib/pkgconfig ./configure ...
The above invocation succeeded, so did subsequent make and make install.
Running my newly build tmux, however, aborts with a missing shared object, not surprisingly libevent-2.0.so.5:
tmux: error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such file or directory
I thought building against a custom library implies it will also be used at runtime? ldd on my tmux gives me:
linux-vdso.so.1 => (0x00007fff8f5ff000)
libutil.so.1 => /lib64/libutil.so.1 (0x0000003cf8800000)
libncurses.so.5 => /lib64/libncurses.so.5 (0x0000003cf7e00000)
libevent-2.0.so.5 => not found
librt.so.1 => /lib64/librt.so.1 (0x0000003ce8600000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x0000003cea200000)
libc.so.6 => /lib64/libc.so.6 (0x0000003ce7600000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003cf7200000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003ce7e00000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003ce8200000)
/lib64/ld-linux-x86-64.so.2 (0x0000003ce7200000)
So, libevent-2.0.so.5 is not found.
Do I need to resort to setting, I don't know, LIBS, LDFLAGS or some other variables or switches to configure script above, so that, I don't know, the paths to my newly built libevent are embedded in tmux binary, courtesy of ld?
I do not have root access - university Linux workstation - and frankly I don't need one, I think. I also do not want to muck about with LD_LIBRARY_PATH or the like. Suffice to say, doing LD_LIBRARY_PATH=$PATH_TO_MY_LIBEVENT/lib tmux works fine. But I want it to work "by default", locating and using my libevent.
I guess the solution would apply to pretty much any software using the "GNU build system". What's the right thing to do here?
You built against a library, but the system doesn't know where the library is. Since you don't want to install the library, but rather leave it in the place where you built it, you could solve it with -rpath= option of the linker — it embeds a search path for libraries into the executable file.
Just rebuild your application with it being added to your LDFLAGS, like LDFLAGS="-rpath=/home/mypath/to/libevent" (but note, it is a linker option, and it is possible that in the makefile as a linker used the gcc itself — gcc does not know the option, then you need to write it likeLDFLAGS="-Wl,-rpath=/home/mypath/to/libevent" to force gcc to pass the option down to the actual linker)
By the way, actually you can change rpath even without recompiling the application — there's a tool patchelf for that job.

How to use alternate glibc with existing libstdc++?

I need to use a self-compiled version of glibc (2.18), newer than the default one on the system (2.15). I can compile&link a C++ program, but when I try to run it, I get errors about libstdc++.so.6. (C programs seems to work just fine.) Do I need to recompile gcc against the newer glibc for this to work? Why? (Update: I figured this part out, but I have a few other questions at the bottom.)
Here is a sample C++ program:
#include <iostream>
int main()
{
std::cout << "ok\n";
return 0;
}
Following this answer, I compiled it with:
g++ -Wl,--rpath=/path/to/glibc-2.18/lib -Wl,--dynamic-linker=/path/to/glibc-2.18/lib/ld-2.18.so a.cpp
It compiles with no errors, then ldd says:
$ ldd a.out
linux-vdso.so.1 => (0x00007fff421fe000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f3b96e7f000)
libc.so.6 => /path/to/glibc-2.18/lib/libc.so.6 (0x00007f3b96ad1000)
libm.so.6 => /path/to/glibc-2.18/lib/libm.so.6 (0x00007f3b967cf000)
/path/to/glibc-2.18/lib/ld-2.18.so => /lib64/ld-linux-x86-64.so.2 (0x00007f3b9719d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3b965b9000)
But when I try to run it:
$ ./a.out
./a.out: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory
This is confusing because it looks like ldd finds libstdc++.so.6 just fine (the specific version is libstdc++.so.6.0.16).
Update: The problem seems to have been (not sure) that the new 2.18 dynamic linker is using its own library path which includes only subfolders of /path/to/glibc-2.18/lib. I got the program to run by adding this new path, followed by the standard paths (/lib', '/usr/lib, etc) to /path/to/glibc-2.18/etc/ld.so.conf and running /path/to/glibc-2.18/sbin/ldconfig. More questions:
Do I absolutely need the new 2.18 dynamic linker to run a program with glibc-2.18? Can't the standard linker do it? (This would avoid me having to set up and continuously update the paths of the 2.18 dynamic linker.)
If I compile with the 2.18 dynamic linker but without --rpath, the program doesn't work. Why?
Should I be using -L/path/to/glibc-2.18/lib in the compilation command (in addition to --rpath and --dynamic-linker)?
Do I absolutely need the new 2.18 dynamic linker to run a program with glibc-2.18?
Yes (well, almost. See footnote).
This would avoid me having to set up and continuously update the paths of the 2.18 dynamic linker.
A common technique is to create a g++ shell wrapper, e.g. g++glibc2.18, and encapsulate adding the necessary link arguments there. Then a simple make CXX=g++glibc2.18 would do the right thing.
Can't the standard linker do it?
No. See this answer for explanation.
If I compile with the 2.18 dynamic linker but without --rpath, the program doesn't work. Why?
See the same answer.
Should I be using -L/path/to/glibc-2.18/lib in the compilation command (in addition to --rpath and --dynamic-linker)?
Yes, if you want to use symbols that are present in glibc-2.18 but not present in your system library. Otherwise, no.
Footnote:
As an alternative, you could build your program without the special flags, then use "explicit loader invocation" to actually run it: /path/to/glibc-2.18/lib/ld-2.18.so /path/to/a.out.
Beware: this doesn't always work: if the program likes to re-exec itself (and under other rare conditions). You may also have trouble debugging it when it is invoked that way.

Installing ghc binaries on Linux (can't find libgmp.so)

I am trying to install the Haskell Platform on Linux for the first time (I'm also a fairly new Linux user). The victim system is a fresh Red Hat system. And everything involved here should be 64 bit.
The directions at the platform website [1] indicate that I need a ghc7.0.3 to boostrap things. They provide a link to a generic binary of ghc-7.0.3 to do this. I fetched this and ran
$ ./configure ...
$ make install ...
as per the directions without incident (it is a binary, so no compilation needed)
However, when I tried to run ghci I get the output.
$ ghci
GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... <command line>: can't load .so/.DLL for: gmp (libgmp.so: cannot open shared object file: No such file or directory)
For some reason ghci cannot find libgmp.so. Running ghci ultimately invokes
/usr/local/lib/ghc-7.0.3/ghc
with a mess of options. I checked the dependencies via ldd
$ ldd /usr/local/lib/ghc-7.0.3/ghc
linux-vdso.so.1 => (0x00007fffe5f5c000)
libncursesw.so.5 => /lib64/libncursesw.so.5 (0x0000003ee7000000)
librt.so.1 => /lib64/librt.so.1 (0x0000003ee5800000)
libutil.so.1 => /lib64/libutil.so.1 (0x0000003ef3000000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003ee5000000)
libgmp.so.3 => /usr/lib64/libgmp.so.3 (0x0000003ee4400000)
libm.so.6 => /lib64/libm.so.6 (0x0000003ee4c00000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003ee5400000)
libc.so.6 => /lib64/libc.so.6 (0x0000003ee4800000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003ef3400000)
/lib64/ld-linux-x86-64.so.2 (0x0000003ee4000000)
and it shows that it foud libgmp. libgmp is in /usr/local/lib and /usr/local/lib64.
I am not sure how to get further with this. Any suggestions?
[1] http://hackage.haskell.org/platform/linux.html
You either add /usr/local/lib and/or /usr/local/lib64 to $LD_LIBRARY_PATH, or add them to /etc/ld.so.conf, or (since you already have /usr/lib64/libgmp.so.3) add a missing symbolic link:
cd /usr/lib64
sudo ln -s libgmp.so.3 libgmp.so
(and perhaps the same for /usr/lib).
Note that /usr/lib64/libgmp.so.3 might be a different version from /usr/local/lib64/libgmp.so, make sure ghc can actually be used with the former.
Installing gmp-devel package helped in my case (opensuse)
I'm not sure that setting a symbolic link from libgmp.so to libgmp.so.3 is the right way to go. What happens when you get a version update and so libgmp.so.3 disappears. Setting LD_LIBRARY_PATH seems like a better solution.
There's also another solution for RedHat/CentOS and by extension probably Fedora: install the gmp-devel package. This sets up the symbolic link above, but does so with in the distribution (so updates should update the symbolic link also).

Resources