Search code examples
gccbuildcentosshared-libraries

How to build and install gcc with built-in rpath?


I'm trying to build and install my own gcc 4.7.2 in /usr/local to use in place of the gcc 4.4.6 in /usr. (This is on CentOS 6.3.)

gcc makes executables and dynamic libraries that dynamically link to its own dynamic libraries, e.g. libstdc++.so. How do I build and install gcc so that the generated binaries automatically get a linker -rpath option (-rpath /usr/local/lib64) that causes dynamic libraries in /usr/local/lib64 to be linked instead of those in /usr/lib64 or /lib64?

If it works properly, after I build an executable with the gcc without specifying "-Wl,-rpath=/usr/local/lib64", when I ldd the executable, it should show /usr/local/lib64/libstdc++.so.6 instead of /usr/lib64/libstdc++.so.6. Similarly for the libgcc_s.so.1.

I have tried different approaches, including specifying LDFLAGS_FOR_TARGET=-Wl,-rpath=/usr/local/lib64,-rpath=/usr/local/lib on the 'configure' command-line, but nothing worked.


Solution

  • If you don't want to export paths there's an alternative solution:

    with your toolchain in the PATH:

    gcc -dumpspecs > specsfile
    

    edit specsfile and in the section link add your -rpath example:

    *link:
    %{!static:--eh-frame-hdr} -m %(link_emulation) %{shared:-shared}   %{!shared:     %{!static:       %{rdynamic:-export-dynamic}       -dynamic-linker %(dynamic_linker)}       %{static:-static}} -rpath /usr/local/lib64
    

    at this point you can test if it work with:

    g++ -specs=specsfile test.cpp
    readelf -d a.out |grep RPATH
    

    if it work you can make it permanent (no need to pass -specs everytime)

    strace -fF -o /tmp/g++.log g++ test.cpp
    grep specs /tmp/g++.log
    

    the grep should show the paths where gcc look for the default specs file.

    The specs files are flexible enough to allow conditional linking depending on variables, example:

    {!static: %{mabi=n32:-rpath-link %R/lib32:%R/usr/lib32} %{mabi=64:-rpath-link %R/lib64:%R/usr/lib64} %{mabi=32:-rpath-link %R/lib:%R/usr/lib}}
    

    should use different and multiple paths depending on mabi (untested, yet), %R should be the sysroot path, can be changed with needed full path.

    There's also a --with-specs= option gcc configure eventually to be used at build time, not clear to me yet how to use with the link section (working on it).

    --with-specs="%{shared:-Wl,-rpath -Wl,$(DESTDIR)/lib}%{!shared:-Wl,-rpath -Wl,$(DESTDIR)/lib}"
    

    It work, I used both shared and not !shared just for test, probably some smarter condition should be used, note that it isn't reported with -dumpspecs.

    Reading through some thread of the gcc mailing list I had the impression specs aren't liked by everyone (but if I'm not wrong 4.9 add another option --with-extra-specs) instead preferred way to do such customizations appears to be configure.host, but I'm done and not looking into it, have fun! :-)

    see also: gcc faq rpath

    update above

    I don't know if you can set a pre-defined rpath, probably if you can would be in the linker ld of binutils not in gcc/g++, but why would you do that?

    Just export LD_LIBRARY_PATH at runtime and LD_RUN_PATH at build time

    export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH
    ldd a.out
    

    ldd should show the paths you exported.

    To quote a message given when a shared library is built with libtool:

    If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following:

    • add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution
    • add LIBDIR to the `LD_RUN_PATH' environment variable during linking
    • use the `-Wl,--rpath -Wl,LIBDIR' linker flag
    • have your system administrator add LIBDIR to `/etc/ld.so.conf'

    See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages.

    for completeness the Makefile I used for testing the thing, all the configure options, environment variables (see boot ldflags) I tried didn't work, --enable-rpath included.

    use with mkdir ~/gcc copy the Makefile below into ~/gcc then cd ~/gcc && make build-gcc

    notice the options used are only for this test case, don't use as reference.

    FETCH = aria2c --file-allocation=none -c -d dl
    NICE = nice -n 19
    PARALLEL = -j4
    DESTDIR = $(HOME)/gcc/install
    SRCDIR = $(HOME)/gcc/src
    
    all:
    
    # if more downloads are added just remove {dl,src}/*-my-stamp not the .bak
    # the .bak should avoid to rebuild targets because of timestamp
    touch_stamp = if [ -f [email protected] ]; then \
            touch -r [email protected] $@; \
        else \
            touch $@ [email protected]; \
        fi
    
    dl/dl-my-stamp:
        $(FETCH) https://ftp.gnu.org/gnu/gcc/gcc-4.7.2/gcc-4.7.2.tar.bz2
        $(FETCH) http://ftp.gnu.org/gnu/gmp/gmp-4.3.2.tar.bz2
        $(FETCH) ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz
        $(FETCH) https://ftp.gnu.org/gnu/mpfr/mpfr-2.4.2.tar.bz2
        $(FETCH) --check-certificate=false http://www.mirrorservice.org/sites/sourceware.org/pub/binutils/snapshots/binutils-2.24.51.tar.bz2 \
            ftp://sourceware.org/pub/binutils/snapshots/binutils-2.24.51.tar.bz2
        $(touch_stamp)
    
    untar_dep = src/untar-my-stamp
    src/untar-my-stamp: dl/dl-my-stamp
        mkdir -p src
        tar -C src -xjf dl/gcc-4.7.2.tar.bz2
        tar -C src -xjf dl/gmp-4.3.2.tar.bz2
        tar -C src -xzf dl/mpc-0.8.1.tar.gz
        tar -C src -xjf dl/mpfr-2.4.2.tar.bz2
        tar -C src -xjf dl/binutils-2.24.51.tar.bz2
        $(touch_stamp)
    
    define configure-rule
    $(1)_install = $(DESTDIR)/$(1)-install-my-stamp
    $(1)_builddir = $$($(1)_dir)/build
    $(DESTDIR)/$(1)-install-my-stamp: $$($(1)_deps)
        mkdir -p $$($(1)_builddir)
        cd $$($(1)_builddir) && \
            $$($(1)_env) ../configure --cache-file=$(SRCDIR)/$(1)-config.cache \
                $$($(1)_configure)
        $(NICE) make -C $$($(1)_builddir) $$($(1)_make_target) $(PARALLEL)
    ifneq ($$($(1)_post_make),)
        $$($(1)_post_make)
    endif
        touch $$@
    .PHONY: build-$(1) clean-$(1)
    build-$(1): $$($(1)_install)
    clean-$(1):
        -rm -fr $$($(1)_builddir)
    endef
    
    gmp_dir = src/gmp-4.3.2
    gmp_env =   CONFIG_SITE=$(SRCDIR)/config.site
    gmp_configure = --prefix=$(DESTDIR) \
                    --disable-shared --enable-static --enable-cxx
    gmp_deps = $(untar_dep)
    gmp_make_target = install
    $(eval $(call configure-rule,gmp))
    
    mpfr_dir = src/mpfr-2.4.2
    mpfr_env =  CONFIG_SITE=$(SRCDIR)/config.site
    mpfr_configure = --prefix=$(DESTDIR) \
                    --disable-shared --enable-static \
                    --with-gmp=$(DESTDIR)
    mpfr_deps = $(untar_dep) $(gmp_install)
    mpfr_make_target = install
    $(eval $(call configure-rule,mpfr))
    
    mpc_dir = src/mpc-0.8.1
    mpc_env =   CONFIG_SITE=$(SRCDIR)/config.site
    mpc_configure = --prefix=$(DESTDIR) \
                    --disable-shared --enable-static \
                    --with-gmp=$(DESTDIR) --with-mpfr=$(DESTDIR)
    mpc_deps = $(untar_dep) $(gmp_install) $(mpfr_install)
    mpc_make_target = install
    $(eval $(call configure-rule,mpc))
    
    gcc_dir = src/gcc-4.7.2
    gcc_env =   CONFIG_SITE=$(SRCDIR)/config.site \
        CFLAGS="-I/usr/include/i386-linux-gnu" \
        CXXFLAGS="-I/usr/include/i386-linux-gnu"
    gcc_configure = --prefix=$(DESTDIR) \
                    --libdir=$(DESTDIR)/lib \
                    --with-local-prefix=$(DESTDIR) \
                    --with-gmp=$(DESTDIR) --with-mpfr=$(DESTDIR) \
                    --with-mpc=$(DESTDIR) \
                    --disable-bootstrap \
                    --enable-languages=c,c++ \
                    --disable-libgomp --disable-multilib \
                    --disable-libmudflap --disable-libssp \
                    --disable-libquadmath \
                    --enable-rpath \
                    MAKEINFO=missing
    gcc_deps = $(untar_dep) $(gmp_install) $(mpfr_install) $(mpc_install)
    gcc_make_target = 
    gcc_post_make = make -C $(gcc_builddir) install
    $(eval $(call configure-rule,gcc))
    
    binutils_dir = src/binutils-2.24.51
    #binutils_env = LDFLAGS=-Wl,-rpath\ $(DESTDIR)/lib
    binutils_env = CONFIG_SITE=$(SRCDIR)/config.site \
        CFLAGS="-I/usr/include/i386-linux-gnu" \
        BOOT_LDFLAGS="-rpath-link=$(DESTDIR)/lib -rpath=$(DESTDIR)/lib"
    binutils_configure = --prefix=$(DESTDIR) \
                    --libdir=$(DESTDIR)/lib \
                    --with-gmp=$(DESTDIR) \
                    --enable-rpath
    binutils_deps = $(untar_dep) $(gmp_install)
    #binutils_make_target = install
    binutils_post_make = make -C $(binutils_builddir) install
    $(eval $(call configure-rule,binutils))
    
    
    .PHONY: env
    env:
        @echo export PATH=$(DESTDIR)/bin:\$$PATH
        @echo export LIBRARY_PATH=/usr/lib/i386-linux-gnu