I've finished the process of upstreaming patches to LLVM sanitizers (almost 2000LOC of local code) and submitted to upstream new improvements for the NetBSD support. Today out of the box (in unpatched version) we have support for a variety of compiler-rt LLVM features: ASan (finds unauthorized memory access), UBSan (finds unspecified code semantics), TSan (finds threading bugs), MSan (finds uninitialized memory use), SafeStack (double stack hardening), Profile (code coverage), XRay (dynamic code tracing); while other ones such as Scudo (hardened allocator) or DFSan (generic data flow sanitizer) are not far away from completeness.

The NetBSD support is no longer visibly lacking behind Linux in sanitizers, although there are still failing tests on NetBSD that are not observed on Linux. On the other hand there are features working on NetBSD that are not functional on Linux, like sanitizing programs during early initialization process of OS (this is caused by /proc dependency on Linux that is mounted by startup programs, while NetBSD relies on sysctl(3) interfaces that is always available).

Changes in compiler-rt

A number of patches have been merged upstream this month. Part of the upstreamed code has been originally written by Yang Zheng during GSoC-2018. My work was about cleaning the patches, applying comments from upstream review and writing new regression tests. Some of the changes were newly written over the past month, like background thread support in ASan/NetBSD or NetBSD compatible per-thread cleanup destructors in ASan and MSan.

Additionally, I've also ported the LLVM profile (--coverage) feature to NetBSD and investigated the remaining failing tests. Part of the failures were caused by already a copy of older runtime inside the NetBSD libc (ABIv2 libc vs ABIv4 current). The remaining two tests are affected by incompatible behavior of atexit(3) in Dynamic Shared Objects. Replacing the functionality with destructors didn't work and I've marked these tests as expected failures and moved on.

Changes in compiler-rt:

  • 3d5a3668a Reenable hard_rss_limit_mb_test.cc for android-26
  • decb231c3 Add support for background thread on NetBSD in ASan
  • 3ebc523bb Fix a mistake in previous
  • df1f46250 Update NetBSD ioctl(2) entries with 8.99.28
  • 2de4ff725 Enable asan_and_llvm_coverage_test.cc for NetBSD
  • 4d9ac421b Reimplement Thread Static Data MSan routines with TLS
  • f4a536af4 Adjust NetBSD/sha2.cc to be portable to more environments
  • 21bd4bd9f Adjust NetBSD/md2.cc to be portable to more environments
  • 1f2d0324e Adjust NetBSD/md[45].cc to be portable to more environments
  • 2835fe7cf Add support for LLVM profile for NetBSD
  • 52af2fe7c Reimplement Thread Static Data ASan routines with TLS
  • 79d385b5c Improve the comment in previous
  • 384486fa4 Expand TSan sysroot workaround to NetBSD
  • 81e370964 Enable test/msan/pthread_getname_np.cc for NetBSD
  • 19e2af50e Enable SANITIZER_INTERCEPT_PTHREAD_GETNAME_NP for NetBSD
  • 5088473c7 Fix internal_sleep() for NetBSD
  • cd24f2f94 Mark interception_failure_test.cc as passing for NetBSD and asan-dynamic-runtime
  • ececda6ca Set shared_libasan_path in lit tests for NetBSD
  • 429bc2d51 Add a new interceptors for cdbr(3) and cdbw(3) API from NetBSD
  • 71553eb50 Add new interceptors for vis(3) API in NetBSD
  • a3e78a793 Add data types needed for md2(3)/NetBSD interceptors
  • 0ddb9d099 Add interceptors for the sha2(3) from NetBSD
  • 9e2ff43a6 Add interceptors for md2(3) from NetBSD
  • 42ac31ee6 Add new interceptors for FILE repositioning stream
  • 8627b4b30 Fix a typo in the strtoi test
  • 660f7441b Revert a chunk of previous change in sanitizer_platform_limits_netbsd.h
  • 9a087462c Add interceptors for md5(3) from NetBSD
  • 086caf6a2 Add interceptors for the rmd160(3) from NetBSD
  • 8f77a2e89 Add interceptors for the md4(3) from NetBSD
  • 27af3db52 Add interceptors for the sha1(3) from NetBSD
  • 6b9f7889b Add interceptors for the strtoi(3)/strtou(3) from NetBSD
  • 195044df9 Add a new interceptors for statvfs1(2) and fstatvfs1(2) from NetBSD
  • f0835eb01 Add a new interceptor for fparseln(3) from NetBSD
  • 11ecbe602 Add new interceptor for strtonum(3)
  • 19b47fcc0 Remove XFAIL in get_module_and_offset_for_pc.cc for NetBSD-MSan
  • b3a7f1d78 Add a new interceptor for modctl(2) from NetBSD
  • 39c2acc81 Add a new interceptor for nl_langinfo(3) from NetBSD
  • 2eb9a4c53 Update GET_LINK_MAP_BY_DLOPEN_HANDLE() for NetBSD x86
  • e8dd644be Improve the regerror(3) interceptor
  • dd939986a Add interceptors for the sysctl(3) API family from NetBSD
  • 67639f9cc Add interceptors for the fts(3) API family from NetBSD
  • c8fae517a Add new interceptor for regex(3) in NetBSD

