Sunday 30 November 2014

Relocating GCC

AdaCore's doinstall is an excellent way of installing the compiler where you want to. Unfortunately, GCC 4.9 uses the shared libgcc_s.1.dylib, which has no @rpath-type constructs, and so the compiler executables expect to find libgcc_s.1.dylib in the place it was built for. (Not to mention libstdc++.6.dylib.)

This could be explained by the fact that GCC is now C++-based, so that exceptions are possible in theory, and - to quote the GCC Link Options manual entry for -shared-libgcc, -static-libgcc,

There are several situations in which an application should use the shared libgcc instead of the static version. The most common of these is when the application wishes to throw and catch exceptions across different shared libraries. In that case, each of the libraries as well as the application itself should use the shared libgcc.

Therefore, the G++ and GCJ drivers automatically add -shared-libgcc whenever you build a shared library or a main executable, because C++ and Java programs typically use exceptions, so this is the right thing to do.

Or, perhaps, the next paragraph:
If GCC finds, at its configuration time, that you have a non-GNU linker or a GNU linker that does not support option --eh-frame-hdr, it links the shared version of libgcc into shared libraries by default.

One possibility: use @executable_path,

install_name_tool \
  -change \
    /opt/gcc-4.9.1/lib/libgcc_s.1.dylib \
    @executable_path/../lib/libgcc_s.1.dylib \
  gnat1
(there would be more ../'s, because gnat1 etc are at $prefix/libexec/gcc/x86_64-apple-darwin13/4.9.1/).

The way I'm actually going is to change it after installation to the actual path. This cunning Makefile target should do the trick:

ins-fixup::
        if [ "$(prefix)" != "$(built_prefix)" ]; then \
          cpp=`find lib -type f -name libstdc++\*.dylib`; \
          gcc=`find lib -type f -name libgcc_s\*.dylib`; \
          for ex in `find $(prefix)/bin -type f -perm -0111`; do \
            if [ "`file $$ex | grep Mach-O`" ]; then \
              install_name_tool \
                -change \
                $(built_prefix)/$$cpp \
                $(prefix)/$$cpp \
                -change \
                $(built_prefix)/$$gcc \
                $(prefix)/$$gcc \
                $$ex; \
            fi; \
          done; \
          for ex in `find $(libexecsubdir) -type f -perm -0111`; do \
            if [ "`file $$ex | grep Mach-O`" ]; then     \
              install_name_tool \
                -change \
                $(built_prefix)/$$cpp \
                $(prefix)/$$cpp \
                -change \
                $(built_prefix)/$$gcc \
                $(prefix)/$$gcc \
                $$ex; \
           fi; \
         done; \
       fi

Problem: fails at gprclean,

/usr/bin/install_name_tool: object: /Users/simon/local-4.9.1/bin/gprclean truncated or malformed object (LC_SEGMENT_64 command 3 fileoff field plus filesize field extends past the end of the file)

The install_name_tool man page suggests

For this tool to work when the install names or rpaths are larger the binary should be built with the ld(1) -headerpad_max_install_names option.

man ld tells us

-headerpad_max_install_names

    Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. Only useful if intend to run install_name_tool to alter the load commands later.

so I rebuilt the gprbuild executables with that switch: now all OK (and tested on Yosemite, too).

No comments:

Post a Comment