Say that you got a c program, but almost any program will do, and put it in a file:
#include <stdio.h>
#include <gnu/libc-version.h>
int main (void) {
puts (gnu_get_libc_version ());
return 0;
}
And say that you want to build it against a specific version of glibc, for some reason. My initial attempt at doing this would be to create a Guix environment containing that old version of glibc along with gcc (and coreutils for programs like ls).
$ guix environment --pure --ad-hoc glibc#2.29 gcc-toolchain coreutils
$ rm a.out && gcc printer.c && ldd a.out && a.out
linux-vdso.so.1 (0x00007ffd2cd0c000)
libgcc_s.so.1 => /gnu/store/jlrfl1ss3b4xjggvajwffa9zppfcxksf-gcc-5.5.0-lib/lib/libgcc_s.so.1 (0x00007fcefd7b6000)
libc.so.6 => /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/libc.so.6 (0x00007fcefd5f9000)
/gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 (0x00007fcefd7d1000)
2.31
Unfortunately this doesn't seem to work. The resulting program is linked against a newer version of glibc than I expected, 2.31 rather than 2.29. But this may be due to gcc itself being linked against linked against glibc 2.31 and that ends up polluting the environment, so to speak.
$ ldd $(which gcc)
linux-vdso.so.1 (0x00007fff7cfc5000)
libm.so.6 => /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/libm.so.6 (0x00007ff842b93000)
libc.so.6 => /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/libc.so.6 (0x00007ff8429d6000)
/gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 (0x00007ff842cd6000)
Where do I go from here? I've tried using older versions of gcc packaged in Guix, but they all seem to be built against glibc 2.31. I also tried adding -L /gnu/store/hlsas48h6x7364kcfs8yy6xfksdsffr4-glibc-2.29/lib to my gcc-invocation but to no avail.
I was able to figure it out, or rather: cbaines on #guix#freenode pointed me towards the function make-gcc-toolchain which allowed me to set up the environment I wanted. By placing the following code into a file called development-environment.scm:
(use-modules (gnu packages base)
(gnu packages commencement)
(gnu packages gcc)
(gnu packages version-control))
(define-public gcc-glibc-2.29-toolchain
(make-gcc-toolchain gcc glibc-2.29))
(list gcc-glibc-2.29-toolchain git coreutils)
and then running guix environment --pure --ad-hoc --load=development-environment.scm I was able to build my program using the version of glibc that I wanted:
$ rm a.out && gcc printer.c && ldd a.out && ./a.out
linux-vdso.so.1 (0x00007fff7e17c000)
libgcc_s.so.1 => /gnu/store/71rcc4qxfgyzr0qphkh9adjsqsb999zk-gcc-glibc-2.29-7.5.0-lib/lib/libgcc_s.so.1 (0x00007f2c8adc9000)
libc.so.6 => /gnu/store/hlsas48h6x7364kcfs8yy6xfksdsffr4-glibc-2.29/lib/libc.so.6 (0x00007f2c8ac0f000)
/gnu/store/hlsas48h6x7364kcfs8yy6xfksdsffr4-glibc-2.29/lib/ld-linux-x86-64.so.2 (0x00007f2c8ade4000)
2.29
Hot tip for posterity: put the following in a file called .envrc, install the package direnv and insert it into your shell rc, and fill it with the following content:
use guix --ad-hoc --load=development-environment.scm
That way, every time you enter that folder with your shell the development environment will be loaded for you. The right version of glibc/gcc will be run even without the --pure flag.
Related
I am cross-compiling by binaries identically by CMake clauses like
add_executable(mybinary1 mybinary1.c util_temp.c)
target_link_libraries(mybinary1 m)
add_executable(mybinary2 mybinary2.c util_temp.c)
target_link_libraries(mybinary2 m)
Unfortunately, one of them fails when running on target platform:
/mybinary2: /lib/arm-linux-gnueabihf/libm.so.6: version `GLIBC_2.27' not found (required by ./mybinary2)
First binary runs well.
ldd shows
# ldd mybinary2
./mybinary2: /lib/arm-linux-gnueabihf/libm.so.6: version `GLIBC_2.27' not found (required by ./mybinary2)
linux-vdso.so.1 (0x7ed1d000)
/usr/lib/arm-linux-gnueabihf/libarmmem.so (0x76fa2000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76f27000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76de6000)
/lib/ld-linux-armhf.so.3 (0x76fcc000)
# ldd mybinary1
linux-vdso.so.1 (0x7ee09000)
/usr/lib/arm-linux-gnueabihf/libarmmem.so (0x76ece000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76e53000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76d12000)
/lib/ld-linux-armhf.so.3 (0x76efb000)
What does it mean and how to solve it? Can I compile against specific version of a library? Why does it refer GLIBS indirectly, via libm?
If I do
target_link_libraries(mybinary2 m -static)
it starts to work but binary becomes x20 larger.
I also tried
__asm__(".symver realpath,realpath#GLIBC_2.19");
int main(int argc, char *argv[])
in mybinary2.c with no effect.
target_link_libraries(mybinary2 m -static-libgcc -static-libstdc++)
in CMakeLists.txt with no effect.
target_link_libraries(mybinary2 m libc.so.6:2.19)
with error message
[build] /usr/lib/gcc-cross/arm-linux-gnueabihf/7/../../../../arm-linux-gnueabihf/bin/ld: cannot find -llibc.so.6:2.19
Is latter syntax correct? How to see, which version (and of what) is on my system?
I tried commands like
ld -llibc
but neither work.
On my target computer (it is Raspberry Pi with old version of OS):
# ldd --version
ldd (Debian GLIBC 2.19-18+deb8u10) 2.19
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
I don't know how to use cross ldd on source computer.
Based on what your target computer outputs via ldd --version and based on the error (which also explains why a static link works):
You have a GLIBC version mismatch, i.e. you compile it using GLIBC 2.27 but on the target computer you have GLIBC 2.19. You either need to crosscompile with a lower version than you are currently using or upgrade glibc on the target computer.
EDIT: To clarify which errors and general info I mean:
1)
# ldd mybinary2
./mybinary2: /lib/arm-linux-gnueabihf/libm.so.6: version `GLIBC_2.27' not found (required by ./mybinary2)
# ldd --version
ldd (Debian GLIBC 2.19-18+deb8u10) 2.19
EDIT2: Did a quick search and assuming you are crosscompiling on debian you could use the following command to get available package versions:
apt-cache policy myPackage
Where myPackage will most likely be libc6-dev-armhf-cross for your current needs.
EDIT3: As Andrew Henle pointed out. The easiest solution is to directly compile your project on the target device (or a compilation environment that is identical to the one on the target). Consider the above only if the target device is limiting you in doing so.
I looked for similar post on this topic but none the solutions work for me. I am trying to build a small program using openssl.
/mnt/sda1/openssl$ gcc tstsvr.c -I/mnt/sda1/openssl/include -L/mnt/sda1/openssl -lcrypto -lssl
When I try to run it:
$ sudo ./a.out
./a.out: error while loading shared libraries: libcrypto.so.81.1.1: cannot open shared object file: No such file or directory
But:
$ ldd a.out
linux-vdso.so.1 (0x00007fff23fe6000)
libcrypto.so.81.1.1 => /mnt/sda1/openssl/libcrypto.so.81.1.1 (0x00007f82f1db9000)
libssl.so.81.1.1 => /mnt/sda1/openssl/libssl.so.81.1.1 (0x00007f82f1d1e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f82f1b1a000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f82f1b14000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f82f1af1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f82f20ad000)
Format looks fine (inside /mnt/sda1/openssl):
$ file a.out
a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=760dc5f7be4f51f598bab38a0b1eab1a42ef8a68, for GNU/Linux 3.2.0, not stripped
$ file libcrypto.so.81.1.1
libcrypto.so.81.1.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=404d1e7ed143383801efbb10ed7914f2cd0858d4, not stripped
$ ldd libcrypto.so.81.1.1
linux-vdso.so.1 (0x00007ffc44b5a000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f1083a8c000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1083a69000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1083877000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1083d93000)
Just to make sure, I added path to LD_LIBRARY_PATH as well. Even ran sudo ldconfig. Neither made any difference.
What else can I try?
Per sudoers(5):
By default, the env_reset flag is enabled. This causes commands to be executed with a new, minimal environment.
...
Note that the dynamic linker on most operating systems will remove variables that can control dynamic linking from the environment of set-user-ID executables, including sudo. Depending on the operating system this may include RLD*, DYLD, LD_, LDR_*, LIBPATH, SHLIB_PATH, and others. These type of variables are removed from the environment before sudo even begins execution and, as such, it is not possible for sudo to preserve them.
Your easiest option is probably to do:
sudo LD_LIBRARY_PATH=/mnt/sda1/openssl ./a.out
Is it possible to run a generic C program on top of the bare linux kernel?
I am using a virtualized linux minimal system with Qemu, where I load my custom kernel and a root filesystem, but I apparently can't run my user-space hello world program.
EDIT 1: I have updated the question to be more detailed. Here are the steps I have followed:
1] I have compiled the hello_world program with the following command, on my host machine:
gcc hello_world.c -o hello_world
The source code is the following:
#include <stdio.h>
int main(int argc, char const *argv[]) {
printf("Hello world!\n");
return 0;
}
By running the program on my host machine, it works fine.
I have checked as #nexus suggested the libraries against which the program is built, and are the following:
$ ldd hello_world
linux-vdso.so.1 => (0x00007ffc0dfc8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efd90658000)
/lib64/ld-linux-x86-64.so.2 (0x000055a57447f000)
2] I have build the root filesystem by executing buildroot, which comes by default from Ciro Santilli's linux-kernel-module-cheat. I have compiled the linux kernel and got its corresponding bzImage
3] I have mounted the root filesystem and copied my compiled program inside of it, as follows:
sudo mount -t ext2 rootfs.ext2 /home/andreww/Desktop/mountfile/
sudo cp hello_world /home/andreww/Desktop/mountfile/
sudo umount /home/andreww/Desktop/mountfile/
This feels wrong, I did it because I haven't found a way to compile the C program directly inside the Guest Machine.
On the guest machine I don't have gcc, indeed if I try to call it I get:
# -sh: gcc: not found
4] I have started the emulated system with the command
qemu-system-x86_64 \
-M pc \
-append 'root=/dev/vda $extra_append' \
-drive file=${images_dir}/rootfs.ext2,if=virtio,format=raw \
-kernel ${images_dir}/bzImage \
-m 256M \
-net nic,model=virtio \
-net user \
-enable-kvm \
-cpu host \
-smp threads=4
Which I have taken from Ciro Santilli's linux-kernel-module-cheat.
The rootfs.ext2 is the root filesystem which the system is started against, and the bzImage is the kernel image I am going to start the guest machine with.
5] The guest machine is emulated within a shell. This is what I expected since I am virtualizing only the bare kernel and not a whole operating system image.
I am displayed the buildroot login, and I login as root and run the following commands:
When I try to run my hello program I am displayed the following message:
-sh: ./hello_world: not found
I suppose that my program is never executed, even if I have seen by debugging that it's loaded as an elf file into the system.
If I type an random command which doesn't exists, I get the same error.
The goal is to make the hello_world program run in user-space, on top of the kernel, I don't want it to be a part of it.
I agree with you guys, I'm probably missing some libraries or something basic, but I have no idea on what to do.
You can basically run a process instead of init, if that is what you intend to do. To run a process instead of the systems init application, you can either replace the original init or pass init=/path/to/you/application as a kernel parameter to your VM.
But there are several things you should keep in mind:
All libraries need to be present in your VM at the same locations as on the system you have build the application on. This includes also the basic c-library it has been built against. You can get a list of all those libraries using
ldd hello_world
As an example, if you compile the following program
#include <stdio.h>
int main(int argc, char** argv) {
printf("hello world!\n");
return 0;
}
you might get something like this as a result for ldd
$ ldd test
linux-vdso.so.1 => (0x00007ffdba6ba000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8559f50000)
/lib64/ld-linux-x86-64.so.2 (0x000055fc58ece000)
If you replace the init process, you need to take care of console interrupts and handle them properly. If you don't you risk really bad system crashes. As a result, it is a good idea to keep an init process and let that process start your application, if you do not really know what you are up to.
I have seen the output of your command in the past. Are you using a shell-script to start your application? If so, it might indicate that you are currently missing some libraries in your virtual environment.
Edit:
Maybe you could instead create a statically linked application which means to compile all libraries into the same file. On gcc you could use that -static switch for that. See the difference:
$ gcc -o test test.c
$ ldd test
linux-vdso.so.1 => (0x00007ffcd1f22000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f088d43a000)
/lib64/ld-linux-x86-64.so.2 (0x00005580f8a2b000)
$ gcc -static -o test test.c
$ ldd test
not a dynamic executable
As already mentioned you can use ldd to find the required libraries. Another option I would normally use is objdump, as the output is more detailed.
So for my application that would mean something like this (I have shortened the output to the relevant section):
$ objdump -p test
test: file format elf64-x86-64
[...]
Dynamic Section:
NEEDED libc.so.6
INIT 0x00000000004003c8
FINI 0x00000000004005c4
INIT_ARRAY 0x0000000000600e10
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000600e18
FINI_ARRAYSZ 0x0000000000000008
[...]
So in my case, that is libc.so.6 I would need to copy from /lib/x86_64-linux-gnu/libc.so.6 on my host to /lib/x86_64-linux-gnu/libc.so.6 within the virtual machine.
Hope that helps.
I came across a strange issue when using gcc to link the shared library in specified path but not in the standard path.
When I downloaded GNU readline library version 6.3 and compiled it successfully in path $HOME/Downloads.
GNU readline library needs to link libtinfo, so I installed it by sudo apt-get install libtinfo.
After that, I created a small sample test named rl.c to check it. To build my sample project, I also created two symbolic links like follow:
$ ls -lrt ~/Downloads/
drwxr-xr-x 6 sfzhang sfzhang 4096 Mar 2 15:45 readline-6.3
lrwxrwxrwx 1 sfzhang sfzhang 12 Mar 2 16:00 readline -> readline-6.3
$ ls -lrt ~/Downloads/readline-6.3/shlib/
-rwxr-xr-x 1 sfzhang sfzhang 833856 Mar 2 15:36 libreadline.so.6.3
lrwxrwxrwx 1 sfzhang sfzhang 18 Mar 2 16:28 libreadline.so -> libreadline.so.6.3
To use the new built readline library, I export the path to LD_LIBRARY_PATH:
$ echo $LD_LIBRARY_PATH
/home/sfzhang/Downloads/readline/shlib
And, I compiled rl.c using the following command:
$ gcc -o rl rl.c -I$HOME/Downloads -L$HOME/Downloads/readline/shlib -lreadline -ltinfo
Checking the rl linked libraries:
$ ldd rl
linux-vdso.so.1 => (0x00007fffe09a3000)
libreadline.so.6 => /lib/x86_64-linux-gnu/libreadline.so.6 (0x00007fe22d243000)
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007fe22d01a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe22cc8d000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe22d492000)
I also tried anther command, but got the same result:
gcc -o rl rl.c -I$HOME/Downloads -L$HOME/Downloads/readline/shlib -lreadline -ltinfo -Wl,-rpath,$HOME/Downloads/readline/shlib
So, why the rl was liked to /lib/x86_64-linux-gnu/libreadline.so.6 but not $HOME/Downloads/readline/shlib/libreadline.so.
OS: Linux debian 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u1 x86_64 GNU/Linux
gcc: gcc version 4.7.2 (Debian 4.7.2-5)
ldd: ldd (Debian EGLIBC 2.13-38+deb7u6) 2.13
You need a symbolic link called libreadline.so.6 too, not just libreadline.so. The reason is that the soname of libreadline.so.6.3 is libreadline.so.6.
A soname is a "generic" name embedded in the library itself. When you link against a shared library that has a soname, it is that name that gets embedded in your executable and is later looked up by the dynamic linker. (For shared libraries without sonames the filename is used instead, though this is uncommon.) You can tell what the soname of a library is by running e.g.
objdump -p <library> | grep SONAME
The point of sonames is to make it so that your executable is linked against the most generic library name that should be compatible (usually this is the library name with just the major version -- 6 in this case -- tacked on), rather than just the specific (e.g., minor bug fix release) version that you happened to link against.
The output from ldd also tells you that it's looking specifically for libreadline.so.6.
If you really want a path to a library to be hardcoded in the executable, you will need to pass the following to gcc:
-Wl,-rpath=$HOME/Downloads/readline/shlib
setting custom LD_LIBRARY_PATH is not recommended, if you want to use it, use it as a parameter for execution.
LD_LIBRARY_PATH=$HOME/Downloads/readline/shlib ./rc
The are alternative ways to fix your issue:
Link against full path to the .so. E.g.
$ gcc -o rl rl.c -I$HOME/Downloads $HOME/Downloads/readline/shlib/readline.so.6.3 -ltinfo
And as #Ulfalizer suggests, you may need another symbolic link with just the major number.
If you are using LD_LIBRARY_PATH do not forget to export it.
When using -L<path> also use -Wl,-rpath,<path>, so that runtime linker ld.so.2 finds the shared library in the same path as ld does, e.g:
$ gcc -o rl rl.c -I$HOME/Downloads -L$HOME/Downloads/readline/shlib -Wl,-rpath,$HOME/Downloads/readline/shlib -lreadline -ltinfo
When debugging linker issues use readelf -d <binary> command to see which exact versions of shared libraries <binary> needs (NEEDED attribute) and where it looks for them (RPATH attribute) prior to looking in the standard linker directories (configuration in /etc/ld.so.conf/).
I'm trying to use Scons to build a simple project on a server on which I have rights to install stuff only in specific locations (and not in /usr/ ). Since I'm not happy with default compiler the server is offering me, I installed g++4.8 and verified it works just fine. But when I try to use Scons to build a simple project, while it picks up correct g++ (I can get that by checking the version), it's looking for libstdc++ in /usr/ directories instead of the directory where g++4.8 installation resides. E.g. code compiles, but upon execution fails with:
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./main)
Again - this doesn't happen when I call the compiler myself from the terminal.
Even when I add the lib path containing libraries for g++4.8 with LIBPATH option, I get the same error.
Here's my SConscript file:
Import('env')
COMPILER_FLAGS = '-Wall -fopenmp -O3 -std=c++11'
LINK_FLAGS = '-Wall -fopenmp -O3 -std=c++11'
LIB_PATH = 'myfolder/gcc-4.8.2/lib64'
PROGRAM = 'main'
SRC = ['main.cpp', 'Foo.cpp']
env.Append(CPPFLAGS = COMPILER_FLAGS)
env.Append(LINKFLAGS = LINK_FLAGS)
env.Program(target = PROGRAM, source = SRC, LIBPATH = LIB_PATH)
and SConstruct is just
import os
env = Environment(ENV = os.environ)
SConscript('./SConscript', exports=['env'], duplicate=0)
Edit:
I made sure location of my compiler comes in the path before default compiler. But even if I set it explicitly with Environment(CXX=...) it's the same story. Here's the build output:
/mypath/gcc-4.8.2/bin/g++ -o Foo.o -c -Wall -fopenmp -O3 -std=c++11 Foo.cpp
/mypath/gcc-4.8.2/bin/g++ -o main.o -c -Wall -fopenmp -O3 -std=c++11 main.cpp
/mypath/gcc-4.8.2/bin/g++ -o main -Wall -fopenmp -O3 -std=c++11 main.o Foo.o -L/mypath/gcc-4.8.2/lib64
scons: done building targets.
-bash-3.2$
-bash-3.2$
-bash-3.2$ ./main
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./main)
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by ./main)
-bash-3.2$
Yet another edit:
ldd on both manual and scons compile reveal:
linux-vdso.so.1 => (0x00007fff513fd000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6(0x0000003e7f600000)
libm.so.6 => /lib64/libm.so.6 (0x0000003e79600000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003e7de00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003e79200000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e78e00000)
So indeed even manual compile doesn't look for the libs in the right directory (or where I installed the compiler) and the problem isn't with the scons itself, but likely that I didn't configure something right, but then I'm really puzzled as to why the executable runs fine, while it doesn't for scons.
Ok, so my problem wasn't with scons, but with me not giving explicit paths to nonstandard locations of libstdc++ and friends. SO answer over here explains this in more detail:
Linking g++ 4.8 to libstdc++
You're misinterpreting the error. GCC always knows how to find its own libraries, including libstdc++. The problem is that after you've compiled the program the runtime linker (which is not part of GCC, it's part of your OS and comes from glibc) doesn't know how to find the newer libstdc++, so it finds the default system one, which is too old.
The problem and solution are described at in the Libstdc++ FAQ, "How do I insure that the dynamically linked library will be found?", and manual, "Finding Dynamic or Shared Libraries"
This doesn't sound right.
Can you show us what you do to override the compiler?
If you are only doing the above, I don't think your compiler will be overridden with the new version.
You need to do something like
env = Environment(CC='/path/to/gcc')
Or Environment(CXX='/path/to/g++') if you want to override the c++ compiler
Or is your path on your environment setup to have the directory of the custom compiler before the standard compilers directory?
It might help to clean and then run with scons with --debug=presub which will show you the command line used to build each target.
Also your environment is a dictionary, so try printing out different keys to make sure they match what you expect:
print env['CC']
print env['CXX']