Part of the new code has been quickly ported from NetBSD to other Operating Systems, mostly FreeBSD, and when applicable to Darwin and Linux.

Changes in other LLVM projects

In order to eliminate local diffs in other LLVM projects, I've upstreamed two patches to LLVM and two to OpenMP. I've also helped other BSDs to get their support in OpenMP (DragonFlyBSD and OpenBSD).

LLVM changes:

  • 50df229c26a Add NetBSD support in needsRuntimeRegistrationOfSectionRange.
  • 267dfed3ade Register kASan shadow offset for NetBSD/amd64

OpenMP changes:

  • 67d037d Implement __kmp_is_address_mapped() for NetBSD
  • 9761977 Implement __kmp_gettid() for NetBSD
  • a72c79b Add OpenBSD support to OpenMP
  • b3d05ab Add DragonFlyBSD support to OpenMP

NetBSD changes

I've introduced 5 changes to the NetBSD source tree over the past month, not counting updates to TODO lists.

  • Raise the fill_vmentries() E2BIG limit from 1MB to 10MB
  • Correct libproc_p.a in distribution sets
  • compiler_rt: Update prepare-import.sh according to future updates
  • Correct handling of minval > maxval in strtonum(3)
  • Stop mangling __func__ for C++11 and newer

The first change is needed to handle large address space with sysctl(3) operation to retrieve the map. This feature is required in sanitizers and part of the tests were failing because within 1MB it wasn't possible to pass all the information about the process virtual map (mostly due to a large number of small allocations).

The second change was introduced to unbreak MKPROFILE=no build, I needed this during my work of porting the modern LLVM profile feature.

The third change is a preparation for import of compiler-rt sanitizers into the NetBSD distribution.

The forth change was a bug fix for strtonum(3) implementation in libc.

The fifth change was intended to reuse native compiler support for the __func__ compiler symbol.

Integration of LLVM sanitizers with the NetBSD basesystem

We are ready to push support for LLVM sanitizers into the NetBSD basesystem as all the needed patches have been merged. I've divided the remaining tests of integration of LLVM sanitizers into three milestones:

  1. Import compiler-rt sources into src/. A complete diff is pending for final acceptance in internal review.
  2. Integrate building of compiler-rt stanitizer under the MKLLVM=yes option. This has been made functional, but it needs polishing and submitting to internal review.
  3. Make MKSANITIZER available out of the box with the toolchain available in "./build.sh tools". This will be continuation of the previous point. All the MKSANITIZER patches independent from compiler type are already committed into the NetBSD distribution, however there will be likely some extra minor adaptation work here too.

Plan for the next milestone

Finish the integration of LLVM sanitizers with the NetBSD distribution.

This work was sponsored by The NetBSD Foundation.

