Since for Eulora2 reasons I've been spending more time with the KVM switch pointed at my CentOS box lately, I eventually found that said box didn't have my trusty Scheme interpreter installed. My ensuing attempt to correct this deficiency turned up the fact that evidently nobody, myself included, had ever tried to build the interpreter on such a system, namely, one based on the much-loathed, barnacle-encrusted, yet traditional GNU C library (glibc). Non-GNU Linux systems based on musl libc had hosted the interpreter's early development ; OpenBSD, a system of entirely different lineage, then featured in its testing at various points. And in general I wrote it with due attention to portability, and for the most part steered clear of higher-level C library interfaces (even - or especially - malloc/free
: there's a garbage collector anyway, so why suffer two different and incompatible heap allocation systems in one executable). Despite the conservative approach, there's always something that comes up when the rubber of theory meets the road of practice, potholes and all, or "what can go wrong, will go wrong" and all that.
In this case, what went wrong was a result of the deliberate design of glibc. Its header files by default only expose a bare minimum of programming interfaces, basically those conforming to sufficiently old standards. To use anything vaguely recent, no matter how widely supported it may be in the Unix community - and by "recent" we're talking for instance 1993 - you have to define some assortment of "feature test macros" (FTMs) prior to including the headers. The idea is that this enables libc to introduce new interface names without conflicting with old programs that happened to use the same names for themselves, and perhaps that it promotes more portable code by ensuring developers don't unknowingly introduce nonstandard dependencies.
It's dubious whether this does any good in practice, as the collection of FTMs is rather confusing, arbitrary and coarse-grained. That is, a program doing even a moderate amount of system interfacing is going to end up with such a broad swath enabled anyway that there's little hope of keeping the environment "clean". Further, most Unices are much less restrictive with the default environment, with the result that presumed portable programs developed elsewhere become a pain to get working on glibc. Its most general FTM is _GNU_SOURCE
, basically the one-stop switch for "get out of my way and let me use all the things that are supposed to be there" - undermining the supposed benefits of the visibility control and bringing in the particularly ugly effect that GNU extensions may be exposed which conflict with standard interfaces sharing the same name. Short of that portability poison pill, there are various narrower FTMs with names like _BSD_SOURCE
and _POSIX_C_SOURCE
, with the latter demanding a numeric value.
I took the approach of enabling the narrowest set of macros that did the job, in conjunction with directly declaring some of the desired syscall-wrapper functions (in many cases the type signature is simple enough that the header mess can be bypassed altogether). Besides this unwelcome make-work, there was also a more substantive portability issue brought to the fore, where I was using the fairly new O_CLOEXEC
flag (Linux 2.6.23) and pipe2
syscall (Linux 2.6.27), because they were the only guaranteed thread-safe way to do what I wanted (prevent leaking newly opened file descriptors to child processes). I had included example fallback code for older systems, which is safe for now because the interpreter is strictly single-threaded. Finding this all a little unclear from the perspective of a fresh reader of the code, I expanded the comments and added a HAVE_POPEN2
setting, on by default, giving the user a clear way to switch implementations in the event of a build or runtime failure due to a system lacking those interfaces.
Patch | Seals | Tree |
---|---|---|
gscm_glibc_build_fix.vpatch(i) | jfw | Browse |
As to the larger implications of this development, it means the Gales Bitcoin Wallet signer should now work on CentOS 6 for amd64, and most other legacy distributions too. (JWRD support will remain on a reasonable-effort basis for these systems.) Combined with last June's performance improvement I'd say this justifies an update to our overall Bitcoin software release, although bitcoind
(the node implementation) remains unchanged. Thus:
fetch-bitcoind-0004.sh, signature.
If everything was already working fine for you, there's no particular need to upgrade ; of course this will be my reference going forward and if you do choose to try it out, your report of how it went would be most welcome in the comments.
- Improve portability to glibc systems and add a HAVE_PIPE2 configurable. [^]