Tag Archives: OS X

Using bridged networking with KVM/QEMU guests on a Linux host

Using KVM to virtualize guest operating systems under Linux is great, but the default networking configuration doesn’t allow incoming connections to the VM. This is not peculiar to KVM – this is the default for most (all?) virtualization technologies, including VirtualBox.

If, like me, you need to access you guest VM’s from the outside, then you need to setup bridge networking. There are plenty of resources on the web on how to do this, and this blog is simply a quick overview of the steps I took to enable bridge networking under KVM. It will serve as a reminder to my self of the steps I took, and possibly help others out as well.

Below is a list of the sites I used for setting up bridged networking with KVM under Linux:

https://help.ubuntu.com/community/NetworkConnectionBridge
http://drupal.bitfunnel.net/drupal/macosx-bridge-qemu
http://en.blog.guylhem.net/post/88201449689/running-qemu-with-tap0-and-nat-under-osx-109
http://superuser.com/questions/42249/how-to-bridge-two-ethernet-ports-on-mac-os-x

In a nutshell, setting up a bridge on Linux means installing the necessary bridge software and editing the /etc/network/interfaces file, both of which are described here:

https://help.ubuntu.com/community/NetworkConnectionBridge

If you’re like me, seeing a working config file gets you a long way, so below is my /etc/network/interfaces file. For reasons not related to bridging, my network is setup as a class B network, so you will probably have to adjust the various addresses accordingly:

$ cat /etc/network/interfaces

# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

iface eth0 inet manual

auto br0
iface br0 inet static
    address 172.16.0.190
    network 172.16.0.0
    netmask 255.255.0.0
    broadcast 172.16.0.255
    gateway 172.16.0.1
    dns-search mydomain.com
    dns-nameservers 172.16.0.10
    bridge_ports eth0
    bridge_stp off
    bridge_fd 0
    bridge_maxwait 0

Once bridging is setup on the Linux host, all that needs to be done is start QEMU with the correct network options, and then edit the guest network settings. The options to use for QEMU are:

-net nic,vlan=0
-net tap,vlan=0,ifname=tap0

Note that these options replace the existing QEMU network options, and are not to be used in conjunction with them.

It is these args that inform QEMU that you wish to use bridged networking.

To use bridge networking on Windows 7, add the above two arguments to your QEMU start line and then setup your network manually within Windows 7. For example, if you use this command for non-bridge networking:

qemu-system-x86_64 -enable-kvm -m 8192M -cpu host -drive file=/dev/zvol/rpool/kvm/windows7/disk0 -netdev user,id=vlan0 -net nic,model=e1000,netdev=vlan0 -smp 2

Then use this for bridged networking:

qemu-system-x86_64 -enable-kvm -m 8192M -cpu host -drive file=/dev/zvol/rpool/kvm/windows7/disk0 -net nic,vlan=0 -net tap,vlan=0,ifname=tap0 -smp 2

Note that this assumes you have previously setup bridge networking support in Linux. Also, you must run QEMU as root in order to use bridge networking.

To use bridge networking on OS X Mountain Lion, use the above two arguments for networking in your QEMU start line. For example:

qemu-system-x86_64 -enable-kvm -m 4096 -cpu core2duo -machine q35 -usb -device usb-kbd -device usb-mouse -device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" -kernel ./chameleon_svn2534_boot -smbios type=2 -device ide-drive,bus=ide.2,drive=MacHDD -drive id=MacHDD,if=none,format=raw,file=/dev/zvol/rpool/kvm/mountain-lion/disk0 -net nic,vlan=0 -net tap,vlan=0,ifname=tap0 -monitor stdio -smp 2

And then setup your network manually within Mountain Lion using these commands:

sudo ifconfig bridge0 create
sudo ifconfig bridge0 addm en0
sudo ifconfig bridge0 up

These commands aren’t necessary, but may be useful if you make a mistake:

sudo ifconfig bridge0 down
sudo ifconfig bridge0 deletem en0
sudo ifconfig bridge0 destroy

To use bridge networking on OS X Mavericks, use the OS X Network utility as described in the above posts to add a bridge, making sure to add your Ethernet device (en0) to it. Then add use the two QEMU networking options above in your QEMU command-line along with the virtio driver. For example:

