[ Home ]
SBCL Internals

The pages on this CLiki-driven site can be edited by anybody at any time. No warranty of any kind can therefore be made; any implied warranties of merchantability or fitness for a particular purpose are expressly disclaimed
[ Home ] [ Recent Changes ] [ About CLiki ] [ Text Formatting ]

SBCL supports Darwin aka Mac OS X on PPC since 0.8.3, and provides experimental support for Darwin on x86, incuding MacOS on Intel, since CVS versions 0.9.10.15. Important features of these ports are described here.

History

The SBCL PPC port was originally based on Gary Byers' port of CMUCL to the PowerPC. Daniel Barlow ported this to SBCL on Linux PPC. Pierre Mai? started the port to OS X, then handed it off to Patrik Nordebo?, who then worked on it before handing it to Brian Mastenbrook. Since then runtime-related issues have been mostly supported by Brian; Patrik has worked on a port of gencgc to PPC as well. Raymond Toy? has used some of this to port CMUCL to OS X. Cyrus Harmon used Raymond's CMUCL port of the generational garbage collectod and Patrik's initial GENCGC porting work to enable GENCGC (in a precise, not conservative, despite the name, garbage collection mode) on SBCL/PPC.

Supported Features

As of 0.8.21 SBCL on the PPC used the twospace copying collector, which means that threads are not supported. All other features expected of a SBCL port are available: dynamic linking, linkage-tables, Unicode, etc.

As of 0.9.10, SBCL on PPC uses the GENCGC garbage collector. This is a necessary but not sufficient step for bringing Threads? to PPC. Additional work includes locking primitives and thread library calls. It has been reported that the PPC GENCGC garbage collector has an infrequent though fatal bug as of 1.0.16. The twospace GC seems free of this bug.

Unique features / workarounds

Virtual memory space reservation by the linker

In 10.3 "Panther" Apple moved the malloc heap to start where read-only space expected to be mapped by the runtime. Moving the read-only space was not a simple matter of changing the addresses because jumps to read-only space are written throughout the core file as absolute branches, which have a fixed number of bits for the branch target. Typically this is not a problem because this part of virtual memory has been available to us once the runtime is booted up; however, the malloc heap is established before main() is called in the runtime.

The two possible solutions to this problem: rewrite the appropriate VOPs to load and jump to an address in read-only space without an absolute branch instruction, or find some method of reserving this space before malloc() can. The latter of these approaches was chosen, and the method chosen was the linker. Generally the linker gets first pick at virtual memory because non-position-independent code needs to be mapped at an absolute address. Because OS X does not have linker scripts, a small program was written to output an object file which contained a segment which would be mapped at the correct address and correct size, and be mapped as a zero-fill copy-on-write allocation.

Apple's implementation of nm did not understand this object file nor the SBCL binary when it was linked in (it expected that the size of the segment would be less than the size of the file, which is not true when the segment maps as zero-fill copy-on-write memory). The first solution to this was to link the SBCL binary once, use nm to read the symbol table, and then link the SBCL binary a second time with the generated object file. (nm is needed to obtain the addresses of functions in the runtime that Lisp calls directly.) This was based on the assumption that the linker would pick the same addresses for symbols when the extra object file (which did not define any symbols or map any of the binary into memory) was linked in. This was indeed true in the vast majority of cases; however, occasionally the linker did move around symbols. This resulted in segfaults when lisp attempted to call back into C during target-2. Adding a new function or expanding the size of data used in the runtime was a frequent way of triggering this; choosing a different version of gcc often seemed to work around it.

In 0.8.21.32 a different method of reserving this virtual memory space was added: now the generated object file would be written so that the segment had a size of 0 but the correct starting address, and linked into the runtime before nm was called. nm correctly reads the symbol table of the linked sbcl binary, and a second program (ppc-darwin-fix-rospace) is called to modify the linked binary by changing the size of the segment to the correct size (without modifying any addresses of symbols). In addition, this mechanism has been generalized to reserve space for every allocation that needs to be at a fixed starting address. This should prevent changes in the starting location of the malloc heap from ever conflicting with our fixed space addresses.

Originally I considered this workaround to be a "hack", but I don't believe that it is really a hack any longer. It is the linker's job to map bits of C code at their expected (absolute) addresses from a binary file; the only difference here is that the spaces are not filled in until the runtime is booted and the core is mapped.

sigreturn() bug workaround

Apple's first implementation of OS X for the PowerPC 970 (named by Apple the G5) did not have a 64-bit userspace; however, a guarantee was made that the kernel would preserve the contents of 64-bit registers across context switches to enable them to be used for 64-bit long long arithmetic. However, a bug in their implementation of this feature prevented several lisps (SBCL, OpenMCL, and LispWorks) from working. On return from a signal, the registers would be restored from a second sigcontext which preserved the 64-bit integer registers. This sigcontext did not duplicate the floating point registers which would be contained in the 32-bit sigcontext; the kernel was expected to restore the floating point registers from the 32-bit sigcontext. This did not happen when a signal interrupted a system call; the floating point registers were restored from garbage memory. While this bug did not affect many user applications on OS X, signals are used quite frequently for the implementation of garbage collection in Lisp. When the floating point exception register was restored from garbage memory, it often had the floating point exception bit set, which resulted in an immediate SIGFPE after returning from the signal handler.

The workaround was to call sigreturn() explicitly in the signal handler, which invoked the version of sigreturn which did not restore the 64-bit registers (and did restore the floating point registers correctly). This worked because the new sigreturn() was given a new system call number and calling sigreturn() explicitly resulted in a call to the old system call number. However, this version of sigreturn is likely to disappear in a future revision of OS X. Apple's bug has been fixed since sometime in the 10.3.x series, but if compatibility is to be maintained with 10.2.8 "Jaguar", a new workaround will need to be added. Gary Byers has added a workaround to OpenMCL which clears the floating-point exception bit manually; this may be sufficient.

[Add more details about Darwin/x86 here or an a new page]

Supported Versions of Darwin

[Which versions of Darwin are known to work? Which ones are known not to work? In particular, do we still support 10.2.8 or whatever the last version of 10.2 was? The DARWIN_FIX_CONTEX stuff can go away if we don't support this anymore.]


This page is linked from: Brian Mastenbrook   Floating Point Problems on PPC   index  

CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively