Execute binaries / tests on host that are built by non-host toolchain - gcc

Scenario:
We would like to build our sources by an external / hermetic toolchain so all includes, libs and tools from the host are ignored
To do so, a new toolchain is introduced to Bazel-Configuration. >>> working well
Using -Wl,-rpath=$ORIGIN/%{runtime_library_search_directories}/... the relative path from the binary to the libs that should be loaded on runtime gets defined >>> works quite well
When doing bazel run the Binary is executed but the host's LD is being used
To get rid of this a wrapper (written in Bash) is injected using --run_under. >>> dirty workaround
Problem:
--run_under could only be used once. We also need this option to execute tests within a specific environment and so this option is not the way of choice for us. IMHO it's also a bit dirty workaround.
We also tried to use -Wl,--dynamic-linker=<<PATH_TO_LD>>. But we were not able to get neither a relative nor an absolute path to LD when linking the executable.
Questions:
Is there ...
... any way to get the absolute/relative path to LD when linking?
... any other way of running a binary on Host using a toolchain?
... a possibility to do sandboxing/chroot so the correct LD of the toolchain is being used automatically?
Sidenotes:
Bazel 1.1.0 is used
the toolchain is GCC8 build from sources
the host is an Ubuntu 18.04.1 image running in docker

Related

Cmake implicit link directory paths and how to set/override to enable building a common sandbox on different OS versions w.r.t lib dependencies

I work with a large source tree that builds with cmake (rev 3.9.6). I often check the tree
out into a common NFS mounted path, and then build the sandbox on different machines (mounted
thru a common NFS path).
I would ideally like to be able to first build the tree on a CentOS7 system, and
then log into a CentOS8 system, type make from the top of the tree, and see
there is no need to reconfigure anything (cmake) or rebuild anything (all
make dependencies satisfied).
All the binaries built on the CentOS7 system run on the CentOS8 system and it looks
like all tests pass on both CentOS7 and CentOS8. It also works to build the source tree
from scratch on a CentOS8 system as well (after dealing with how compiler warnings changed for
the newer compilers on CentOS8).
In practice I've found that building first on CentOS7 causes cmake generated files
(build.make) to end up with a CentOS7 gcc compiler specific path in them as a make dependency.
Or it looks to me like these generated cmake files from the CentOS7 build,
<top of tree>/CMakeFiles/3.9.6/{CMakeCCompiler.cmake,CMakeCXXCompiler.cmake}
set the serarch paths for the compiler and system libraries via,
set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib")
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib")
The first path, /usr/lib/gcc/x86_64-redhat-linux/4.8.5, is unique to the compiler
version on CentOS7 and so does not exist on CentOS8. A cmake generated dependency using this path (in build.make files)stops
make dead in its tracks when typing make at the top of the sandbox in NFS on a CentOS8 machine
because it can't resolve the non-existant path:
foo: /usr/lib/gcc/x86_64-redhat-linux/4.8.2/libgomp.so
However, that pathname to libgomp.so thru the CentOS7 gcc path is a soft link,
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/libgomp.so -> ../../../../lib64/libgomp.so.1.0.0
Or the same version of libgomp.so (libgomp.so.1.0.0) will be referenced on CentOS7 by:
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/libgomp.so
/usr/lib64/libgomp.so.1
Alternately, on CentOS8 libgomp.so.1.0.0 is found via the paths:
/usr/lib/gcc/x86_64-redhat-linux/8/libgomp.so
/usr/lib64/libgomp.so.1
I've experimented with removing the gcc specific path from the implicit link cmake
variables to instead have,
set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/usr/lib64;/lib64;/usr/lib")
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib64;/lib64;/usr/lib")
But the CentOS7 compiler specific path will still be found via the /usr/lib entry and I cannot exclude
/usr/lib as a search path altogether. I would really just like to exclude /usr/lib/gcc (?) as an implicit search path
and even then I would have to specify linking against libgomp.so.1 or libgomp.so.1.0.0 instead of libgomp.so.
(BTW - I'm not sure setting those cmake variables explicitly before the project statement in the top level
CMakeLists.txt file worked, but I did not see any way to modify/change how the CMakeC[XX]Compiler.cmake files
are generated in the first place).
Now I could add a custom command to trigger PRE_BUILD on every cmake target to edit any cmake generated build.make
with that the dependency such that,
foo: /usr/lib/gcc/x86_64-redhat-linux/4.8.2/libgomp.so
... edited thru custom command becomes...
foo: /lib64/libgomp.so.1.0.0
This does in fact work and allows make to finish checking (and find) all dependencies when I run it from the top
of the tree on CentOS7 or CentOS8 (after first building on CentOS7). But I would prefer not to retroactively edit
cmake generated files.
So happy to hear any suggestions or if it's just unreasonable to expect to achieve this kind
of parity when building the same NFS mounted sandbox on two revs of the OS with different compiler (revisions and paths), i.e.,
the "right" thing to do is to always re-run cmake on CentOS8 and then rebuild everything on CentOS8.

How to distribute gcc compiler binaries?

I want to distribute a gcc compiler within our company. I built required version from the sources, installed it in specified directory on my machine (all machines, btw, have the same architecture). I expected that I can put contents of this directory on the server and with the next update all my colleagues will get a new compiler.
But of course that doesn't work.
I tried to search for a ready solution for the linux but couldn't find anything.
Right now if I try to compile some basic program I get an error:
gcc: error trying to exec 'cc1plus': execvp: No such file or directory.
But this file was build and indeed resides in one of the subfolders of main install directory. I tried to explicitly specify the path to this subprogramm using GCC_EXEC_PREFIX and COMPILER_PATH environment variables but with no luck.
Is it possible to achieve what I want?

Cannot switch gcc version

On a Ubuntu 16.04.2 I am trying to compile code with a newer gcc.
So I did the following:
in the workspace create a file tools/cpp/CROSSTOOL
copy latest CROSSTOOL content from bazel repo into that
in CROSSTOOL file, under identifier local_linux, change /usr/bin/gcc to /usr/bin/gcc-7
Call bazel build //foo
When I output the commandline executed to build //foo:foo, it calls /usr/bin/gcc.
Can someone point out what I am doing wrong?
You are probably using static crosstool that is only used for bootstrapping and some tests (and some other details). What bazel usually uses is tools/cpp/CROSSTOOL.tpl which is a template populated by tools/cpp/cc_configure.bzl. This is the script that inspects CC environment variable and if set, it uses that for gcc.
Of course you can use static crosstool, you just have to tell bazel about it using --crosstool_top=//some/crosstool flag. You can save it in a project local .bazelrc.

compile simple hello-world c program for mips netbsd

the last week I have been trying to set-up a compiler which can compile to netbsd with mips architecture.
I cannot find anything on the internet how to do this. All documents refer to compiling the kernel to the architecture but not programs.
How can this be so hard....
host is netbsd amd64 machine
Set the compiler appropriately. Point it at the version of gcc in your TOOLDIR. In this case, something like mips--netbsd-gcc. Definitely make sure TOOLDIR is on your path, so the driver can find the proper assembler, proper loader, and proper libraries.
Take a look at the Makefile in any of src/bin/* as an example, and read through the system mk include files referenced (in src/share/mk)
Generally speaking, the goal is to have a working cross-compiler and a filesystem root for the target, all installed on your development machine. The target root is needed since you need all sorts of libraries to build userland applications. Those libraries need to be compiled for the target, not for the host.
Assuming you build everything from source, it goes as follows:
Choose a prefix for the toolchain (say /opt/mips) and another prefix for the root filesystem of the target (say /opt/target). All of those are on your development machine, not on the target!
Configure, build and install the cross-compiler for your target. This goes into the toolchain prefix.
Configure, build and install the kernel for your target, into the target root prefix. This should install the necessary kernel development headers needed later. If you can install such headers without compiling the kernel, more power to you, of course.
Configure, build and install the C library (say glibc) for your target, into the target root.
Configure, build and install whatever other libraries your userland application needs - into the target root.
Finally, configure, build and install the userland application. Once installed into the target root, you can copy it over to the target into the same prefix (say /opt/target as suggested before).
Generally to install into a different prefix - one that overlaps stuff on your build host (like /usr) - you'd need to do some tricks to fool make install into seeing the target prefix instead of your own. A simple approach would be to have a chroot environment on your build host, where you can bind-mount the prefix (say /usr) read-only, with a writable (mount_union) overlay on top of it.
When you build stuff for the target, you need to pass proper arguments to configure, of course.

USB GCC Development Environment with Libraries

I'm trying to get something of an environment on a usb stick to develop C++ code in. I plan to use other computers, most of the time linux, to work on this from a command line using g++ and make.
The problem is I need to use some libraries, like Lua and OpenGL, which the computers don't have. I cannot add them to the normal directories, I do not have root on these computers. Most of the solutions I've found involve putting things in /usr/lib/ and the like, but I cannot do that. I've also attempted adding options like '-L/media//lib', which is where they are kept, and it didn't work. When compiling, I get the same errors I got when first switching to an OS with the libraries not installed.
Is there somewhere on the computer outside of /usr/ I can put them, or a way to make gcc 'see' them?
You need more than the libraries to be able to compile code utilizing those libraries. (I'm assuming Linux here, things might be slightly different on e.g. OSX,BSDs,Cygwin,Mingw..)
Libraries
For development you need these 3 things when your code uses a library:
The library header files, .h files
The library development files, libXXX.so or libXXX.a typically
The library runtime files , libXXX.so.Y where Y is a version number. These are not needed if you statically link in the library.
You seem to be missing the header files (?) Add them to your usb stick, say under /media/include
Development
Use (e.g.) the compiler flag -I/media/include when compiling source code to refer to a non-standard location of header files.
Use the compiler/linker flag -L/media/lib to refer to non-standard location of libraries.
You might be missing the first step.
Running
For dynamically linked libraries, the system will load those only from default locations, typically /lib/ , /usr/lib/
Learn the ldd tool to help debug this step.
You need to tell the system where to load additional libraries when you're running a program, here's 3 alternatives:
Systemwide: Edit /etc/ld.so.conf and add /media/libs there. Run ldconfig -a afterwards.
Local, to the current shell only. set the LD_LIBRARY_PATH environment variable to refer to /media/lib, run export LD_LIBRARY_PATH=/media/lib
Executable: Hardcode the non-standard library path in the executable. You add this to the linking step when creating your executable: -Wl,-rpath,/media/lib
Etc.
There could be other reasons things are not working out, if so,
show us the output of ls -l /media/libs , and where you put the library header files, the command line you use to compile/link, and the exact errors you get.
Missing the headers and/or development libraries (for dynamic libraries there is usually a symlink from a libXXX.so to a libXXX.so.Y , the linker needs the libXXX.so , it will not look directly at libXXX.so.Y)
using libraries not compatible with your current OS/architecture. (libraries compiled on one linux distro is often not compatible with another distro, or even another minor version of the same distro)
using an usb stick with a FAT32 filesystem, you'll get in trouble with symlinks..

Resources