diff options
author | TheCharlatan <seb.kung@gmail.com> | 2018-10-13 17:07:56 +0200 |
---|---|---|
committer | TheCharlatan <seb.kung@gmail.com> | 2018-12-04 02:21:06 +0100 |
commit | 5a76933903ef2f29532588d359862831a3644128 (patch) | |
tree | 88dd643545c7fab6b9bca156dfb9ed8d230db2d0 | |
parent | Merge pull request #4781 (diff) | |
download | monero-5a76933903ef2f29532588d359862831a3644128.tar.xz |
Add glibc back compat code
To ensure that the binaries compiled by gitian run across many linux
distributions, enforce 2.17 as the minimum libc version supported.
-rw-r--r-- | CMakeLists.txt | 9 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | contrib/gitian/gitian-linux.yml | 2 | ||||
-rwxr-xr-x | contrib/gitian/symbol-check.py | 163 | ||||
-rw-r--r-- | src/common/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/common/compat/glibc_compat.cpp | 98 |
6 files changed, 113 insertions, 165 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c38c673a..78dc14acb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -696,6 +696,13 @@ else() set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap") endif() + if(BACKCOMPAT) + add_definitions(-DFDELT_TYPE=long\ int) + add_linker_flag_if_supported(-Wl,--wrap=__divmoddi4 LD_BACKCOMPAT_FLAGS) + add_linker_flag_if_supported(-Wl,--wrap=glob LD_BACKCOMPAT_FLAGS) + message(STATUS "Using Lib C back compat flags: ${LD_BACKCOMPAT_FLAGS}") + endif() + # some windows linker bits if (WIN32) add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS) @@ -708,7 +715,7 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${LD_BACKCOMPAT_FLAGS}") # With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that # is fixed in the code (Issue #847), force compiler to be conservative. @@ -521,6 +521,8 @@ The required packages are the names for each toolchain on apt. Depending on your Using `depends` might also be easier to compile Monero on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above. +The produced binaries still link libc dynamically. If the binary is compiled on a current distribution, it might not run on an older distribution with an older installation of libc. Passing `-DBACKCOMPAT=ON` to cmake will make sure that the binary will run on systems having at least libc version 2.17. + ## Installing Monero from a package **DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.** diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 473a7720d..3bb25c314 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -152,7 +152,7 @@ script: | for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON make DISTNAME=monero-${i} mv bin ${DISTNAME} diff --git a/contrib/gitian/symbol-check.py b/contrib/gitian/symbol-check.py deleted file mode 100755 index 6808e77da..000000000 --- a/contrib/gitian/symbol-check.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014 Wladimir J. van der Laan -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -''' -A script to check that the (Linux) executables produced by gitian only contain -allowed gcc, glibc and libstdc++ version symbols. This makes sure they are -still compatible with the minimum supported Linux distribution versions. - -Example usage: - - find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py -''' -import subprocess -import re -import sys -import os - -# Debian 6.0.9 (Squeeze) has: -# -# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B) -# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6) -# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6) -# -# Ubuntu 10.04.4 (Lucid Lynx) has: -# -# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all) -# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all) -# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names) -# -# Taking the minimum of these as our target. -# -# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: -# GCC 4.4.0: GCC_4.4.0 -# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3 -# (glibc) GLIBC_2_11 -# -MAX_VERSIONS = { -'GCC': (4,4,0), -'CXXABI': (1,3,3), -'GLIBCXX': (3,4,13), -'GLIBC': (2,11) -} -# See here for a description of _IO_stdin_used: -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 - -# Ignore symbols that are exported as part of every executable -IGNORE_EXPORTS = { -'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' -} -READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') -CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') -# Allowed NEEDED libraries -ALLOWED_LIBRARIES = { -# bitcoind and bitcoin-qt -'libgcc_s.so.1', # GCC base support -'libc.so.6', # C library -'libpthread.so.0', # threading -'libanl.so.1', # DNS resolve -'libm.so.6', # math library -'librt.so.1', # real-time (clock) -'ld-linux-x86-64.so.2', # 64-bit dynamic linker -'ld-linux.so.2', # 32-bit dynamic linker -# bitcoin-qt only -'libX11-xcb.so.1', # part of X11 -'libX11.so.6', # part of X11 -'libxcb.so.1', # part of X11 -'libfontconfig.so.1', # font support -'libfreetype.so.6', # font parsing -'libdl.so.2' # programming interface to dynamic linker -} - -class CPPFilt(object): - ''' - Demangle C++ symbol names. - - Use a pipe to the 'c++filt' command. - ''' - def __init__(self): - self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) - - def __call__(self, mangled): - self.proc.stdin.write(mangled + '\n') - self.proc.stdin.flush() - return self.proc.stdout.readline().rstrip() - - def close(self): - self.proc.stdin.close() - self.proc.stdout.close() - self.proc.wait() - -def read_symbols(executable, imports=True): - ''' - Parse an ELF executable and return a list of (symbol,version) tuples - for dynamic, imported symbols. - ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) - syms = [] - for line in stdout.splitlines(): - line = line.split() - if len(line)>7 and re.match('[0-9]+:$', line[0]): - (sym, _, version) = line[7].partition('@') - is_import = line[6] == 'UND' - if version.startswith('@'): - version = version[1:] - if is_import == imports: - syms.append((sym, version)) - return syms - -def check_version(max_versions, version): - if '_' in version: - (lib, _, ver) = version.rpartition('_') - else: - lib = version - ver = '0' - ver = tuple([int(x) for x in ver.split('.')]) - if not lib in max_versions: - return False - return ver <= max_versions[lib] - -def read_libraries(filename): - p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - libraries = [] - for line in stdout.splitlines(): - tokens = line.split() - if len(tokens)>2 and tokens[1] == '(NEEDED)': - match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) - if match: - libraries.append(match.group(1)) - else: - raise ValueError('Unparseable (NEEDED) specification') - return libraries - -if __name__ == '__main__': - cppfilt = CPPFilt() - retval = 0 - for filename in sys.argv[1:]: - # Check imported symbols - for sym,version in read_symbols(filename, True): - if version and not check_version(MAX_VERSIONS, version): - print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version)) - retval = 1 - # Check exported symbols - for sym,version in read_symbols(filename, False): - if sym in IGNORE_EXPORTS: - continue - print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym))) - retval = 1 - # Check dependency libraries - for library_name in read_libraries(filename): - if library_name not in ALLOWED_LIBRARIES: - print('%s: NEEDED library %s is not allowed' % (filename, library_name)) - retval = 1 - - sys.exit(retval) - - diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aed9bfee7..1aaab555d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -49,6 +49,10 @@ if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) endif() +if (BACKCOMPAT) + list(APPEND common_sources compat/glibc_compat.cpp) +endif() + set(common_headers) set(common_private_headers diff --git a/src/common/compat/glibc_compat.cpp b/src/common/compat/glibc_compat.cpp new file mode 100644 index 000000000..bf567987d --- /dev/null +++ b/src/common/compat/glibc_compat.cpp @@ -0,0 +1,98 @@ +#include <cstddef> +#include <cstdint> +#include <strings.h> +#include <string.h> +#include <glob.h> +#include <unistd.h> +#include <fnmatch.h> + +#if defined(HAVE_SYS_SELECT_H) +#include <sys/select.h> +#endif + +// Prior to GLIBC_2.14, memcpy was aliased to memmove. +extern "C" void* memmove(void* a, const void* b, size_t c); +//extern "C" void* memset(void* a, int b, long unsigned int c); +extern "C" void* memcpy(void* a, const void* b, size_t c) +{ + return memmove(a, b, c); +} + +extern "C" void __chk_fail(void) __attribute__((__noreturn__)); + +#if defined(__i386__) || defined(__arm__) + +extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); + +extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp) +{ + int32_t c1 = 0, c2 = 0; + int64_t uu = u, vv = v; + int64_t w; + int64_t r; + + if (uu < 0) { + c1 = ~c1, c2 = ~c2, uu = -uu; + } + if (vv < 0) { + c1 = ~c1, vv = -vv; + } + + w = __udivmoddi4(uu, vv, (uint64_t*)&r); + if (c1) + w = -w; + if (c2) + r = -r; + + *rp = r; + return w; +} +#endif + +/* glibc-internal users use __explicit_bzero_chk, and explicit_bzero + redirects to that. */ +#undef explicit_bzero +/* Set LEN bytes of S to 0. The compiler will not delete a call to + this function, even if S is dead after the call. */ +void +explicit_bzero (void *s, size_t len) +{ + memset (s, '\0', len); + /* Compiler barrier. */ + asm volatile ("" ::: "memory"); +} + +// Redefine explicit_bzero_chk +void +__explicit_bzero_chk (void *dst, size_t len, size_t dstlen) +{ + /* Inline __memset_chk to avoid a PLT reference to __memset_chk. */ + if (__glibc_unlikely (dstlen < len)) + __chk_fail (); + memset (dst, '\0', len); + /* Compiler barrier. */ + asm volatile ("" ::: "memory"); +} +/* libc-internal references use the hidden + __explicit_bzero_chk_internal symbol. This is necessary if + __explicit_bzero_chk is implemented as an IFUNC because some + targets do not support hidden references to IFUNC symbols. */ +#define strong_alias (__explicit_bzero_chk, __explicit_bzero_chk_internal) + +#undef glob +extern "C" int glob_old(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob); +#ifdef __i386__ +__asm__(".symver glob_old,glob@GLIBC_2.1"); +#elif defined(__amd64__) +__asm__(".symver glob_old,glob@GLIBC_2.2.5"); +#elif defined(__arm__) +__asm(".symver glob_old,glob@GLIBC_2.4"); +#elif defined(__aarch64__) +__asm__(".symver glob_old,glob@GLIBC_2.17"); +#endif + +extern "C" int __wrap_glob(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob) +{ + return glob_old(pattern, flags, errfunc, pglob); +} + |