qemu-system-x86_64 -enable-kvm -m 4096 -cpu core2duo -machine q35 -usb -device usb-kbd -device usb-mouse -device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" -kernel ./chameleon_svn2534_boot -smbios type=2 -device ide-drive,bus=ide.2,drive=MacHDD -drive id=MacHDD,if=none,format=raw,file=/dev/zvol/rpool/kvm/mavericks/disk0 -net nic,model=virtio,vlan=0 -net tap,vlan=0,ifname=tap0 -monitor stdio -smp 2

Installing OS X KVM/QEMU guest onto a Linux host zvol

First off, let me start by saying that most of what you read in this post is information I’ve gathered from other sources. Why then have a blog about the subject? Because for some reason, none of the procedures on the other sites completely worked for me. Using the knowledge learned from other sites, along with a few minor tweaks and trial and error, I finally got a sequence that worked for me. This blog will serve as a reminder to me of what I did should I need to do it again and may also help other people who are in the same situation I was in.

In my quest to get an OS X KVM guest under Linux, I visited many, many sites. I can’t be 100% sure that I’ve included all those sites below, but here is a list of sites I’m positive I’ve frequented in order to get OS X running under KVM. 90% of what I present here is a derivative of their work:

http://blog.ostanin.org/2014/02/11/playing-with-mac-os-x-on-kvm
http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM
http://kernelpanik.net/running-mac-osx-yosemite-on-kvm
https://macosxvirtualmachinekvm.wordpress.com/guide-mac-os-x-vm-on-unraid
http://forums.appleinsider.com/t/159955/howto-create-bootable-mavericks-iso

I have tested the procedure below with Mountain Lion, Mavericks and Yosemite. For Mavericks and Yosemite, there are extra steps needed to get networking going, but the steps are manageable.

The first problem I encountered was how to create a usable ISO image from the OS X installers obtained through the App Store. Some of the sites claimed you could simply use dd under Linux, while others said you could use the Mac Disk Utility to extract the image. Yet other sites had long sequences of commands and/or scripts to run that would create the ISO for you. There is even a createinstallmedia command on Mavericks and Yosemite. For whatever reason, none of these worked for me. The installation method either failed altogether or presented me with this error when trying to create the VM:

With Mountain Lion or Mavericks:

Can't find /mach_kernel

With Yosemite:

Can't find /System/Library/Kernels/kernel

Since having an ISO image of OS X is step #1, I do not know how other people have gotten OS X to work under KVM by following the directions on the web for creating an ISO. I’m assuming all those people can’t be wrong and I must be the problem, but since I couldn’t get a working ISO using the procedures they describe, I’ve slightly modified those procedures to get a working ISO. To me, it seems the problem is that the bootloader used to bootstrap the system (more on that later) can’t/isn’t looking for the kernel inside of the package (which makes perfect sense to me). To compensate, I’ve extracted the kernel from the package and put them where the bootloader wants them to be.

Note that you will need a Mac in order to create the ISO images as described below. I’m sure there is a way to achieve the same results using Windows or Linux, but since all of the posts I’ve read used a Mac, and I happen to have a Hackintosh that I recently built handy, I just created the ISO on my Hackintosh. If you have another way to create the ISO or already have a working one, then you can skip the ISO creation stuff.

From poking around the OS X installers and reading other posts, I learned that the kernel name and location changed in Yosemite, so that explains why I get two different errors. The next question is why isn’t the kernel present in the OS X Installer? Examining the installer does indeed show there is no /mach_kernel in the Mountain Lion or Mavericks installer, or /System/Library/Kernels/kernel in the Yosemite installer. However, digging a little deeper, I learned that the kernels do exist in a .pkg file inside of the installer. For Mountain Lion and Mavericks, that package is BaseSystemBinaries.pkg. For Yosemite, it’s Essentials.pkg. After downloading the OS X Installer from the App Store, the packages containing the kernel can be found by:

1) Right-clicking the “Install OS X Mountain Lion|Mavericks|Yosemite” icon in Finder
2) Selecting “Show Package Contents”
3) Opening the resulting “Contents” folder
4) Opening the “SharedSupport” folder
5) Double-clicking the “InstallESD.dmg” icon
6) Opening the “Packages” folder

If you install the Pacifist program from https://www.charlessoft.com, you can then right-click the correct package (BaseSystemBinaries.pkg or Essentials.pkg) and select “Open With->Pacifist”. From there, you can select the “mach_kernel” file for Mountain Lion or Mavericks and hit “Extract To…” to extract the kernel. For Yosemite, you extract “System/Library/Kernels/kernel” instead.

