aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2019-07-15 15:03:50 -0700
committerDylan Baker <dylan@pnwbakers.com>2019-08-14 13:13:23 -0700
commitd708b274a1de8acda65a6f35d1a016fd2339da14 (patch)
tree107a408a2de227cd5aa95f4e5e00a538ce50a4dd
parentb5b48f33bc37f0d83bf92f9af74311c8c030cb7f (diff)
downloadmeson-d708b274a1de8acda65a6f35d1a016fd2339da14.zip
meson-d708b274a1de8acda65a6f35d1a016fd2339da14.tar.gz
meson-d708b274a1de8acda65a6f35d1a016fd2339da14.tar.bz2
linkers: Add GNU linkers
-rw-r--r--mesonbuild/linkers.py176
1 files changed, 176 insertions, 0 deletions
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index c813e3e..c0a07ec 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -194,6 +194,36 @@ class CcrxLinker(StaticLinker):
return ['-nologo', '-form=library']
+def prepare_rpaths(raw_rpaths: str, build_dir: str, from_dir: str) -> typing.List[str]:
+ # The rpaths we write must be relative if they point to the build dir,
+ # because otherwise they have different length depending on the build
+ # directory. This breaks reproducible builds.
+ internal_format_rpaths = [evaluate_rpath(p, build_dir, from_dir) for p in raw_rpaths]
+ ordered_rpaths = order_rpaths(internal_format_rpaths)
+ return ordered_rpaths
+
+
+def order_rpaths(rpath_list: typing.List[str]) -> typing.List[str]:
+ # We want rpaths that point inside our build dir to always override
+ # those pointing to other places in the file system. This is so built
+ # binaries prefer our libraries to the ones that may lie somewhere
+ # in the file system, such as /lib/x86_64-linux-gnu.
+ #
+ # The correct thing to do here would be C++'s std::stable_partition.
+ # Python standard library does not have it, so replicate it with
+ # sort, which is guaranteed to be stable.
+ return sorted(rpath_list, key=os.path.isabs)
+
+
+def evaluate_rpath(p: str, build_dir: str, from_dir: str) -> str:
+ if p == from_dir:
+ return '' # relpath errors out in this case
+ elif os.path.isabs(p):
+ return p # These can be outside of build dir.
+ else:
+ return os.path.relpath(os.path.join(build_dir, p), os.path.join(build_dir, from_dir))
+
+
class DynamicLinker(metaclass=abc.ABCMeta):
"""Base class for dynamic linkers."""
@@ -368,3 +398,149 @@ class PosixDynamicLinkerMixin:
def get_search_args(self, dirname: str) -> typing.List[str]:
return ['-L', dirname]
+
+
+class GnuLikeDynamicLinkerMixin:
+
+ """Mixin class for dynamic linkers that provides gnu-like interface.
+
+ This acts as a base for the GNU linkers (bfd and gold), the Intel Xild
+ (which comes with ICC), LLVM's lld, and other linkers like GNU-ld.
+ """
+
+ _BUILDTYPE_ARGS = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': ['-Wl,-O1'],
+ 'minsize': [],
+ 'custom': [],
+ } # type: typing.Dict[str, typing.List[str]]
+
+ def get_pie_args(self) -> typing.List[str]:
+ return ['-pie']
+
+ def get_asneeded_args(self) -> typing.List[str]:
+ return ['-Wl,--as-needed']
+
+ def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]:
+ if not args:
+ return args
+ return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive']
+
+ def get_allow_undefined_args(self) -> typing.List[str]:
+ return ['-Wl,--allow-shlib-undefined']
+
+ def get_lto_args(self) -> typing.List[str]:
+ return ['-flto']
+
+ def sanitizer_args(self, value: str) -> typing.List[str]:
+ if value == 'none':
+ return []
+ return ['-fsanitize=' + value]
+
+ def invoked_by_compiler(self) -> bool:
+ """True if meson uses the compiler to invoke the linker."""
+ return True
+
+ def get_coverage_args(self) -> typing.List[str]:
+ return ['--coverage']
+
+ def export_dynamic_args(self, env: 'Environment') -> typing.List[str]:
+ m = env.machines[self.for_machine]
+ if m.is_windows() or m.is_cygwin():
+ return ['-Wl,--export-all-symbols']
+ return ['-Wl,-export-dynamic']
+
+ def import_library_args(self, implibname: str) -> typing.List[str]:
+ return ['-Wl,--out-implib=' + implibname]
+
+ def thread_flags(self, env: 'Environment') -> typing.List[str]:
+ if env.machines[self.for_machine].is_haiku():
+ return []
+ return ['-pthread']
+
+ def no_undefined_args(self) -> typing.List[str]:
+ return ['-Wl,--no-undefined']
+
+ def fatal_warnings(self) -> typing.List[str]:
+ return ['-Wl,--fatal-warnings']
+
+ def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
+ suffix: str, soversion: str, darwin_versions: typing.Tuple[str, str],
+ is_shared_module: bool) -> typing.List[str]:
+ m = env.machines[self.for_machine]
+ if m.is_windows() or m.is_cygwin():
+ # For PE/COFF the soname argument has no effect
+ return []
+ sostr = '' if soversion is None else '.' + soversion
+ return ['-Wl,-soname,{}{}.{}{}'.format(prefix, shlib_name, suffix, sostr)]
+
+ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
+ rpath_paths: str, build_rpath: str,
+ install_rpath: str) -> typing.List[str]:
+ m = env.machines[self.for_machine]
+ if m.is_windows() or m.is_cygwin():
+ return []
+ if not rpath_paths and not install_rpath and not build_rpath:
+ return []
+ args = []
+ origin_placeholder = '$ORIGIN'
+ processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir)
+ # Need to deduplicate rpaths, as macOS's install_name_tool
+ # is *very* allergic to duplicate -delete_rpath arguments
+ # when calling depfixer on installation.
+ all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths])
+ # Build_rpath is used as-is (it is usually absolute).
+ if build_rpath != '':
+ all_paths.add(build_rpath)
+
+ # TODO: should this actually be "for (dragonfly|open)bsd"?
+ if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd():
+ # This argument instructs the compiler to record the value of
+ # ORIGIN in the .dynamic section of the elf. On Linux this is done
+ # by default, but is not on dragonfly/openbsd for some reason. Without this
+ # $ORIGIN in the runtime path will be undefined and any binaries
+ # linked against local libraries will fail to resolve them.
+ args.append('-Wl,-z,origin')
+
+ # In order to avoid relinking for RPATH removal, the binary needs to contain just
+ # enough space in the ELF header to hold the final installation RPATH.
+ paths = ':'.join(all_paths)
+ if len(paths) < len(install_rpath):
+ padding = 'X' * (len(install_rpath) - len(paths))
+ if not paths:
+ paths = padding
+ else:
+ paths = paths + ':' + padding
+ args.append('-Wl,-rpath,' + paths)
+
+ # TODO: should this actually be "for solaris/sunos"?
+ if mesonlib.is_sunos():
+ return args
+
+ # Rpaths to use while linking must be absolute. These are not
+ # written to the binary. Needed only with GNU ld:
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=16936
+ # Not needed on Windows or other platforms that don't use RPATH
+ # https://github.com/mesonbuild/meson/issues/1897
+ #
+ # In addition, this linker option tends to be quite long and some
+ # compilers have trouble dealing with it. That's why we will include
+ # one option per folder, like this:
+ #
+ # -Wl,-rpath-link,/path/to/folder1 -Wl,-rpath,/path/to/folder2 ...
+ #
+ # ...instead of just one single looooong option, like this:
+ #
+ # -Wl,-rpath-link,/path/to/folder1:/path/to/folder2:...
+ args.extend(['-Wl,-rpath-link,' + os.path.join(build_dir, p) for p in rpath_paths])
+
+ return args
+
+
+class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker):
+
+ """Representation of GNU ld.bfd and ld.gold."""
+
+ pass