The NetBSD Foundation is a non-profit organization and welcomes any donations to help us continue funding projects and services to the open-source community. Please consider visiting the following URL, and chip in what you can:

http://netbsd.org/donations/#how-to-donate

Posted late Thursday afternoon, January 3rd, 2019 Tags:
Prepared by Michał Górny (mgorny AT gentoo.org).

LLD is the link editor (linker) component of Clang toolchain. Its main advantage over GNU ld is much lower memory footprint, and linking speed. It is of specific interest to me since currently 8 GiB of memory are insufficient to link LLVM statically (which is the upstream default).

The first goal of LLD porting is to ensure that LLD can produce working NetBSD executables, and be used to build LLVM itself. Then, it is desirable to look into trying to build additional NetBSD components, and eventually into replacing /usr/bin/ld entirely with lld.

In this report, I would like to shortly summarize the issues I have found so far trying to use LLD on NetBSD.

DT_RPATH vs DT_RUNPATH

RPATH is used to embed a library search path in the executable. Since it takes precedence over default system library paths, it can be used both to specify the location of additional program libraries and to override system libraries.

Currently, RPATH can be embedded in executables using two tags: the “old” DT_RPATH tag and the “new” DT_RUNPATH tag. The existence of two tags comes from behavior exhibited by some operating systems (e.g. glibc systems): DT_RPATH used to take precedence over LD_LIBRARY_PATH, making it impossible to override the paths specified there. Therefore, a new DT_RUNPATH tag was added that comes after LD_LIBRARY_PATH in precedence. When both DT_RPATH and DT_RUNPATH are specified, the former is ignored.

On NetBSD, DT_RPATH does not take precedence over LD_LIBRARY_PATH. Therefore, there wasn't ever a need for DT_RUNPATH and the support for it (as alias to DT_RPATH) was added only very recently: on 2018-12-30.

Unlike GNU ld, LLD defaults to using “new” tag by default and therefore produces executables whose RPATHs do not work on older NetBSD versions. Given that using DT_RUNPATH on NetBSD has no real advantage, using the --disable-new-dtags option to suppress them is preferable.

More than two PT_LOAD segments

PT_LOAD segments are used to map executable image into the memory. Traditionally, GNU ld produces exactly two PT_LOAD segments: a RX text (code) segment, and a RW data segment. NetBSD dynamic loader (ld.elf_so) hardcodes the assumption of that design. However, lld sometimes produces an additional read-only data segment, causing the assertions to fail in the dynamic loader.

I have attempted to rewrite the memory mapping routine to allow for arbitrary number of segments. However, apparently my patch is just “a step in the wrong direction” and Joerg Sonnenberger is working on a proper fix.

Alternatively, LLD has a --no-rosegment option that can be used to suppress the additional segment and work around the problem.

Clang/LLD driver design issues

Both GCC and Clang use a design based on a front-end driver component. That is, the executable called directly by the user is a driver whose purpose is to perform initial command-line option and input processing, and run appropriate tools performing the actual work. Those tools may include the C preprocessor (cpp), C/C++ compiler (cc1), assembler, link editor (ld).

This follows the original UNIX principle of simple tools that perform a single task well, and a wrapper that combines those tools into complete workflows. Interesting enough, it makes it possible to keep all system-specific defaults and logic in a single place, without having to make every single tool aware of them. Instead, they are passed to those tools as command-line options.

This also accounts for simpler and more portable build system design. The gcc/clang driver provides a single high-level interface for performing a multitude of tasks, including compiling assembly files or linking executables. Therefore, the build system and the user do not need to be explicitly aware of low-level tooling and its usage. Not to mention it makes much easier to swap that tooling transparently.

For example, if you are linking an executable via the driver, it takes care of finding the appropriate link editor (and makes it easy to change it via -fuse-ld), preparing appropriate command-line options (e.g. if you do a -m32 multilib build, it sets the emulation for you) and passes necessary libraries to link (e.g. an appropriate standard C++ library when building a C++ program).

