commit 138470ca7ce810f62761af8c3989654ec4a611fa Author: Georg Koppen gk@torproject.org Date: Thu May 12 20:25:02 2016 +0000
Bug 17406: Include SelfRando support in our nightlies
SelfRando is a new defense against code reuse attacks developed by the Radactor and Readactor++ people. We should give it a wider testing audience by including it in the nightly. --- RelativeLink/start-tor-browser | 3 + gitian/descriptors/linux/gitian-firefox.yml | 7 + gitian/descriptors/linux/gitian-utils.yml | 38 +++- gitian/fetch-inputs.sh | 4 +- gitian/gpg/ELFUTILS.gpg | Bin 0 -> 10483 bytes gitian/mkbundle-linux.sh | 9 +- gitian/patches/libfaketime-asan.patch | 268 ++++++++++++++++++++++++++-- gitian/verify-tags.sh | 3 +- gitian/versions.nightly | 4 + 9 files changed, 313 insertions(+), 23 deletions(-)
diff --git a/RelativeLink/start-tor-browser b/RelativeLink/start-tor-browser index 2e7805d..19c5cfc 100755 --- a/RelativeLink/start-tor-browser +++ b/RelativeLink/start-tor-browser @@ -270,6 +270,9 @@ fi
LD_LIBRARY_PATH="${HOME}/TorBrowser/Tor/" export LD_LIBRARY_PATH +export SELFRANDO_write_layout_file= +export NSS_DISABLE_HW_AES=1 + # We need to disable LSan which is enabled by default now. Otherwise we'll get # a crash during shutdown: https://bugs.torproject.org/10599#comment:59 ASAN_OPTIONS="detect_leaks=0" diff --git a/gitian/descriptors/linux/gitian-firefox.yml b/gitian/descriptors/linux/gitian-firefox.yml index e24a740..22686b7 100644 --- a/gitian/descriptors/linux/gitian-firefox.yml +++ b/gitian/descriptors/linux/gitian-firefox.yml @@ -29,12 +29,15 @@ remotes: "dir": "tor-browser" - "url": "https://github.com/wolfcw/libfaketime" "dir": "faketime" +- "url": "https://github.com/immunant/selfrando.git" + "dir": "selfrando" files: - "binutils-linux64-utils.zip" - "gcc-linux64-utils.zip" - "re-dzip.sh" - "dzip.sh" - "versions" +- "self-rando-utils.zip" script: | source versions INSTDIR="$HOME/install" @@ -49,6 +52,7 @@ script: | export DEB_BUILD_HARDENING_FORMAT=1 export DEB_BUILD_HARDENING_PIE=1 # + unzip -d $INSTDIR self-rando-utils.zip # Preparing Binutils and GCC for Tor Browser unzip -d $INSTDIR binutils-linux$GBUILD_BITS-utils.zip # Make sure gold is used with the hardening wrapper for full RELRO, see @@ -96,6 +100,9 @@ script: | # that feature it would hang sometimes for unknown but to libfaketime related # reasons. export LD_PRELOAD="" + # Self-Rando wrapper + export PATH="$HOME/build/selfrando/Tools/TorBrowser/tc-wrapper/:$PATH" + export SELFRANDO_skip_shuffle= make -f client.mk configure CONFIGURE_ARGS="--with-tor-browser-version=${TORBROWSER_VERSION} --enable-update-channel=${TORBROWSER_UPDATE_CHANNEL} --enable-bundled-fonts" # We need libfaketime for all the timestamps e.g. written into the libraries. # BUT we need to exclude |make build| from it. Otherwise the build fails close diff --git a/gitian/descriptors/linux/gitian-utils.yml b/gitian/descriptors/linux/gitian-utils.yml index 72ead1b..15b26f4 100644 --- a/gitian/descriptors/linux/gitian-utils.yml +++ b/gitian/descriptors/linux/gitian-utils.yml @@ -24,10 +24,14 @@ packages: - "libssl-dev" # Needed for binutils (64bit) as we are building with PIE enabled. - "libstdc++6-4.7-pic" +# Needed for Selfrando +- "scons" reference_datetime: "2000-01-01 00:00:00" remotes: - "url": "https://github.com/libevent/libevent.git" "dir": "libevent" +- "url": "https://github.com/immunant/selfrando.git" + "dir": "selfrando" files: - "binutils.tar.bz2" - "gcc.tar.bz2" @@ -36,6 +40,7 @@ files: - "versions" - "dzip.sh" - "libfaketime-asan.patch" +- "elfutils.tar.bz2" script: | INSTDIR="$HOME/install" source versions @@ -56,11 +61,8 @@ script: | # The libstdc++ shipped by default is non-PIC which breaks the binutils build # if we build with DEB_BUILD_HARDENING_PIE=1. We need to install a PIC one AND # make sure it gets used before the non-PIC one would. - if [ $GBUILD_BITS == "64" ]; - then - ln -s /usr/lib/gcc/x86_64-linux-gnu/4.7/libstdc++_pic.a libstdc++.a - export LDFLAGS="-L/home/debian -lstdc++" - fi + ln -s /usr/lib/gcc/x86_64-linux-gnu/4.7/libstdc++_pic.a libstdc++.a + export LDFLAGS="-L/home/debian -lstdc++" cd binutils* # We want to use gold as the linker in our toolchain mainly as it is way # faster when linking Tor Browser code (especially libxul). But apart from @@ -90,6 +92,31 @@ script: | cd ..
export DEB_BUILD_HARDENING_FORMAT=1 + OPATH="$PATH" + OLD_LIBRARY_PATH="$LD_LIBRARY_PATH" + export PATH="$INSTDIR/binutils/bin:$INSTDIR/gcc/bin:$PATH" + export LD_LIBRARY_PATH="$INSTDIR/gcc/lib64/:$LD_LIBRARY_PATH" + + # Building Elfutils + tar xjf elfutils.tar.bz2 + cd elfutils*/ + ./configure --prefix=$INSTDIR/elfutils + make $MAKEOPTS + make install + cd .. + + # Building Selfrando + cd selfrando + scons -Q arch=x86_64 ADDN_CCFLAGS="-include $(ls $HOME/build/elfutils-*/libelf/elf.h) -I $INSTDIR/elfutils/include -fno-reorder-functions" ADDN_LINKFLAGS="-L $INSTDIR/elfutils/lib" + mkdir -p $INSTDIR/self-rando + cp sconsRelease/x86_64/bin/* $INSTDIR/self-rando/ + cd .. + + PATH="$OPATH" + unset OPATH + LD_LIBRARY_PATH="$OLD_LIBRARY_PATH" + unset OLD_LIBRARY_PATH + # Building Libevent cd libevent ./autogen.sh @@ -134,4 +161,5 @@ script: | ~/build/dzip.sh openssl-$OPENSSL_VER-linux$GBUILD_BITS-utils.zip openssl ~/build/dzip.sh libevent-${LIBEVENT_TAG#release-}-linux$GBUILD_BITS-utils.zip libevent ~/build/dzip.sh gmp-$GMP_VER-linux$GBUILD_BITS-utils.zip gmp + ~/build/dzip.sh self-rando-utils.zip self-rando cp *utils.zip $OUTDIR/ diff --git a/gitian/fetch-inputs.sh b/gitian/fetch-inputs.sh index 027a47e..4f63374 100755 --- a/gitian/fetch-inputs.sh +++ b/gitian/fetch-inputs.sh @@ -112,7 +112,7 @@ update_git() {
############################################################################## # Get+verify sigs that exist -for i in OPENSSL BINUTILS GCC PYCRYPTO PYTHON_MSI GMP +for i in OPENSSL BINUTILS GCC PYCRYPTO PYTHON_MSI GMP ELFUTILS do PACKAGE="${i}_PACKAGE" URL="${i}_URL" @@ -236,6 +236,7 @@ ln -sf "$SETUPTOOLS_PACKAGE" setuptools.tar.gz ln -sf "$GMP_PACKAGE" gmp.tar.bz2 ln -sf "$PARSLEY_PACKAGE" parsley.tar.gz ln -sf "$GO_PACKAGE" go.tar.gz +ln -sf "$ELFUTILS_PACKAGE" elfutils.tar.bz2
# Fetch latest gitian-builder itself # XXX - this is broken if a non-standard inputs dir is selected using the command line flag. @@ -273,6 +274,7 @@ goxcrypto https://go.googlesource.com/crypto $GO_X_CRYPTO_TAG goxnet https://go.googlesource.com/net $GO_X_NET_TAG obfs4 https://git.torproject.org/pluggable-transports/obfs4.git $OBFS4_TAG noto-fonts https://github.com/googlei18n/noto-fonts $NOTOFONTS_TAG +selfrando https://github.com/immunant/selfrando.git $SELFRANDO_TAG EOF
# HTTPS-Everywhere is special, too. We need to initialize the git submodules and diff --git a/gitian/gpg/ELFUTILS.gpg b/gitian/gpg/ELFUTILS.gpg new file mode 100644 index 0000000..f1cd4b3 Binary files /dev/null and b/gitian/gpg/ELFUTILS.gpg differ diff --git a/gitian/mkbundle-linux.sh b/gitian/mkbundle-linux.sh index a1674dd..48aac24 100755 --- a/gitian/mkbundle-linux.sh +++ b/gitian/mkbundle-linux.sh @@ -35,7 +35,7 @@ fi
if [ -z "$VM_MEMORY" ]; then - export VM_MEMORY=4000 + export VM_MEMORY=6000 fi
./make-vms.sh @@ -104,13 +104,14 @@ if [ ! -f inputs/binutils-$BINUTILS_VER-linux64-utils.zip -o \ ! -f inputs/gcc-$GCC_VER-linux64-utils.zip -o \ ! -f inputs/openssl-$OPENSSL_VER-linux64-utils.zip -o \ ! -f inputs/libevent-${LIBEVENT_TAG_ORIG#release-}-linux64-utils.zip -o \ - ! -f inputs/gmp-$GMP_VER-linux64-utils.zip ]; + ! -f inputs/gmp-$GMP_VER-linux64-utils.zip -o \ + ! -f inputs/self-rando-utils.zip ]; then echo echo "****** Starting Utilities Component of Linux Bundle (1/5 for Linux) ******" echo
- ./bin/gbuild -j $NUM_PROCS -m $VM_MEMORY --commit libevent=$LIBEVENT_TAG $DESCRIPTOR_DIR/linux/gitian-utils.yml + ./bin/gbuild -j $NUM_PROCS -m $VM_MEMORY --commit libevent=$LIBEVENT_TAG,selfrando=$SELFRANDO_TAG $DESCRIPTOR_DIR/linux/gitian-utils.yml if [ $? -ne 0 ]; then #mv var/build.log ./utils-fail-linux.log.`date +%Y%m%d%H%M%S` @@ -169,7 +170,7 @@ then echo "****** Starting TorBrowser Component of Linux Bundle (3/5 for Linux) ******" echo
- ./bin/gbuild -j $NUM_PROCS -m $VM_MEMORY --commit tor-browser=$TORBROWSER_TAG,faketime=$FAKETIME_TAG $DESCRIPTOR_DIR/linux/gitian-firefox.yml + ./bin/gbuild -j $NUM_PROCS -m $VM_MEMORY --commit tor-browser=$TORBROWSER_TAG,faketime=$FAKETIME_TAG,selfrando=$SELFRANDO_TAG $DESCRIPTOR_DIR/linux/gitian-firefox.yml if [ $? -ne 0 ]; then #mv var/build.log ./firefox-fail-linux.log.`date +%Y%m%d%H%M%S` diff --git a/gitian/patches/libfaketime-asan.patch b/gitian/patches/libfaketime-asan.patch index 5346eae..ff3e62b 100644 --- a/gitian/patches/libfaketime-asan.patch +++ b/gitian/patches/libfaketime-asan.patch @@ -1,12 +1,3 @@ -From 0ef83e9a0631e25e62e1dbc483108742fc125304 Mon Sep 17 00:00:00 2001 -From: Georg Koppen gk@torproject.org -Date: Mon, 14 Sep 2015 14:27:36 +0000 -Subject: [PATCH] No dying anymore - ---- - libsanitizer/asan/asan_linux.cc | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc index c504168..1d1a6dd 100644 --- a/libsanitizer/asan/asan_linux.cc @@ -23,6 +14,259 @@ index c504168..1d1a6dd 100644 } }
--- -1.7.10.4 - +diff --git a/libsanitizer/sanitizer_common/SelfRandoAddressTranslator.h b/libsanitizer/sanitizer_common/SelfRandoAddressTranslator.h +new file mode 100644 +index 0000000..5f13870 +--- /dev/null ++++ b/libsanitizer/sanitizer_common/SelfRandoAddressTranslator.h +@@ -0,0 +1,155 @@ ++// ++// Created by tf on 10/12/2015. ++// ++ ++#pragma once ++#include "sanitizer_internal_defs.h" ++#include "sanitizer_libc.h" ++#include "sanitizer_common.h" ++ ++#define PROT_READ 0x1 /* Page can be read. */ ++#define PROT_WRITE 0x2 /* Page can be written. */ ++#define PROT_EXEC 0x4 /* Page can be executed. */ ++#define PROT_NONE 0x0 /* Page can not be accessed. */ ++#define MAP_SHARED 0x01 /* Share changes. */ ++ ++namespace __sanitizer { ++ class SelfRandoAddressTranslator { ++ struct __attribute__((__packed__)) FunctionRecord { ++ void *undiv_addr; ++ void *div_addr; ++ u32 size; ++ }; ++ ++ struct __attribute__((__packed__)) LibraryRecord { ++ u32 version; ++ u32 seed; ++ void *file_base; ++ void *mem_base; ++ void *size; ++ ++ private: ++ char _name; ++ ++ public: ++ inline char *name() { return &_name; } ++ ++ inline FunctionRecord *function_records() { ++ return (FunctionRecord *) (&_name + internal_strlen(&_name) + 1); ++ } ++ ++ u8 *record_end() { ++ FunctionRecord *fr = function_records(); ++ while (fr->undiv_addr) { ++ fr++; ++ } ++ return (u8 *) fr + sizeof(void *); ++ } ++ }; ++ ++ // 16K loaded modules should be enough for everyone. ++ LibraryRecord* library_records_idx[1<<14]; ++ int library_records_idx_size; ++ u8 *library_records_end; ++ void add_library_record(LibraryRecord* lr); ++ ++ void read_layout_file(); ++ u8 *record_end(int i); ++ ++ public: ++ SelfRandoAddressTranslator(); ++ ++ uptr convertAddress(uptr original_address); ++ }; ++ ++ ++ ++ ++ SelfRandoAddressTranslator::SelfRandoAddressTranslator() { ++ Printf("SRAT init\n"); ++ read_layout_file(); ++ } ++ ++ void SelfRandoAddressTranslator::add_library_record(LibraryRecord* lr) { ++ library_records_idx_size++; ++ CHECK(library_records_idx_size <= 1<<14); ++ library_records_idx[library_records_idx_size - 1] = lr; ++ } ++ ++ u8* SelfRandoAddressTranslator::record_end(int i) { ++ if (i < library_records_idx_size - 1) { ++ return (u8 *) library_records_idx[i + 1] - sizeof(void *); ++ } else { ++ return library_records_end - sizeof(void*); ++ } ++ } ++ ++ void SelfRandoAddressTranslator::read_layout_file() { ++ uptr pid = internal_getpid(); ++ char filename[32]; ++ internal_snprintf(filename, 32, "/tmp/%d.mlf", pid); ++ ++ u8 buf[200]; // Current size of struct stat: 144 ++ if(0 != internal_stat(filename, &buf)) { ++ Printf("SRAT warning: cannot find Memory Layout File. Stack trace will be probably wrong.\n"); ++ return; ++ } ++ ++ uptr fd = OpenFile(filename, /*write*/ false); ++ uptr size = *(uptr*) (buf+48); ++ CHECK_NE(fd, -1); ++ u8 *f = (u8 *) internal_mmap(0, size, PROT_READ, MAP_SHARED, (int) fd, 0); ++ CHECK_NE(f, 0); ++ library_records_end = f + size; ++ ++ u8 *p = f; ++ while (p < library_records_end) { ++ LibraryRecord *lr = (LibraryRecord *) p; ++ add_library_record(lr); ++ CHECK_EQ(lr->version, 0x00000101); ++ p = lr->record_end(); ++ } ++ ++ } ++ ++ uptr SelfRandoAddressTranslator::convertAddress(uptr p) { ++// Printf("conv %p", p); ++ for (int i = 0; i < library_records_idx_size; ++i) { ++ LibraryRecord *lr = library_records_idx[i]; ++ ++ if (p > (uptr) lr->mem_base && p < (uptr) lr->mem_base + (uptr) lr->size) { ++ // found the correct library ++ ++ FunctionRecord *function_records = lr->function_records(); ++ ++ int a = 0; ++ int b = (int) ((FunctionRecord *) record_end(i) - function_records); ++// Printf(" (lib=>%s< a=%d b=%d)", lr->name(), a, b); ++ CHECK_EQ((u8 *) (function_records + b), record_end(i)); ++ ++ while (b - a > 1) { // bisection ++ int c = a + (b - a) / 2; ++ FunctionRecord *fr = &function_records[c]; ++ ++ if ((uptr) fr->div_addr > p) { // too high ++ b = c; ++ } else if ((uptr) fr->div_addr + (uptr) fr->size < p) { // too low ++ a = c; ++ } else { // found ++ b = c + 1; ++ a = c; ++ } ++ } ++ FunctionRecord *fr = &function_records[a]; ++ CHECK((uptr) fr->div_addr < p && p < (uptr) fr->div_addr + (uptr) fr->size); ++ ++ p = (p - (uptr) fr->div_addr + (uptr) fr->undiv_addr); ++ ++ break; ++ } ++ } ++ ++// Printf(" => %p\n", p); ++ return p; ++ } ++} +\ No newline at end of file +diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +index 5cc21d3..393a494 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc ++++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +@@ -21,6 +21,7 @@ + #include "sanitizer_procmaps.h" + #include "sanitizer_symbolizer.h" + #include "sanitizer_symbolizer_libbacktrace.h" ++#include "SelfRandoAddressTranslator.h" + + #include <errno.h> + #include <stdlib.h> +@@ -512,7 +513,7 @@ class POSIXSymbolizer : public Symbolizer { + internal_symbolizer_(internal_symbolizer), + libbacktrace_symbolizer_(libbacktrace_symbolizer) {} + +- uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) { ++ virtual uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) { + BlockingMutexLock l(&mu_); + if (max_frames == 0) + return 0; +@@ -580,7 +581,7 @@ class POSIXSymbolizer : public Symbolizer { + return frame_id; + } + +- bool SymbolizeData(uptr addr, DataInfo *info) { ++ virtual bool SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) +@@ -606,7 +607,7 @@ class POSIXSymbolizer : public Symbolizer { + return true; + } + +- bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, ++ virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address) { + BlockingMutexLock l(&mu_); + return FindModuleNameAndOffsetForAddress(pc, module_name, module_address); +@@ -720,9 +721,40 @@ class POSIXSymbolizer : public Symbolizer { + LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked. + }; + ++ ++ class SelfRandoPOSIXSymbolizer : public POSIXSymbolizer { ++ SelfRandoAddressTranslator translator; ++ ++ public: ++ SelfRandoPOSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer, ++ InternalSymbolizer *internal_symbolizer, ++ LibbacktraceSymbolizer *libbacktrace_symbolizer) ++ : POSIXSymbolizer(external_symbolizer, internal_symbolizer, libbacktrace_symbolizer), ++ translator() { } ++ ++ ++ private: ++ virtual uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) override { ++ uptr conv_addr = translator.convertAddress(addr); ++ uptr res = POSIXSymbolizer::SymbolizePC(conv_addr, frames, max_frames); ++ if (res == 1) frames->address = addr; ++ return res; ++ } ++ ++ virtual bool SymbolizeData(uptr addr, DataInfo *info) override { ++ addr = translator.convertAddress(addr); ++ return POSIXSymbolizer::SymbolizeData(addr, info); ++ } ++ ++ virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, uptr *module_address) override { ++ pc = translator.convertAddress(pc); ++ return POSIXSymbolizer::GetModuleNameAndOffsetForPC(pc, module_name, module_address); ++ }; ++ }; ++ + Symbolizer *Symbolizer::PlatformInit() { + if (!common_flags()->symbolize) { +- return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0); ++ return new(symbolizer_allocator_) SelfRandoPOSIXSymbolizer(0, 0, 0); + } + InternalSymbolizer* internal_symbolizer = + InternalSymbolizer::get(&symbolizer_allocator_); +@@ -754,10 +786,11 @@ Symbolizer *Symbolizer::PlatformInit() { + } + } + +- return new(symbolizer_allocator_) POSIXSymbolizer( ++ return new(symbolizer_allocator_) SelfRandoPOSIXSymbolizer( + external_symbolizer, internal_symbolizer, libbacktrace_symbolizer); + } + ++ + } // namespace __sanitizer + + #endif // SANITIZER_POSIX diff --git a/gitian/verify-tags.sh b/gitian/verify-tags.sh index 46cb01a..5268f85 100755 --- a/gitian/verify-tags.sh +++ b/gitian/verify-tags.sh @@ -118,10 +118,11 @@ siphash $GOSIPHASH_TAG goxcrypto $GO_X_CRYPTO_TAG goxnet $GO_X_NET_TAG noto-fonts $NOTOFONTS_TAG +selfrando $SELFRANDO_TAG EOF
# Verify signatures on signed packages -for i in OPENSSL BINUTILS GCC PYCRYPTO PYTHON_MSI GMP +for i in OPENSSL BINUTILS GCC PYCRYPTO PYTHON_MSI GMP ELFUTILS do PACKAGE="${i}_PACKAGE" URL="${i}_URL" diff --git a/gitian/versions.nightly b/gitian/versions.nightly index 088625b..9ff7e8f 100755 --- a/gitian/versions.nightly +++ b/gitian/versions.nightly @@ -48,6 +48,7 @@ GO_X_CRYPTO_TAG=master GO_X_NET_TAG=master OBFS4_TAG=master NOTOFONTS_TAG=720e34851382ee3c1ef024d8dffb68ffbfb234c2 +SELFRANDO_TAG=8e49e7aa664625d8e4793579864231b151589d0f
GITIAN_TAG=tor-browser-builder-4
@@ -69,6 +70,7 @@ SETUPTOOLS_VER=1.4 PARSLEY_VER=1.2 GO_VER=1.4.2 NSIS_VER=2.51 +ELFUTILS_VER=0.160
## File names for the source packages OPENSSL_PACKAGE=openssl-${OPENSSL_VER}.tar.gz @@ -99,6 +101,7 @@ NOTOJPFONT_PACKAGE=NotoSansJP-Regular.otf NOTOKRFONT_PACKAGE=NotoSansKR-Regular.otf NOTOSCFONT_PACKAGE=NotoSansSC-Regular.otf NOTOTCFONT_PACKAGE=NotoSansTC-Regular.otf +ELFUTILS_PACKAGE=elfutils-${ELFUTILS_VER}.tar.bz2
# Hashes for packages with weak sigs or no sigs OPENSSL_HASH=e7e81d82f3cd538ab0cdba494006d44aab9dd96b7f6233ce9971fb7c7916d511 @@ -157,3 +160,4 @@ NOTOJPFONT_URL=${NOTOCJKBASE_URL}/${NOTOJPFONT_PACKAGE} NOTOKRFONT_URL=${NOTOCJKBASE_URL}/${NOTOKRFONT_PACKAGE} NOTOSCFONT_URL=${NOTOCJKBASE_URL}/${NOTOSCFONT_PACKAGE} NOTOTCFONT_URL=${NOTOCJKBASE_URL}/${NOTOTCFONT_PACKAGE} +ELFUTILS_URL=https://fedorahosted.org/releases/e/l/elfutils/$%7BELFUTILS_VER%7D/$%7BELFUT...