With the port from Linux to BSD done, I now returned to the OS X port. My thinking that porting from BSD to OS X would be easier than porting from Linux to OS X turned out to be true, but there were still unforeseen obstacles to come.
The first obstacle I ran into was my game timer wasn’t working on OS X. On Linux and BSD, I was using the sigaction and setitimer system calls to implement a timer that would execute a timer_service routine via SIGALRM once per second. Unfortunately, this mechanism wasn’t working on OS X, and I wasn’t sure which syscall was in error – sigaction or setitimer, since both were needed to implement the timer. My gut feeling (and experience) told me it was sigaction, but I wasn’t 100% sure. I’ve run into problems with sigaction in the past (including during the BSD port) and the problem usually turns out to be finding the right sigaction struct to use. While researching this, I learned something I did not know about OS X – it requires the stack to be aligned on 16-byte boundaries when making external calls. I thought this might be the issue, so I made sure to align the stack, but it didn’t help. I spent a lot of time looking into this, even posting the following question on Stack Overflow:
Unfortunately, as of yet, there have been no answers to my question. While waiting for an answer, I discovered that the OS X library function, _sigaction(), did work. Of course, this requires me to link against libc, but while I research the problem and/or wait for an answer, it will allow me to continue work on the port. I’m curious why the other syscalls I’m using work (open, read, write, etc.), but sigaction doesn’t. One thing I noticed is that the disassembly of _sigaction() is quite lengthy compared to other system calls, leading me to believe the library call is doing something extra.
With the sigaction problem solved for the moment, I next had to focus on the 16-byte alignment issue. Most of the reading I did on this subject turned out to be false, causing me to waste enormous amounts of time. For example, I read that OS X would start your program with the stack already 16-bit aligned and that the stack needed to be aligned on a 0xXXXXXX0c boundary at the point of the syscall. Both of these turned out to be false. Initially, my program kept crashing randomly upon startup. I discovered this problem while debugging, when I noticed the starting address of the stack in _start was always 4-byte aligned, but only 1/4 of the time was 16-byte aligned. I solved this problem by adding code to align the stack to 16-bytes upon start-up. As for being aligned at 0xXXXXXX0c before a call, it turns out it needs to be aligned on 0xXXXXXX00.
With the start-up problem out of the way, I focused on aligning the stack on all routines that made external calls (system calls, in the case of Madness) on a 16-byte boundary. This proved to be harder than it sounds and turned out to be very time-consuming. Again, the documentation on this subject led me astray. Supposedly there are two options, -mstackrealign and -mstack-alignment=16, that can be used to solve this problem. Unfortunately, they don’t appear to work. The clang compiler accepts them, but they don’t seem to align the stack. I have a suspicion that these arguments, as well as the guarantee that the stack starts out 16-bit aligned, only applies when writing C code and not assembly.
As if the above problems weren’t enough, I learned that the lldb debugger that I was using was reporting the esp address incorrectly. I aligned all my code by single-stepping through the debugger, but in the end the code still didn’t work. When I substituted printing the esp via the code, I noticed the values were different (usually off by 8). I believe using the debugger skews the stack pointer, and so should not be used to help manually align the stack. After discovering this, I had to go back and realign the stack on all routines that made syscalls.
Another oddity I discovered with the lldb debugger was using it remotely through a terminal. For most of the OS X work, I used OS X Mavericks on my Hackintosh (which I created using the excellent utilities and tutorials over at tonymacx86 http://www.tonymacx86.com). As I started to port to more operating systems, it made sense to me to instead virtualize OS X and stick the VM on one of my servers, just like I was doing with FreeBSD. That way I could free up my laptop to run Linux and ssh into all the various platforms needed to develop Madness. So I created a Snow Leopard VM under Virtual Box, using the same utilities from tonymacx86. I had to start with Snow Leopard for two reasons:
1) VirtualBox doesn’t support booting from USB, so I needed OS X to be on a DVD.
2) The only DVD of OS X I have is for Snow Leopard, and the iBoot (http://www.tonymacx86.com/downloads.php?do=cat&id=3) software doesn’t seem to support booting itself from CD and then installing OS X from USB. As far as I could tell, both iBoot and OS X either need to reside on disc or USB – you cannot have one on CD and the other on USB.
Once Snow Leopard was installed, I upgraded it to OS X Lion using the xMove Lion utility (http://www.tonymacx86.com/downloads.php?do=cat&id=9). Once on Lion, I upgraded to OS X Mountain Lion, using xMove Mountain Lion. There doesn’t seem to be an xMove for Mavericks, so my VM is currently stuck at Mountain Lion. All of this worked fine, but when I tried to use lldb remotely through ssh it kept exiting with one of these errors:
error: initial process state wasn't stopped: exited
error: process exited with status -1 (lost connection)
When I ran lldb locally on the VM, it prompted me for my credentials. After digging around, it turns out I had to enable security by running this command directly on the OS X VM (not through ssh):
/usr/sbin/DevToolsSecurity -enable on
Once I did that, I was able to use lldb remotely for debugging.
With all these problems out of the way, Madness on OS X is now a reality. The code seems to work, but I won’t swear that I’ve found and aligned all possible areas where the stack needs to be aligned on 16-bytes. To make tracking this easy, I put in a check for an unaligned stack on OS X only. If the code detects an unaligned stack, it will print an error message and exit.
You can find the binaries for OS X, as well as the source code, here:
Next, it’s on to a Windows port, which is sure to be harder than even the OS X port.