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 - gcc

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.

Related

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

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

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?

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.

cross-gcc doesn't search for target as and ld in path?

I've successfully built a couple of cross-gcc compilers, hosted on OSX Lion and targeting both i386-pc-solaris2.10 and x86_64-linux-gnu.
I have 2.22 binutils for those target installed under $BINUTILSROOT and $BINUTILSROOT/bin in my PATH. Reading http://gcc.gnu.org/install/configure.html, in particular
--with-as=pathname
Specify that the compiler should use the assembler pointed to by pathname, rather than the one found by the standard rules to find an assembler, which are:
Unless GCC is being built with a cross compiler, check the libexec/gcc/target/version directory. libexec defaults to exec-prefix/libexec; exec-prefix defaults to prefix, which defaults to /usr/local unless overridden by the --prefix=pathname switch described above. target is the target system triple, such as `sparc-sun-solaris2.7', and version denotes the GCC version, such as 3.0.
If the target system is the same that you are building on, check operating system specific directories (e.g. /usr/ccs/bin on Sun Solaris 2).
Check in the PATH for a tool whose name is prefixed by the target system triple.
Check in the PATH for a tool whose name is not prefixed by the target system triple, if the host and target system triple are the same (in other words, we use a host tool if it can be used for the target as well).
I thought my -gcc (configured with --with-gnu-as --with-gnu-ld) would have picked up respectively i386-pc-solaris2.10-as and x86_64-linux-gnu-as (and corresponding -ld) because they are in $BINUTILSROOT/bin which is in the PATH and so the 3rd bullet from the above list should apply.
But this doesn't seem to work, and I've confirmed with dtrace that -gcc doesn't search for -as and -ld in the PATH.
The only solution I've found to be working is to also fully specify as and ld adding
--with-as=$BINUTILSROOT/bin/-as --with-ld=$BINUTILSROOT/bin/-ld
when configuring gcc.
Am I misinterpreting gcc docs, or this is the only way to have cross-compilation working?
Ordinarily you'd install a cross-compiler in the same directory as your cross-binutils. If you do that it'll Just Work.
If you're not installing the compiler into the same directory because you want to "stage" it for building a package, then you should configure with the --prefix of the final installed location (in which the binutils should already be present), and then install with
make DESTDIR=/path/to/staging/dir install
to override the prefix setting. You'd then copy those files into the true prefix (presumably as part of a package install) before you use them.
If you don't want to install in the same directory for another reason then you have to specify the path as you've discovered. There are other ways to make it work, but --with-as is the intended solution. If you really don't like that solution, then you can do
make configure-gcc
ln -s $BINUTILSROOT/bin/as gcc/as
ln -s $BINUTILSROOT/bin/ld gcc/ld
That will make the build work (IIRC), but the final installed compiler will still look in the standard places. In fact, this works because, during build only, the gcc directory is one of the standard places.
The reason for all this is that it doesn't use "x86_64-linux-gnu-as": it actually uses "prefix/x86_64-linux-gnu/bin/as" and if that doesn't exist it looks in the other standard places for "as", and typically finds the host "/usr/bin/as" which doesn't work well (and leads to very confusing error messages).

Should I care that the symbol version dump is missing? How do I get one?

I am trying to compile a driver that we have from source and I am working through the issues with a new target environment. One of the slightly disturbing things I see is the following warning:
WARNING: Symbol version dump /usr/src/linux-2.6.38/Module.symvers
is missing; modules will have no dependencies and modversions.
I spent a fair amount of time looking on the web and this is shown in output frequently when other questions are asked, but I didn't see any commentary about whether or not this is an issue.
In any case, how would I tell linux/ubuntu to generate Module.symvers?
Module.symvers is generated when the kernel itself is compiled and ought to be provided to the user as part of the kernel build environment package, however that may look on Ubuntu (possibly broken there?) Fedora and openSUSE for example ship one or more “kernel-devel” (and/or similarly-named) packages that ship this build environment and make the file reachable through /lib/modules/<version>/build/Module.symvers. When using a self-compiled kernel, substitue /lib/modules/version/build for the appropriate path to the build directory (where all the .o files are).

Resources