Category Archives: Mac OS X

apr-1-config broken under Mavericks

UPDATE October 23 2013 As commenters point out below, simply installing Xcode 5.0.1 and its Command Line Tools is enough. I’ve finally got around to testing out the suggestion provided to me almost two weeks ago, and it worked.


The content below is preserved for posterity. Please just install Command Line Tools and see if that helps.

Is perhaps brew install serf or brew install subversion broken for you on Mavericks? Or generally apr-1-config seems to return spurious results referring to nonexistent locations in /usr/include or to OSX10.9.xctoolchain?

While Apple does not recommend touching any system file or folder as they may and will be overwritten during OS upgrade, consider editing the following lines:

-prefix="/usr"
+prefix="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr"
-CC="/Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.9.xctoolchain/usr/bin/cc"
-CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.9.xctoolchain/usr/bin/cc -E"
+CC="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc"
+CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -E"

Now that I have serf package installed, all I have left to figure out (or get someone else to figure out!) is subversion. Only then I might have some hope of getting hgsubversion to work again and getting some work done without resorting to Subversion directly.

Running a physically installed Ubuntu in VirtualBox on Mac

What follows includes my thoughts and notes more than definitive knowledge. I did manage to run Ubuntu in VirtualBox, but the process how I got there may include incorrect conclusions. Here goes.

  • VirtualBox 4.2.16 under OS X
  • OS X 10.8.4
  • Ubuntu 12.04 32-bit
  • Windows 8 32-bit
  • MacBook Unibody late 2009

(Yes, I presume you have the above setup; if you have something that’s 64-bit, unfortunately, I can’t help you there.)

DISCLAIMER

Everything in this post is dangerous to your data, even if you are an expert. Think thrice before doing anything, back up everything, triple-check everything I suggest you do for hidden assumptions that may be related to my machine.

Some steps may have inadvertently been omitted. I did not note everything I did, and I don’t feel like reproducing each and every of my steps.

I know this disclaimer will probably not dissuade you from trying this, but this includes particularly dangerous stuff and I don’t want anyone misunderstanding: I am NOT advising you to do this. I am NOT advising you to follow my advice. I am only documenting this process for my own private future use.

Mac specifics

Mac runs EFI instead of BIOS. This is quite similar to today’s UEFI machines, but does present its problems.

The partition table format used is GPT. Again, this is something normal and expected on today’s new UEFI machines, but something people may be unfamiliar with.

Somewhat specific to Mac, if one installs Windows, then the resulting partition table format includes both MBR and GPT. This is a result of the fact that 32-bit Windows don’t boot from GPT and don’t like EFI, and even more so, a result of the support for Windows on Macs being produced before -Longhorn- Vista was released. So the lowest common denominator is, in this case, 32-bit Windows XP; so, “BootCamp” produces a GPT+MBR hybrid partition table. (Experts seem to dislike hybrid MBRs.)

Under (U)EFI, the equivalent of BIOS does very little. It has support for FAT (usually FAT32), for loading additional drivers from the FAT partition, and for running .EFI binaries (mostly boot loaders, although in some cases these may be utility programs or shells).

On PCs, we will be mostly interested in 32-bit and 64-bit Intel .EFI binaries (i386 and x86_64 binaries). There are even universal binaries. Running an appropriate .EFI binary is how we boot an operating system. Since 3.3, Linux kernels seem to include an EFI stub; this means you can take a “vmlinux” binary and feed it directly to the EFI subsystem of an (U)EFI machine, without use of ELILO, GRUB2, or some other boot loader.

To expose the binary to the EFI system of the machine, you need to mount the FAT-formatted partition of type “EFI System Partition”, change to the \EFI directory on this partition, create a “vendor directory” (that’s the name of your OS or your OS vendor), and put “boot.efi” binary in that directory. Some EFI systems may expose other binaries as well, but this is what one is supposed to do.

Alternatively, systems may include a “Compatibility Support Module”. Macs do. This is a BIOS emulation layer and allows booting “legacy” operating systems. Microsoft says this must be turned off by default. Note that it is not a requirement; there is no need to include CSM. (VirtualBox does not.)