With the kernel now extracted, I figured I could copy it to the correct place needed by the bootloader and get past the above error. To accomplish this, I slightly modified the script that can be found here: http://forums.appleinsider.com/t/159955/howto-create-bootable-mavericks-iso. Basically, I inserted a line in the script to copy the kernel into the ISO during creation:

cp -rp ~/Desktop/mach_kernel /Volumes/install_build

Here is the script in its entirety, with my one-line change:

# Mount the installer image:
hdiutil attach /Applications/Install\ OS\ X\ Mountain\ Lion.app/Contents/SharedSupport/InstallESD.dmg -noverify -nobrowse -mountpoint /Volumes/install_app

# Convert the boot image to a sparse bundle:
hdiutil convert /Volumes/install_app/BaseSystem.dmg -format UDSP -o /tmp/mountain-lion

# Increase the sparse bundle capacity for packages, kernel, etc.:
hdiutil resize -size 8g /tmp/mountain-lion.sparseimage

# Mount the sparse bundle target for further processing:
hdiutil attach /tmp/mountain-lion.sparseimage -noverify -nobrowse -mountpoint /Volumes/install_build

# Remove Package link and replace with actual files:
rm /Volumes/install_build/System/Installation/Packages
cp -rp /Volumes/install_app/Packages /Volumes/install_build/System/Installation/

cp -rp ~/Desktop/mach_kernel /Volumes/install_build/

# Unmount both the installer image and the target sparse bundle:
hdiutil detach /Volumes/install_app
hdiutil detach /Volumes/install_build

# Resize the partition in the sparse bundle to remove any free space:
hdiutil resize -size $(hdiutil resize -limits /tmp/mountain-lion.sparseimage | tail -n 1 | awk '{ print $1 }')b /tmp/mountain-lion.sparseimage

# Convert the sparse bundle to ISO/CD master:
hdiutil convert /tmp/mountain-lion.sparseimage -format UDTO -o /tmp/mountain-lion

# Remove the sparse bundle:
rm /tmp/mountain-lion.sparseimage

# Rename the ISO and move it to the desktop:
mv /tmp/mountain-lion.cdr ~/Desktop/mountain-lion.iso

Be sure to have the kernel you extracted in your Desktop directory, or edit the script accordingly.

After running the script, you will have a mountain-lion.iso under Desktop that is suitable for creating a KVM OS X guest. With a few edits around the operating system names, this script also works with Mavericks and Yosemite, but for Yosemite, the extracted kernel name is “kernel” and needs to be copied to /System/Library/Kernels instead:

cp -rp ~/Desktop/kernel /Volumes/install_build/System/Library/Kernels

Note that once created, you can also burn the ISO to a DVD and restore it later using dd:

dd if=/dev/cdrom of=mountain-lion.iso

This will prevent you from having to repeat this procedure should you need to do it again at a later time.

With the ISO creation done, it is now time to create a zvol. You can do that with the following command:

zfs create -p -V 60G rpool/kvm/mountain-lion/disk0

This will create a 60GB zvol named mountain-lion in the rpool zpool, under dataset kvm/mountain-lion. You can of course substitute the names and size as with your own values.

Next, it’s time to create the actual VM. From what I’ve read, you need to be running kernel version 3.15 or later, as well as QEMU 2.2 or later. I’m not sure if this is true, but I heeded the warning and upgraded both my kernel and QEMU versions.

Before continuing, you need a copy of the Chameleon bootloader. You can find it on some of the above links, or download it here:

Chameleon Bootloader

Assuming you have all the necessary tools installed (KVM, QEMU, etc.), creating the VM is as simple as:

qemu-system-x86_64 \
-enable-kvm -m 4096 \
-cpu core2duo \
-machine q35 \
-usb -device usb-kbd \
-device usb-mouse \
-device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" \
-kernel ./chameleon_svn2534_boot \
-smbios type=2 \
-device ide-drive,bus=ide.2,drive=MacHDD \
-drive id=MacHDD,if=none,format=raw,file=/dev/zvol/rpool/kvm/mountain-lion/disk0 \
-net nic,model=e1000,netdev=net0 \
-netdev tap,id=net0 \
-monitor stdio \
-device ide-drive,bus=ide.0,drive=MacDVD \
-drive id=MacDVD,if=none,snapshot=on,file=./mountain-lion.iso
-smp 2

Note that for Yosemite, you must disable msrs first by running this command as root:

