path: root/ldso
AgeCommit message (Collapse)AuthorLines
2018-10-16add new stage 2b to dynamic linker bootstrap for thread pointerRich Felker-10/+23
commit a603a75a72bb469c6be4963ed1b55fabe675fe15 removed attribute const from __errno_location and pthread_self, and the same reasoning forced arch definitions of __pthread_self to use volatile asm, significantly impacting code generation and imposing manual caching of pointers where the impact might be noticable. reorder the thread pointer setup and place it across a strong barrier (symbolic function lookup) so that there is no assumed ordering between the initialization and the accesses to the thread pointer in stage 3.
2018-10-15fix misleading placement of statement on same line as for loop in ldsoRich Felker-1/+2
the placement triggered -Wmisleading-indentation warnings if enabled, and was gratuitously confusing to anyone reading the code.
2018-10-12fix dlsym of thread-local symbols on archs with DTP_OFFSET!=0Rich Felker-3/+3
commit 6ba5517a460c6c438f64d69464fdfc3269a4c91a modified __tls_get_addr to offset the address by +DTP_OFFSET (0x8000 on powerpc, mips, etc.) and adjusted the result of DTPREL relocations by -DTP_OFFSET to compensate, but missed changing the argument setup for calls to __tls_get_addr from dlsym.
2018-10-12combine arch ABI's DTP_OFFSET into DTV pointersRich Felker-14/+11
as explained in commit 6ba5517a460c6c438f64d69464fdfc3269a4c91a, some archs use an offset (typicaly -0x8000) with their DTPOFF relocations, which __tls_get_addr needs to invert. on affected archs, which lack direct support for large immediates, this can cost multiple extra instructions in the hot path. instead, incorporate the DTP_OFFSET into the DTV entries. this means they are no longer valid pointers, so store them as an array of uintptr_t rather than void *; this also makes it easier to access slot 0 as a valid slot count. commit e75b16cf93ebbc1ce758d3ea6b2923e8b2457c68 left behind cruft in two places, __reset_tls and __tls_get_new, from back when it was possible to have uninitialized gap slots indicated by a null pointer in the DTV. since the concept of null pointer is no longer meaningful with an offset applied, remove this cruft. presently there are no archs with both TLSDESC and nonzero DTP_OFFSET, but the dynamic TLSDESC relocation code is also updated to apply an inverted offset to its offset field, so that the offset DTV would not impose a runtime cost in TLSDESC resolver functions.
2018-10-01add TLSDESC support for 32-bit armRich Felker-0/+7
unlike other asm where the baseline ISA is used, these functions are hot paths and use ISA-level specializations. call-clobbered vfp registers are saved before calling __tls_get_new, since there is no guarantee it won't use them. while setjmp/longjmp have to use hwcap to decide whether to the fpu is in use, since application code could be using vfp registers even if libc was compiled as pure softfloat, __tls_get_new is part of libc and can be assumed not to have access to vfp registers if tlsdesc.S does not. thus it suffices just to check the predefined preprocessor macros. the check for __ARM_PCS_VFP is redundant; !__SOFTFP__ must always be true if the target ISA level includes fpu instructions/registers.
2018-09-18support setting of default thread stack size via PT_GNU_STACK headerRich Felker-0/+12
this facilitates building software that assumes a large default stack size without any patching to call pthread_setattr_default_np or pthread_attr_setstacksize at each thread creation site, using just LDFLAGS. normally the PT_GNU_STACK header is used only to reflect whether executable stack is desired, but with GNU ld at least, passing -Wl,-z,stack-size=N will set a size on the program header. with this patch, that size will be incorporated into the default stack size (subject to increase-only rule and DEFAULT_STACK_MAX limit). both static and dynamic linking honor the program header. for dynamic linking, all libraries loaded at program start, including preloaded ones, are considered. dlopened libraries are not considered, for several reasons. extra logic would be needed to defer processing until the load of the new library is commited, synchronization woud be needed since other threads may be running concurrently, and the effectiveness woud be limited since the larger size would not apply to threads that already existed at the time of dlopen. programs that will dlopen code expecting a large stack need to declare the requirement themselves, or pthread_setattr_default_np can be used.
2018-09-12overhaul internally-public declarations using wrapper headersRich Felker-2/+0
commits leading up to this one have moved the vast majority of libc-internal interface declarations to appropriate internal headers, allowing them to be type-checked and setting the stage to limit their visibility. the ones that have not yet been moved are mostly namespace-protected aliases for standard/public interfaces, which exist to facilitate implementing plain C functions in terms of POSIX functionality, or C or POSIX functionality in terms of extensions that are not standardized. some don't quite fit this description, but are "internally public" interfacs between subsystems of libc. rather than create a number of newly-named headers to declare these functions, and having to add explicit include directives for them to every source file where they're needed, I have introduced a method of wrapping the corresponding public headers. parallel to the public headers in $(srcdir)/include, we now have wrappers in $(srcdir)/src/include that come earlier in the include path order. they include the public header they're wrapping, then add declarations for namespace-protected versions of the same interfaces and any "internally public" interfaces for the subsystem they correspond to. along these lines, the wrapper for features.h is now responsible for the definition of the hidden, weak, and weak_alias macros. this means source files will no longer need to include any special headers to access these features. over time, it is my expectation that the scope of what is "internally public" will expand, reducing the number of source files which need to include *_impl.h and related headers down to those which are actually implementing the corresponding subsystems, not just using them.
2018-09-12move tlsdesc and internal dl function declarations to dynlink.hRich Felker-4/+0
2018-09-12move declarations of tls setup/access functions to pthread_impl.hRich Felker-4/+0
it's already included in all places where these are needed, and aside from __tls_get_addr, they're all implementation internals.
2018-09-12move declarations for malloc internals to malloc_impl.hRich Felker-1/+1
2018-09-12improve machinery for ldso to report libc versionRich Felker-3/+1
eliminate gratuitous glue function for reporting the version, which was probably leftover from the old dynamic linker design which lacked a clear barrier for when/how it could access global data. put the declaration for the data object that replaces it in libc.h where it can be type checked.
2018-09-05define and use internal macros for hidden visibility, weak refsRich Felker-23/+13
this cleans up what had become widespread direct inline use of "GNU C" style attributes directly in the source, and lowers the barrier to increased use of hidden visibility, which will be useful to recovering some of the efficiency lost when the protected visibility hack was dropped in commit dc2f368e565c37728b0d620380b849c3a1ddd78f, especially on archs where the PLT ABI is costly.
2018-07-16block dlopen of libraries with initial-exec refs to dynamic TLSRich Felker-0/+8
previously, this operation succeeded, and the relocation results worked for access from new threads created after dlopen, but produced invalid accesses (and possibly clobbered other memory) from threads that already existed. the way the check is written, it still permits dlopen of libraries containing initial-exec references to static TLS (TLS in the main program or in a dynamic library loaded at startup).
2018-07-16fix inefficient choice of tlsdesc function due to off-by-oneRich Felker-1/+1
tls_id is one-based, whereas [static_]tls_cnt is a count, so comparison for checking that a given tls_id is dynamic rather than static needs to use strict inequality.
2018-06-28avoid spurious dso matches by dladdr outside bounds of load segmentsRich Felker-1/+10
since slack space at the beginning and/or end of writable load maps is donated to malloc, the application could obtain valid pointers in these ranges which dladdr would erroneously identify as part of the shared object whose mapping they came from. instead of checking the queried address against the mapping base and length, check it against the load segments from the program headers, and only match the dso if it lies within the bounds of one of them. as a shortcut, if the address does match the range of the mapping but not any of the load segments, we know it cannot match any other dso and can immediately return failure.
2018-06-28make dladdr consistently produce the first symbol in presence of aliasesRich Felker-1/+1
the early-exit condition for the symbol match loop on exact matches caused dladdr to produce the first match for an exact match, but the last match for an inexact match. in the interest of consistency, require a strictly-closer match to replace an already-found one.
2018-06-28fix symtab-order-dependent spurious matches in dladdrRich Felker-5/+8
commit 8b8fb7f03721c42445f982582f462144ab60a1a0 added logic to prevent matching a symbol with no recorded size (closest-match) when there is an intervening symbol whose size was recorded, but it only worked when the intervening symbol was encountered later in the search. instead of rejecting symbols where addr falls outside their recorded size during the closest-match search, accept them to find the true closest-match, then reject such a result only once the search has finished.
2018-06-27correctly handle non-matching symbols in dladdrRich Felker-4/+13
based on patch by Axel Siebenborn, with fixes discussed on the mailing list after submission and and rebased around the UB fix in commit e829695fcc880f8578c2b964ea2d090f0016c9d7. avoid spurious symbol matches by dladdr beyond symbol size. for symbols with a size recorded, only match if the queried address lies within the address range determined by the symbol address and size. for symbols with no size recorded, the old closest-match behavior is kept, as long as there is no intervening symbol with a recorded size. the case where no symbol is matched, but the address does lie within the memory range of a shared object, is specified as success. fix the return value and produce a valid (with null dli_sname and dli_saddr) Dl_info structure.
2018-06-27avoid using undefined pointer arithmetic in dladdrRich Felker-8/+9
2018-06-26fix dynamic linker mapping/clearing bss in first/only LOAD segmentRich Felker-6/+5
writable load segments can have size-in-memory larger than their size in the ELF file, representing bss or equivalent. the initial partial page has to be zero-filled, and additional anonymous pages have to be mapped such that accesses don't failt with SIGBUS. map_library skips redundant MAP_FIXED mapping of the initial (lowest-address) segment when processing LOAD segments since it was already mapped when reserving the virtual address range, but in doing so, inadvertently also skipped the code to fill/map bss. typical executable and library files have two or more LOAD segments, and the first one is text/rodata (non-writable) and thus has no bss, but it is syntactically valid for an ELF program/library to put its writable segment first, or to have only one segment (everything writable). the binutils bfd-based linker has been observed to create such programs in the presence of unusual sections or linker scripts. fix by moving only the mmap_fixed operation under the conditional rather than skipping the remainder of the loop body. add a check to avoid bss processing in the case where the segment is not writable; this should not happen, but if it does, the change would be a crashing regression without this check.
2018-06-02fix TLS layout of TLS variant I when there is a gap above TPSzabolcs Nagy-2/+3
In TLS variant I the TLS is above TP (or above a fixed offset from TP) but on some targets there is a reserved gap above TP before TLS starts. This matters for the local-exec tls access model when the offsets of TLS variables from the TP are hard coded by the linker into the executable, so the libc must compute these offsets the same way as the linker. The tls offset of the main module has to be alignup(GAP_ABOVE_TP, main_tls_align). If there is no TLS in the main module then the gap can be ignored since musl does not use it and the tls access models of shared libraries are not affected. The previous setup only worked if (tls_align & -GAP_ABOVE_TP) == 0 (i.e. TLS did not require large alignment) because the gap was treated as a fixed offset from TP. Now the TP points at the end of the pthread struct (which is aligned) and there is a gap above it (which may also need alignment). The fix required changing TP_ADJ and __pthread_self on affected targets (aarch64, arm and sh) and in the tlsdesc asm the offset to access the dtv changed too.
2018-04-19reintroduce hardening against partially-replaced allocatorRich Felker-0/+9
commit 618b18c78e33acfe54a4434e91aa57b8e171df89 removed the previous detection and hardening since it was incorrect. commit 72141795d4edd17f88da192447395a48444afa10 already handled all that remained for hardening the static-linked case. in the dynamic-linked case, have the dynamic linker check whether malloc was replaced and make that information available. with these changes, the properties documented in commit c9f415d7ea2dace5bf77f6518b6afc36bb7a5732 are restored: if calloc is not provided, it will behave as malloc+memset, and any of the memalign-family functions not provided will fail with ENOMEM.
2018-04-17enable reclaim_gaps for fdpicRich Felker-2/+18
the existing laddr function for fdpic cannot translate ELF virtual addresses outside of the LOAD segments to runtime addresses because the fdpic loadmap only covers the logically-mapped part. however the whole point of reclaim_gaps is to recover the slack space up to the page boundaries, so it needs to work with such addresses. add a new laddr_pg function that accepts any address in the page range for the LOAD segment by expanding the loadmap records out to page boundaries. only use the new version for reclaim_gaps, so as not to impact performance of other address lookups. also, only use laddr_pg for the start address of a gap; the end address lies one byte beyond the end, potentially in a different page where it would get mapped differently. instead of mapping end, apply the length (end-start) to the mapped value of start.
2018-04-17ldso, malloc: implement reclaim_gaps via __malloc_donateAlexander Monakov-12/+4
Split 'free' into unmap_chunk and bin_chunk, use the latter to introduce __malloc_donate and use it in reclaim_gaps instead of calling 'free'.
2018-02-07disallow non-absolute rpath $ORIGIN for suid/sgid/AT_SECURE processesRich Felker-0/+3
in theory non-absolute origins can only arise when either the main program is invoked by running ldso as a command (inherently non-suid) or when dlopen was called with a relative pathname containing at least one slash. such usage would be inherently insecure in an suid program anyway, so the old behavior here does not seem to have been insecure. harden against it anyway.
2018-02-07honor rpath $ORIGIN for ldd/ldso command with program in working dirRich Felker-1/+10
the rpath fixup code assumed any module's name field would contain at least one slash, an invariant which is usually met but not in the case of a main executable loaded from the current working directory by running ldd or ldso as a command. it would be possible to make this invariant always hold, but it has a higher runtime allocation cost and does not seem useful elsewhere, so just patch things up in fixup_rpath instead.
2018-02-02adjust dladdr dli_fbase definition to match other implementationsRich Felker-1/+1
the Linux and FreeBSD man pages for dladdr document dli_fbase as the "base address" of the library/module found. normally (e.g. AT_BASE) the term "base" is used to denote the base address relative to which p_vaddr addresses are interpreted; however in the case of dladdr's Dl_info structure, existing implementations define it as the lowest address of the mapping, which makes sense in the context of determining which module's memory range the input address falls within. since this is a nonstandard interface provided to mimic one provided by other implementations, adjust it to match their behavior.
2017-11-13fix malloc state corruption when ldso rejects loading a second libcRich Felker-3/+4
commit c49d3c8adadfa24235fcf4779bb722b1aa6f480b added logic to detect attempts to load via another name and instead redirect to the existing libc, rather than loading two and producing dangerously inconsistent state. however, the check for and unmapping of the duplicate libc happened after reclaim_gaps was already called, donating the slack space around the writable segment to malloc. subsequent unmapping of the library then invalidated malloc's free lists. fix the issue by moving the call to reclaim_gaps out of map_library into load_library, after the duplicate libc check but before the first call to calloc, so that the gaps can still be used to satisfy the allocation of struct dso. this change also eliminates the need for an ugly hack (temporarily setting runtime=1) to avoid reclaim_gaps when loading the main program via map_library, which happens when ldso is invoked as a command. only programs/libraries erroneously containing a DT_NEEDED reference to via an absolute pathname or symlink were affected by this issue.
2017-07-04allow specifying argv[0] when invoking a program via ldso commandRich Felker-0/+7
previously, the pathname used to load the program was always used as argv[0]. the default remains the same, but a new --argv0 option can be used to provide a different value.
2017-07-04fix regression in dlopen promotion from RTLD_LOCAL to RTLD_GLOBALRich Felker-1/+6
commit 4ff234f6cba96403b5de6d29d48a59fd73252040 inadvertently removed the logic to do this when changing the representation of global status.
2017-07-04ldso: avoid spurious & possible erroneous work for libs with no depsRich Felker-4/+7
a null pointer for a library's deps list was ambiguous: it could indicate either no dependencies or that the dependency list had not yet been populated. inability to distinguish could lead to spurious work when dlopen is called multiple times on a library with no deps, and due to related bugs, could actually cause other libraries to falsely appear as dependencies, translating into false positives for dlsym. avoid the problem by always initializing the deps pointer, pointing to an empty list if there are no deps. rather than wasting memory and introducing another failure path by allocating an empty list per library, simply share a global dummy list. further fixes will be needed for related bugs, and much of this code may end up being replaced.
2017-03-21fix dlopen/dlsym regression opening libs already loaded at startupRich Felker-2/+2
commit 4ff234f6cba96403b5de6d29d48a59fd73252040 erroneously changed the condition for running certain code at dlopen time to check whether the library was already relocated rather than whether it already had its deps[] table filled. this was out of concern over whether the code under the conditional would be idempotent/safe to call on an already-loaded libraries. however, I missed a consideration in the opposite direction: if a library was loaded at program startup rather than dlopen, its deps[] table was not yet allocated/filled, and load_deps needs to be called at dlopen time in order for dlsym to be able to perform dependency-order symbol lookups. in order to avoid wasteful allocation of lazy-binding relocation tables for libraries which were already loaded and relocated at startup, the check for !p->relocated is not deleted entirely, but moved to apply only to allocation of these dables.
2017-03-15precalculate gnu hash rather than doing it lazily in find_sym inner loopRich Felker-8/+2
this change was suggested based on testing done by Timo Teräs almost two years ago; the branch (and probably call prep overhead) in the inner loop was found to contribute noticably to total symbol lookup time. this change will make lookup slightly slower if libraries were built with only the traditional "sysv" ELF hash table, but based on how much slower lookup tends to be without the gnu hash table, it seems reasonable to assume that (1) users building without gnu hash don't care about dynamic linking performance, and (2) the extra time spent computing the gnu hash is likely to be dominated by the slowness of the sysv hash table lookup anyway.
2017-03-14remove unused refcnt field for shared librariesRich Felker-4/+0
2017-03-14avoid loading of multiple libc versions via explicit pathnameRich Felker-1/+11
such loading is unsafe, and can happen when programs use their own logic to locate a .so file then pass the absolute pathname to dlopen, or if an absolute pathname ends up in DT_NEEDED headers. multiple loads with only the base name were already precluded, provided libc was named appropriately, by special-casing standard library names. one function symbol (in the reserved namespace, but public, since it's part of the crt1 entry point ABI) and one data symbol are checked. this way we avoid likely false positives, particularly from libraries interposing and wrapping functions. there is no hard requirement to avoid breaking such usage, since trying to run a hook before libc is even initialized is not a supported usage case, but it's friendlier not to break things.
2017-03-14emulate lazy relocation as deferrable relocationRich Felker-3/+66
traditional lazy relocation with call-time plt resolver is intentionally not implemented, as it is a huge bug surface and demands significant amounts of arch-specific code and requires ongoing maintenance to ensure compatibility with applications which make use of new additions to the arch's register file in passing function arguments. some applications, however, depend on the ability to dlopen modules which have unsatisfied symbol references at the time they are loaded, either avoiding use of the affected interfaces or manually loading another module to provide the missing definition via their own module dependency tracking outside the ELF data structures. while such usage is non-conforming, failure to support it has been a significant obstacle for users/distributions trying to support affected software, particularly the server. instead of resolving lazy relocations at call time, this patch saves unresolved GOT/PLT relocations for deferral and retries them after each subsequent dlopen until they are resolved. since dlopen is the only time at which the effective global symbol table can change, this behavior is not observably different from traditional lazy binding, and the required code is minimal.
2017-03-13reorder addend handling before symbol lookup in relocation codeRich Felker-16/+17
these two tasks are independent now, but in order to support lazy relocations, the failure path for symbol lookup may want the addend to be available.
2017-03-12rework ldso handling of global symbol table for consistencyRich Felker-44/+53
when loading libraries with dlopen, the caller can request that the library's symbols become part of the global symbol table, or that they only be used for resolving relocations in the loaded library and its dependencies. in the latter case, a subsequent dlopen of the same library can upgrade it to global status. previously, if a library was upgraded from local to global mode, its symbols entered the symbol lookup search order at the point where the library was originally loaded. this means that a new call to dlopen could change the value of a symbol that already had a visible definition, an inconsistency which applications could observe. POSIX is unclear whether this should happen or whether it's permitted to happen, but the resolution of Austin Group issue #982 made it formally unspecified. with this patch, a library whose mode is upgraded from local to global enters the symbol lookup order at the point where it was made global, so that symbol resolution before and after the upgrade are consistent. in order to implement this change, the per-dso global flag is replaced with a separate set of linked-list pointers for participation in the global symbol table. this permits the order of dso objects for symbol resolution to differ from the order used for iteration of all loaded libraries. it also improves performance of find_sym, by avoiding a branch per iteration and skipping, and especially in the case where many non-global libraries have been loaded, by allowing the loop to skip over them entirely. logic for temporarily adding non-global libraries to the symbol table for relocation purposes is also mildly simplified.
2017-03-11treat STB_WEAK and STB_GNU_UNIQUE like STB_GLOBAL in find_symSzabolcs Nagy-3/+1
A weak symbol definition is not special during dynamic linking, so don't let a strong definition in a later module override it. (glibc dynamic linker allows overriding weak definitions if LD_DYNAMIC_WEAK is set, musl does not.) STB_GNU_UNIQUE means that the symbol is global, even if it is in a module that's loaded with RTLD_LOCAL, and all references resolve to the same definition. This semantics is only relevant for c++ plugin systems and even there it's often not what the user wants (so it can be turned off in g++ by -fno-gnu-unique when the c++ shared lib is compiled). In musl just treat it like STB_GLOBAL.
2017-01-13fix crashes in x32 __tls_get_addrrofl0r-5/+5
x32 has another gratuitous difference to all other archs: it passes an array of 64bit values to __tls_get_addr(). usually it is an array of size_t.
2017-01-04fix crash from corrupted tls module list after failed dlopenRich Felker-0/+1
commit d56460c939c94a6c547abe8238f442b8de10bfbd introduced this regression as part of splitting the tls module list out of the dso list. the new code added to dlopen's failure path to undo the changes adding the partially-loaded libraries reset the tls_tail pointer correctly, but did not clear its link to the next list entry. thus, at least until the next successful dlopen, the list was not terminated but ended with an invalid next pointer, which __copy_tls attempted to follow when a new thread was created. patch by Mikael Vidstedt.
2016-11-11treat null vdso base same as missingBobby Bingham-1/+1
On s390x, the kernel provides AT_SYSINFO_EHDR, but sets it to zero, if the program being run does not have a program interpreter. This causes problems when running the dynamic linker directly.
2016-11-11generalize ELF hash table types not to assume 32-bit entriesRich Felker-2/+2
alpha and s390x gratuitously use 64-bit entries (wasting 2x space and cache utilization) despite the values always being 32-bit. based on patch by Bobby Bingham, with changes suggested by Alexander Monakov to use the public Elf_Symndx type from link.h (and make it properly variable by arch) rather than adding new internal infrastructure for handling the type.
2016-11-07fix ldso reserved library name handlingSzabolcs Nagy-19/+19
If a DT_NEEDED entry was the prefix of a reserved library name (up to the first dot) then it was incorrectly treated as a libc reserved name. e.g. dependency was not loaded as it matched libpthread reserved name.
2016-11-07fix accidental global static pointer in ldsoSzabolcs Nagy-1/+2
this was harmless as load_library is not called concurrently, but it used one word of bss.
2016-03-06generalize mips-specific reloc code not to hard-code sym/type encodingRich Felker-1/+1
this change is made in preparation for adding the mips64 port, which needs a 64-bit (and mips64-specific) form of the R_INFO macro, but it's a better abstraction anyway. based on part of the mips64 port patch by Mahesh Bodapati and Jaydeep Patil of Imagination Technologies.
2016-02-18fix regression in SH/FDPIC dynamic linkerRich Felker-0/+2
the dynamic linker was found to hang when used as the PT_INTERP, but not when invoked as a command. the mechanism of this failure was not determined, but the cause is clear: commit 5552ce52000855906a5cb4f08f2e456573cca51f removed the SHARED macro, but arch/sh/crt_arch.h is still using it to choose the right form of the crt/ldso entry point code. moving the forced definition from rcrt1.c to dlstart.c restores the old behavior. eventually the logic should be changed to fully remove the SHARED macro or at least rename it to something more reasonable.
2016-01-30ldso: fix GDB dynamic linker info on MIPSFelix Fietkau-4/+7
GDB is looking for a pointer to the ldso debug info in the data of the ..rld_map section. Signed-off-by: Felix Fietkau <>
2016-01-30fix regression in dynamic-linked tls when both main app & libs have tlsRich Felker-1/+1
commit d56460c939c94a6c547abe8238f442b8de10bfbd introduced this bug by setting up the tls module chain incorrectly when the main app has tls. the singly-linked list head pointer was setup correctly, but the tail pointer was not, so the first attempt to append to the list (for a shared library with tls) would treat the list as empty and effectively removed the main app from the list. this left all tls module id numbers off-by-one. this bug did not appear in any released versions.
2016-01-25move dynamic linker to its own top-level directory, ldsoRich Felker-0/+2077
this eliminates the last need for the SHARED macro to control how files in the src tree are compiled. the same code is used for both libc.a and, with additional code for the dynamic linker (from the new ldso tree) being added to but not libc.a. separate .o and .lo object files still exist for the src tree, but the only difference is that the .lo files are built as PIC. in the future, if/when we add dlopen support for static-linked programs, much of the code in dynlink.c may be moved back into the src tree, but properly factored into separate source files. in that case, the code in the ldso tree will be reduced to just the dynamic linker entry point, self-relocation, and loading of libraries needed by the main application.