Please read around to familiarize yourself with EFI and GPT, and how they differ from BIOS and MBR. Trying to describe them here would be either a lot of work or would result in even more incorrect article than it’s already bound to be.

Easy parts and troublesome parts

VirtualBox supports physical partitions in the VMDK format. You need to use the command line to create the VMDK disk image, but it’s easily doable. It’s essentially a one-liner under normal, tested conditions.

Even when the conditions are not as normal, I’ve successfully ran BootCamp’d Windows XP previously. (Search my blog if you’re interested in hacks I had to do.)

That was easy: Windows XP deals with MBR, and MBR is trivial.

The problematic part with supporting Ubuntu is that its boot loader, GRUB2, actually appears to be too smart for its own good. Having installed the 32-bit version of Ubuntu 12.04, it has (by design) not deployed an EFI boot loader. Instead of deploying grub-efi, it has deployed grub-pc. This is great; the rEFIt boot loader that I use when booting the physical machine has picked up on this and allowed me to pick the Linux system. It does, unfortunately, depend on turning off the “bootable” flag in MBR with fdisk. This appears to be a limitation (or a feature?) of the Apple-provided CSM.

That’s all great. But when I tried to load this system with VirtualBox using default options, I got nothing. Zip. Nada. A black screen and that’s it.

Why? I have no idea. But I presume that GRUB2 picked up on GPT partition table and then got very confused by this on a non-EFI system. Why? Again, no idea. Switching to EFI got the setup to work — but only after installing grub-efi and deploying it to the appropriate place.

Creating virtual disk

You want to have a virtual disk that includes all relevant GPT information.

As I was originally playing with my previous MBR-based method, and the EFI+GPT method came from that, so does my script include some dding of GPT data: the first 40 sectors of the disk, and the last 40 sectors of the disk.

IMPORTANT: Always check device names (GPT partition IDs) with sudo diskutil list /dev/disk0. Always check MBR partition IDs with sudo frisk /dev/disk0. My setup includes Windows 8 and is very weird.

IMPORTANT: This script is NOT intended to be run as-is! Read it to learn what’s going on, triple check every single number, ensure you understand every single line, customize it for your machine and only then think twice before running anything below. Playing with partitions, with disk devices etc is dangerous. Ensure you have backups of everything that’s even remotely significant.

I personally have a Time Machine backup of my important data under OS X, every important project is stored on online code hosting, and everything else under physically installed Ubuntu and Windows 8 is not important. What’s your situation? Can you afford to lose data?