echo 1 > /sys/module/kvm/parameters/ignore_msrs

After running the above qemu-system-x86_64 command, QEMU will start a VNC session that you can connect to in order to finish the installation. I used the TightVNC viewer on Linux, and usage couldn’t be simpler:

vncviewer localhost:5900

This will connect the VNC viewer to localhost port 5900, which is the default. Note that if you have another QEMU instance running, you may need to change the port number, as they increase sequentially with each instance.

Once connected, you will be a the Chameleon boot prompt. I found that I needed to enter the boot option “GraphicsEnabler=No” in order to get a successful installation, so you may need to do the same. At the boot: prompt, type:

boot: GraphicsEnabler=No

After that, the Mac OS X installation should start. Note that for some reason, the installation exits the VNC viewer shortly after starting, and so you have to reconnect a second time. Once connected the second time, it may be a while before the installation splash screen appears. If after 5 or so minutes it doesn’t appear, simply exit QEMU and try again.

Once you’ve reached the splash screen, the first thing you will notice is that the virtual VNC cursor doesn’t quite follow the real cursor. This makes it painful to navigate the menus, so learning to use the keyboard, particularly the Enter and Space keys, will be beneficial. Later on you can get around this issue by using the native Mac Screen Sharing utility instead of the QEMU VNC server.

Now you can follow the usual directions for installing OS X. Don’t forget to partition your zvol using the Disk Utility during installation!

Once the installation is finished, OS X will reboot and you will be back to the Chameleon boot prompt, but this time with two options to boot. The first option is the ISO installer, named “OS X Base Installer”, and the second option is the KVM VM, and is named Mountain Lion, Mavericks or Yosemite. Be sure to select the VM when booting, else the installer will start all over again. If that happens, simply reboot again and choose the right option.

Finish the installation steps, and if all goes well, you should now have an OS X KVM guest running under Linux!

If you installed Mountian Lion, you are all set. If you installed Mavericks or Yosemite, there are a couple of more steps you need to do in order to get networking functional. Apparently, the e1000 driver does not work under Mavericks or Yosemite and so you must install the virtio driver which you can find in the above links or download it here:

Virtual IO Driver

Now the problem is getting the driver to the guest when you have no network. Fortunately, QEMU provides a mechanism for sharing a FAT folder between the host and guest. Unfortunately, the procedure didn’t work for me. After hours of debugging, it turned out that my problem was the procedure didn’t work with the version of QEMU I was using – 2.3.94, but did work with 2.0.0. So I fired up 2.0.0 just to get the driver over to the guest. To create a shared FAT folder, create a directory named VirtIoNetDrv and download the virtio driver there. Then start QEMU 2.0.0 using the same command above, but with the following two additions:

-device ide-drive,bus=ide.4,drive=fatdrive
-drive id=fatdrive,file=fat:ro:VirtIoNetDrv

Also, change the e1000 line to virtio:
For Mavericks:
-net nic,model=virtio,netdev=net0
For Yosemite:
-net nic,model=virtio,netdev=net0,vectors=0

When the OS X guest starts, you will have a drive mounted from which you can copy the virtio driver from. After copying it somewhere to the guest OS, restart the newer version of QEMU as originally described (without the fatdrive arguments, but with the virtio driver instead of e1000). Install the virtio driver and configure networking. That’s it! I imagine this QEMU quirk will be worked out in a future version, and when it does, you won’t have to fire up an older version. It’s also possible that perhaps the way shared FAT folders work in the newer QEMU has changed, and so the procedures on the web for sharing folders does not work.

After installation, you can start the VM by using a slightly simpler QEMU command:

qemu-system-x86_64 \
-enable-kvm -m 4096 \
-cpu core2duo \
-machine q35 \
-usb -device usb-kbd \
-device usb-mouse \
-device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" \
-kernel ./chameleon_svn2534_boot \
-smbios type=2 \
-device ide-drive,bus=ide.2,drive=MacHDD \
-drive id=MacHDD,if=none,format=raw,file=/dev/zvol/rpool/kvm/mountain-lion/disk0 \
-monitor stdio \
-net nic,model=e1000,netdev=net0 \
-netdev tap,id=net0 \
-smp 2

For Mavericks and Yosemite, don’t forget to substitute virtio for e1000, and for Yosemite only, don’t forget to add the vectors=0 option.