The clang toolchain considers LLD an explicit part of this workflow, and — unlike GNU ld — ld.lld is not really suitable for using stand-alone. For example, it does not include any standard search paths for libraries, expecting the driver to provide them in form of appropriate -L options. This way, all the logic responsible for figuring out the operating system used (including possible cross-compilation scenarios) and using appropriate paths is located in one component.

However, Joerg Sonnenberger disagrees with this and believes LLD should contain all the defaults necessary for it to be used stand-alone on NetBSD. Effectively, we have two conflicting designs: one where all logic is in clang driver, and the other where some of the logic is moved into LLD. At this moment, LLD is following the former assumption, and clang driver for NetBSD — the latter. As a result, neither using LLD directly nor via clang works out of the box on NetBSD; to use either, the user would have to pass all appropriate -L and -z options explicitly.

Fixing LLD to work with the current clang driver would require adding target awareness to LLD and changing a number of defaults for NetBSD based on the target used. However, LLD maintainer Rui Ueyama is opposed to introducing this extra logic specifically for NetBSD, and believes it should be added to the clang driver as for other platforms. On the other side, the NetBSD toolchain driver maintainer Joerg Sonnenberger blocks adding this to the driver. Therefore, we have reached a impasse that prevents LLD from working out of the box on NetBSD without local patches.

A work-in-progress implementation of the local target logic approach requested by Joerg can be seen in D56650. Afterwards, additional behavior can be enabled on NetBSD by using target triple properties such as in D56215 (which copies the libdir logic from clang).

For comparison, the same problem solved in a way consistent with other distributions (and rejected by Joerg) can be seen in D56932 (which updates D33726). However, in some cases it will require adding additional options to LLD (e.g. -z nognustack, D56554), and corresponding dummy switches in GNU ld.

Handling of indirect shared library dependencies

When starting a program, the dynamic loader needs to find and load all shared libraries listed via DT_NEEDED entries in order to obtain symbols needed by the program (functions, variables). Naturally, it also needs to process DT_NEEDED entries of those libraries to satisfy their symbol dependencies, and so on. As a result to this, the program can also reference symbols declared in dependencies of its DT_NEEDED libraries, that is its indirect dependencies.

While linking executables, link editors normally verify that all symbols can be resolved in one of the linked libraries. Historically, GNU ld followed the logic used by the dynamic loader and permitted symbols used by program to be present in either direct or indirect dependencies. However, GNU gold, LLD and newer versions of GNU ld use different logic and permit only symbols provided by the direct dependencies.

Let's take an example: you are writing a program that works with .zip files, and therefore you link -lzip. However, you also implement support for .gz files, and therefore call gzopen() provided by -lz which is also a dependency of libzip.so. Now, with old GNU ld versions you could just use -lzip since it would indirectly include libz.so. However, modern linkers will refuse to link it claiming that gzopen is undefined. You need to explicitly link -lzip -lz to resolve that.

Joerg Sonnenberger disagrees with this new behavior, and explicitly preserves the old behavior in GNU ld version used in NetBSD. However, LLD does not support the historical GNU ld behavior at all. It will probably become necessary to implement it from scratch to support NetBSD fully.

Summary

At this point, it seems that using LLD for the majority of regular packages is a goal that can be achieved soon. The main blocker right now is the disagreement between developers on how to proceed. When we can resolve that and agree on a single way forward, most of the patches become trivial.

Sadly, at this point I really do not see any way to convince either of the sides. The problem was reported in May 2017, and in the same month a fix consistent with all other platforms was provided. However, it is being blocked and LLD can not work out of the box.

Hopefully, we will able to finally find a way forward that does not involve keeping upstream clang driver for NetBSD incompatible with upstream LLD, and carrying a number of patches to LLD locally to make it work.

The further goals include attempting to build the kernel and complete userland using LLD, as well as further verifying compatibility of executables produced with various combinations of linker options (e.g. static PIE executables).

Posted late Friday evening, January 18th, 2019 Tags:
Add a comment