NOTE NOTE NOTE: As of OS X 10.9 Mavericks, Detection of disk size in blocks is broken in the script below. I haven’t updated it; when I needed to run it, I read it from a manually-ran fdisk and put it in appropriate place. (n.b. this could possibly be doable from native code: ioctl(fd, DKIOCGETPHYSICALBLOCKSIZE, &block_size); see http://stackoverflow.com/a/15947809/39974)

# ALWAYS check devices with:
# sudo diskutil list /dev/disk0

# ALWAYS check MBR partition IDs with:
# sudo fdisk /dev/disk0

EFISYSTEMPARTITIONOSXDEVICEID=1
RECOVERYPARTITIONOSXDEVICEID=3
LINUXPARTITIONOSXDEVICEID=4
LINUXPARTITIONMBRDEVICEID=3

sudo chmod 777 /dev/disk0s$EFISYSTEMPARTITIONSOXDEVICEID
sudo chmod 777 /dev/disk0s$RECOVERYPARTITIONSOXDEVICEID
sudo chmod 777 /dev/disk0s$LINUXPARTITIONSOXDEVICEID

sudo VBoxManage internalcommands createrawvmdk -filename PhysicalDisk.vmdk -rawdisk /dev/disk0 -partitions $EFISYSTEMPARTITIONOSXDEVICEID,$RECOVERYPARTITIONOSXDEVICEID,$LINUXPARTITIONOSXDEVICEID


# (primary mbr=1, primary gpt header=1, primary gpt table = 32, extra = 6) * 512
PRIMARY=40
sudo dd bs=512 count=$PRIMARY if=/dev/disk0 of=PhysicalDisk-pt.vmdk

# secondary gpt table=32 + secondary gpt table = 1
# see http://7bits.nl/blog/2012/01/02/mac-gpt-partition-table-recovery
SECONDARY=33
DISKSIZE=`diskutil info disk0 | grep "Total Size: .*exactly .* 512-Byte-Blocks"|sed 's/.*Total Size:.*exactly \(.*\) 512-Byte-Blocks)/\1/
'`
OFFSET=`calc $DISKSIZE-$SECONDARY`
OFFSET=`echo $OFFSET|sed 's/^ *//'` # ltrim

sudo dd bs=512 count=$SECONDARY iseek=$OFFSET oseek=$PRIMARY if=/dev/disk0 of=PhysicalDisk-pt.vmdk conv=notrunc

First, note the chmods. These will be required after each reboot. OS X only allows root to access the disks (for very important security reasons); VirtualBox does not run with root privileges. I don’t do this lightly; be VERY mindful that this is actually creating a local security hole, allowing user processes to read and even write to the disk.

Next, if you take a look at PhysicalDisk.vmdk, it’s a text file. You can see how various virtual disk sectors are mapped to various physical disk sectors, to “zero”, or to PhysicalDisk-pt.vmdk. (Please do check that you can find a section that matches this; if not, something went wrong in VBoxManage, and you should delete both .vmdk files.)

dd might not be necessary; but I ensure that whatever’s in GPT is not “accidentally” changed by VirtualBox’s VBoxManage.

Could one map even these 40 initial and 33 trailing sectors to the actual physical disk? Sure. But, why risk anything?

Installing rEFIt

Download rEFIt from its homepage. The install instructions say all you need to do with latest version, 0.14, is open the installer package and hit “Next” repeatedly.

Installing grub-efi

I decided to reuse EFI System Partition. I could have just as easily used the system partition; Apple ships a HFS+ driver, so the EFI subsystem can boot directly from the system partition.

The thing is, Ubuntu can’t write to the HFS partition, so it’s slightly easier to reuse the EFI System Partition.

What am I risking? Well, Apple might wipe this partition clean in an OS update. I hope they won’t.

IMPORTANT: The following can mess up GRUB. I can still boot using the “BIOS” GRUB2, but your mileage may vary.

What follows is inspired by Rod Smith’s EFI-Booting Ubuntu on a Mac.

  1. Boot physical Ubuntu.
  2. sudo apt-get install grub-efi – This removed grub-pc on my machine, although I still seem to have the ability to boot using BIOS. (Anything else would be… troublesome.)
  3. sudo mkdir /boot/efi – This is the place where we’ll mount the EFI System Partition.
  4. sudo mount /dev/sda1 /boot/efi
  5. sudo mkdir -p /boot/efi/EFI/Ubuntu – Apple doesn’t ship an \EFI folder. We’ll create it, along with the “vendor” directory for Ubuntu.
  6. sudo grub-install /dev/sda1 – This should install grub-efi to \EFI\Ubuntu.
  7. ls -al /boot/efi/EFI/Ubuntu – You should see two files from Ubuntu here.

It’s important to understand: 32-bit Ubuntu installs 32-bit GRUB2. This will not be bootable on a 64-bit capable Mac. This is solely useful for VirtualBox.

So, ensure that you can still use the BIOS GRUB2, or have an alternative boot method, or else you’re now converting your physical installation into a VirtualBox-only installation!

Creating virtual machine

I don’t have a script for this one. Go back to OS X, go to VirtualBox GUI and create an Ubuntu-type virtual machine. Don’t pick the 64-bit version; this changes the type of EFI that the virtual machine will use!

Pick the previously created PhysicalDisk.vmdk while creating the machine.

Now edit the settings. Right click on machine name, pick “Settings”, and change the machine to be an EFI machine on the System tab. So: right click [machine name]->Settings->System->Motherboard->Enable EFI (special OSes only).

Don’t boot yet! Did you chmod the disk devices? Remember, you rebooted. Please sudo chmod 777 all partition devices in /dev (and be mindful that this is a security hole you’re creating, which you might somehow avoid with UNIX user groups, but meh).

After this point, do not recreate the PhysicalDisk.vmdk without keeping in mind that this file includes disk image IDs in several places. VirtualBox keeps track of the disk images, and will NOT be happy if the ID changes.

So, done now? Great. Boot.

You’ll be shown the EFI shell. Hoorah!

Now, let’s change to the EFI System Partition‘s filesystem and boot GRUB2.

fs0:
cd EFI\Ubuntu
boot.efi

This should show you your physical machine’s GRUB menu and the booting should move on. Observe the disk light on the bottom of VirtualBox’s window; if it stops flickering for longer than 15 seconds, and Ubuntu does not boot, you can presume you have some sort of an issue.

Note that virtual machine does have different hardware than your physical machine; for example, NVIDIA graphics driver does not work for me. I get the console, but not X11. It would be trivial to fix (replace the selected driver with vesa or something similar in Xorg.conf) but I don’t care: I need to SSH into the machine and tunnel X11 to XQuartz on OS X. I don’t need Unity: I need the ability to work on my code and display the X windows.

So, this works for me. Huzzah!


Small updates

fstab

Add this to /etc/fstab (based on Rod Smith’s post, too):

/dev/sda1       /boot/efi       vfat    ro,fmask=133    0       0

Alternatively, change that ro to rw to get the partition to mount read-write; this may be important for grub updates.

grub-efi-amd64

Ubuntu 12.04 also ships with grub-efi-amd64.

sudo mount /boot/efi -o remount,rw # if not already mounted read-write
sudo apt-get install grub-efi-amd64

Don’t forget to change machine type to “Ubuntu (64-bit)” to update the EFI type.

Note, grub-efi-amd64 conflicts with grub-efi-ia32 and grub-efi, so you’ll end up losing the 32-bit version of the boot loader. This may or may not conflict with ability to boot from BIOS/CSM – I didn’t test this yet.

Routing IPv6 traffic through Debian pptpd into Hurricane Electric’s IPv6 tunnel

This is a repost of an answer I made to my own question on SuperUser (the “non-programmer” Stack Overflow) regarding setting up pptpd under Debian to route IPv6.

In the post, I’m also looking into using this under Mac OS X 10.8 Mountain Lion. I fully understand that PPTP is an insecure protocol and have separately also set up OpenVPN. However, I’m looking at this because PPTP is much more ubiquitous than OpenVPN and it’s easier to set up at both server and client side; no playing with certificate authorities, no playing with distributing configuration files to clients, etc. (Yes, I’m highly annoyed at the OpenVPN client for iOS not supporting the static key setup. Yes, I understand static key is less secure. No, I’m not dealing with stuff that require total and complete anonymity or encryption; I just want a VPN to work.)

This post does not deal with routing the segment through OS X once you got it to OS X.

This post only minimally deals with Windows as a client, because it Just Works™, and does not deal with GNU/Linux as a client, because it didn’t “magically” work under Ubuntu when I tried it, and I am not interested enough to figure out why.

Main goal here is documenting what an OS X user who has access to a Debian server with a public IP needs to do in order to get his OS X machine onto public IPv6 Internet without exposing it to public IPv4 Internet.

Client OS

Mac OS X does not particularly like IPv6 over PPP. Use the following after the connection has been set up:

sudo ipconfig set ppp0 AUTOMATIC-V6
sudo route add -inet6 default -interface ppp0

The prior seems to make OS X adhere to router advertisements; the latter adds a default route for IPv6. (Now, if only the certain-fruity-mobile-operating-system version of route provided -inet6, I’d be a happy wooden boy.)

Also take note that OS X will ignore whatever address was supposed to be negotiated over IPv6 and set up only a local address. This may interfere with routing towards OS X.

On the other hand, Windows 8 (of all systems!) has happily picked up the address sent over PPP, took note of the router advertisement, and overall configured itself flawlessly. PPTP really works nice in Windows.

Server

First thing I missed was that Hurricane Electric’s tunnel broker actually assigns TWO /64 prefixes; one is supposed to be solely for client use, while the other is intended for routing additional clients (such as the PPTP client). And if you need more addresses (or prefixes!), you can even get a /48 prefix. (With IPv6, this means there’s more bits for ‘your’ use; HE’s prefix takes ‘only’ 48 bits. So that provides you a few more bits to control before the auto-generated suffix, created from a MAC address or even created randomly, kicks in and takes over last 64 bits. You could theoretically wiggle and subnet even with only 64-bits to spare, but I’ve seen strange behavior on either Windows 8 or OS X, so I wouldn’t rely too much on that.)

Instead of configuring radvd directly and running it as a server — simply don’t configure it globally. That is, don’t run it as a service on Debian.

Instead, let’s follow Konrad Rosenbaum’s example, at Silmor.de, and have radvd configured after pppd creates the PPP interface.

  1. Set up your IPv6 connectivity. I use Hurricane Electric; I’ve configured it as follows:
    # hurricane electric tunnel
    # based on: http://www.tunnelbroker.net/forums/index.php?topic=1642.0
    auto he-ipv6
    iface he-ipv6 inet6 v4tunnel
        address 2001:470:UUUU:VVVV::2
        netmask 64
        endpoint  216.66.86.114
        ttl 255
        gateway 2001:470:UUUU:VVVV::1
        ## from http://lightyearsoftware.com/2011/02/configure-debian-as-an-ipv6-router/
        # I did not set up the routing of the /64 nor the /48 prefix here, but
        # this would ordinarily do it.  
        #up ip link set mtu 1280 dev he-ipv6
        #up route -6 add 2001:470:WWWW:VVVV::/64 he-ipv6
    
        # Note that Hurricane Electric provides different /64 IPv6 prefixes
        # for the client (UUUU:VVVV) and routing (WWWW:VVVV). 
        # And the /48 prefix is very different altogether.
    
  2. Install pptpd. (Of course, take note of PPTP’s insecurity as a protocol, and consider using OpenVPN or some other alternative.)

  3. Edit /etc/ppp/pptpd-options
    name pptpd
    refuse-pap
    refuse-chap
    refuse-mschap
    require-mschap-v2
    require-mppe-128
    proxyarp
    nodefaultroute
    lock
    nobsdcomp
    ipv6 ::1,::2
    

    Note the last line is different from the text in my question. You’re assigning some static addresses which may be respected by your client OS or not. (OS X seems to ignore them, but Windows uses them.)

  4. Create users for PPTP. Second column filters based on name argument in pptpd-options. Edit /etc/ppp/chap-secrets:
    ivucica pptpd AHyperSecretPasswordInPlainText 10.0.101.2 10.0.101.3 10.0.101.4
    

    You’re supposed to be able to replace the addresses with * instead of listing them manually. I did not try that out.

  5. Assign your PPTP users some IPv6 prefixes. NOTE: this is solely used by the script I’ll list below, which is derived from Konrad’s script.

    Edit /etc/ppp/ipv6-addr:

    ivucica:1234
    littlejohnny:1235
    
  6. Add new file /etc/ipv6-up.d/setupradvd:
    #!/bin/bash
    ADDR=$(grep ^$PEERNAME: /etc/ppp/ipv6-addr |cut -f 2 -d :)
    if test x$ADDR == x ; then
     echo "No IPv6 address found for user $PEERNAME"
     exit 0
    fi
    
    # We'll assign the user a /64 prefix.
    # I'm using a Hurricane Electric-assigned /48 prefix.
    
    # Operating systems seem to expect to be able to assign the 
    # last 64 bits of the address (based on ethernet MAC address
    # or some other identifier). So try to obtain a /48 prefix.
    
    # If you only have a /64 bit prefix, you can try to assign a
    # /80 prefix to your remote users. It works, but I'm only now
    # trying to enable these users to have routing.
    
    USERPREFIX=2001:470:XXXX:$ADDR
    USERPREFIXSIZE=64
    USERPREFIXOURADDRESS=1
    USERPREFIXUSERADDRESS=2
    
    # Add the address for your side of the tunnel to the PPP device.
    ifconfig $IFNAME add $USERPREFIX::$USERPREFIXOURADDRESS/$USERPREFIXSIZE
    
    # establish new route
    # (when a packet is directed toward user subnet, send it to user ip)
    route -6 add $USERPREFIX::/$USERPREFIXSIZE gw $USERPREFIX::$USERPREFIXUSERADDRESS
    
    #generate radvd config
    RAP=/etc/ppp/ipv6-radvd/$IFNAME
    RA=$RAP.conf
    echo interface $IFNAME >$RA
    echo '{ AdvSendAdvert on; MinRtrAdvInterval 5; MaxRtrAdvInterval 100;' >>$RA
    echo ' prefix' $USERPREFIX::/$USERPREFIXSIZE '{};' >>$RA
    
    # Instead of your DNS...
    #echo ' RDNSS $USERPREFIX::$USERPREFIXOURADDRESS {}; };' >>$RA
    # ...try assigning the Google DNS :)
    echo ' RDNSS 2001:4860:4860::8888 {}; }; ' >> $RA
    
    # The creation of radvd configuration could be more readable, but whatever.
    
    # Start radvd
    /usr/sbin/radvd -C $RA -p $RAP.pid
    
    exit 0
    

    Don’t forget to chmod the script to make it executable by pppd:

    chmod 755 /etc/ipv6-up.d/setupradvd
    
  7. The script spews radvd configuration into /etc/ppp/ipv6-radvd/… ensure that the folder exists!
    mkdir /etc/ppp/ipv6-radvd
    
  8. Also add /etc/ppp/ipv6-down.d/setupradvd (and make it executable!) — taken verbatim from Konrad:
    #!/bin/bash
    RAP=/etc/ppp/ipv6-radvd/$IFNAME
    kill `cat $RAP.pid` || true
    rm -f $RAP.*
    

    And

    chmod 755 /etc/ppp/ipv6-down.d/setupradvd
    

I have not tested using DHCPv6 to distribute the routing information, addresses or DNS information, especially since rtadv should be fulfilling these roles. It also would not help me, because as of Mountain Lion, OS X still does not ship with a DHCPv6 client (perhaps intentionally; nine out of ten dentists most of IPv6 experts agree that DHCP is evil).

Once again, please note Michael’s comments on PPTP security; consider using OpenVPN in production.

Yes, Konrad Rosenbaum also has a nice tutorial on IPv6 over OpenVPN. :-)