Note that you do not need to use the “GraphicsEnabler=No” option to boot – you can simply press Enter. To avoid having to press Enter at all, you can tell OS X to skip the bootloader prompt. To do so, simply create the file /Extra/org.chameleon.boot.plist with these contents:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Timeout</key>
    <string>5</string>
    <key>EthernetBuiltIn</key>
    <string>Yes</string>
    <key>PCIRootUID</key>
    <string>1</string>
</dict>

After that, OS X will boot automatically without the need to press Enter at the bootloader.

As mentioned earlier,when you use the VNC provided with QEMU, the virtual mouse pointer doesn’t follow the native mouse pointer, which makes it difficult to use. This can be fixed by enabling screen sharing through OS X and using that instead. But to do that, you will first need to setup bridge networking, which you can read about in my next post.

Madness and the Minotaur for OS X, part II

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:

http://stackoverflow.com/questions/27682745/using-sigaction-and-setitimer-system-calls-to-implement-assembly-language-timer

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:

http://www.frijid.net/madness/madness.html

Next, it’s on to a Windows port, which is sure to be harder than even the OS X port.

Madness and the Minotaur for OS X, part I

So the first request for a port of Madness has arrived, and that request was to port it to OS X. Not quite what I expected, as my money would have been on a request to port it to Windows. However, I was happy that is wasn’t, as porting to OS X would surely be easier as it is a BSD derivative.

I’m not an OS X expert, but I knew that being a BSD derivative, the calling convention was different. Linux system calls are more like DOS in that they take their arguments in registers. BSD, and hence OS X, follow the more traditional UNIX convention of passing arguments by pushing them onto the stack. Since I encapsulated all of the hardware/operating system functionality in a module called hardware.s (which I’m now thinking should be renamed platform.s), adding the pushes before making system calls should be trivial.

So I fired up my trusty Hackintosh system that I created a year or so ago (a Lenovo T530 running OS X Lion), cloned the madness repository and executed make. I didn’t expect it to work, but I certainly didn’t expect to see the errors it was complaining about; errors about unknown pseudo-ops like .equ, .ifdef, and .bss. I thought perhaps it wasn’t using the GNU assembler, so I checked the version:

$ as -v
Apple Inc version cctools-822, GNU assembler version 1.38

Version 1.38? That seems pretty old, as I recall binutils being version 2.x for some time now. Just to be sure, I checked the version on my Linux box:

$ as -v
GNU assembler version 2.22 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.

Sure enough, it is version 2.22.

I also have OS X Mavericks running on my Hackintosh, so I fired it up to see what version it is using. To my surprise, it is the same old version. A little digging around revealed that it is well known that Apple’s GNU toolchain is quite old, and the reason for it seems to be it is being replaced with LLVM. So there will be no new versions of the GNU toolchain shipped with OS X. With this revelation, I tried using llvm-gcc-42 – same errors. I imagine that llvm-gcc probably uses /usr/bin/as anyway.

Next thing to try was clang. With clang, the original errors disappeared, but were replaced by complaints of the unknown directives .rept and endr. Checking my clang version:

$ clang -v
Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn)

and cross-checking that with what’s out there, I find clang is outdated too. Rather than upgrade to the latest & greatest version, I opted to try the next version – clang 3.2. After downloading and installing clang 3.2, my preprocessor problems disappeared and in their place were a few assembler errors that I could deal with.

After working out all the assembler errors, adding push instructions before making system calls, and changing a few system call numbers that differed between OS X and Linux, I had Madness running on OS X. However, the timer, which is central to the game, was not working.

I tried to debug the problem on OS X, but ran into problems with gdb – there were no symbols in the binary for it to use. None of the usual command-line options seemed to work with clang. So I switched to the lldb debugger. This debugger had symbols, but no line information, so I couldn’t set breakpoints on line numbers and so had to resort to addresses. After messing with lldb for a while, I decided the tools on OS X weren’t up to par and figured I’d have an easier time porting to BSD first, and then from BSD to OS X. That way I could shake out all the bugs on BSD (where I have the latest GNU toolchain), and after I had it working there I could try porting it to OS X again. So the OS X port was put on hold in favor of a BSD port!

Update: As mentioned elsewhere in my blog, I have recently setup OS X to run as a KVM guest under Linux. In doing that, I have learned that installing Xcode and then the command-line tools for Xcode gives a working tool chain in which I can compile Madness. I have tried this with both Mountain Lion and Mavericks. This means there is no reason to install clang manually. I haven’t tried the debugger yet, but will post back when I do.