Core Data: Migrating ignores manual mapping model (or fails migration) despite mapping model’s existence

Let’s say you created a somewhat complex migration model. Among other things, let’s say it includes entity migration policies (you know — subclasses of NSEntityMigrationPolicy).

However, Core Data ignores your manual migration model. Why, oh why?

You can try looking into this by clicking on schema name in Xcode 4, picking the “Run” sidebar ‘tab’, picking the “Arguments” tab, and adding -com.apple.CoreData.MigrationDebug 1. (See tech note TN2124.)

Alright, so now you see what the source persistent store’s version hashes are, and what the expected destination store’s version hashes should be. Then you see how Core Data starts migration by telling you its conclusion about what the hashes are (for the second time). Finally, it starts iterating over your manual mapping models (the .xcmappingmodel bundles).

And then you see that it finds your mapping model, picks up on it, then decides the hashes are wrong and ignores it!

“What the…?” you wonder. You compare hashes, and they are listed in different order, but essentially the same.

I can only conclude this is a bug in Core Data (or in the entity editor in Xcode4).

Luckily it’s easy to remedy! Go to the mapping model, pick another source and destination model version, then restore to the correct source and destination model versions. Definitely do make a git commit prior to making this change so you can compare what happened.

Alternatively, an answer on StackOverflow has a different solution which can be applied in case you know what is the version of the original persistent store. It involves manually setting version hashes on the NSEntityMappings inside the NSMappingModel.

Patching an unrecognized selector into a misbehaving OS X app

Let’s say you have an app that misbehaves. As in, it raises an exception mentioning an unrecognized selector.

Of course, it’s third party and closed-source.

But you are an enterprising young developer and you really want to patch this app.

I will not identify the app that I patched, to avoid any impression that I’m cracking the app. (It’s a popular app and I’ve run into pretty nice anti piracy protection blocking gdb on multiple layers. Since I’m not familiar with cracking and was not even attempting that, I’ve decided that it’s best to simply avoid mentioning the app name.)

Makefile

all: misbehavingappfixer.dylib

misbehavingappfixer.dylib: misbehavingappfixer.o
        gcc \
                -dynamiclib \
                -undefined suppress -flat_namespace \
                misbehavingappfixer.o \
                -framework Cocoa -o misbehavingappfixer.dylib
run:
        DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=`pwd`/misbehavingappfixer.dylib /Applications/Misbehaving\ App.app/Contents/MacOS/Misbehaving\ App

misbehavingappfixer.c

#include <objc/runtime.h>
#include <CoreFoundation/CoreFoundation.h>
static CFStringRef nameImp(id self, SEL _cmd)
{
        return CFSTR("IVFlawedClass");
}

void inject_init(void) __attribute__((constructor));
void inject_init(void)
{
        Class _IVFlawedClass = objc_getClass("IVFlawedClass");
        if(!_IVFlawedClass)
        {
                printf("Could not find IVFlawedClass");
                return;
        }

        SEL nameSel = sel_registerName("name");
        class_addMethod(_IVFlawedClass, nameSel, (IMP)nameImp, "@@:");
}

I avoided using Objective-C because I had some issues when I tried going that route. It should work, but I didn’t want to spend any more time on this than I already have.

Now, to use this:

make
make run

This will compile the fix dylib, and then launch the Misbehaving App while first preloading our fix dylib.

For more information, see these:

Note: from comments on Mike Ash’s post, it seems that since I’m not overwriting symbols, I don’t need to (and, more importantly, *should* not) be using flat namespace. That means, I should not be using DYLD_FORCE_FLAT_NAMESPACE and -flat_namespace.

But, it works for me, so what the hell. :)

Symbolicating Mac app’s crash reports — manually

You will need your released app’s dSYM bundle. Since you are a good developer, whenever you release a version of your software, you use Xcode’s built in Archive functionality, so you don’t have a problem with that. (Right?)

So open Xcode’s Organizer. Right click on your archive, and pick “Show in Finder.” Then right click on the archive, and choose “Show Package Contents”.

Enter the “dSYMs” subfolder, and find the sole .dSYM bundle in there. Right click on it, and “Show Package Contents”. Open Contents, Resources, DWARF — and you’ll reach the file that actually contains the debug symbols. For example, “GeorgesGreatApp”, without any extension. Wheeee!

Now, open Terminal. Punch in atos -o and drag and drop the “GeorgesGreatApp” file (the one that contains the debug symbols) into the terminal. Then punch in -arch x86_64 (or whatever architecture the crash occurred on). Finally, paste the address that you want to know more about.

For example, given the following line in the crash log:

1   net.vucica.GeorgesGreatApp    	0x0000000100001e91 0x100000000 + 7825

you’d take the first hex number that appears on that line, and have this on the command line:

atos -o /Users/YOURUSERNAME/Library/Developer/Xcode/Archives/2012-02-29/GeorgesGreatApp\ 29.02.2012.\ 00.40.xcarchive/dSYMs/GeorgesGreatApp.app.dSYM/Contents/Resources/DWARF/GeorgesGreatApp -arch x86_64 0x0000000100001e91

and you’d get this output:

-[GGAppDelegate generateThumbnailsForJpeg:] (in GeorgesGreatApp) (GGAppDelegate.m:326)

Extremely handy when Xcode screws up symbolication or outright refuses to import the crash log. Maybe it’s Xcode’s fault. Maybe Spotlight did not correctly index dSYM’s location. Maybe Spotlight did not correctly index .app’s archived location. Whatever it is, I don’t really care — if symbolication is broken, it’s broken. But at least I can use the underlying mechanism in atos.