Is Windows Safari using Objective-C 2.0?

If you look at the Safari installation directory on Windows you probably noticed the objc.dll file. Are you curious if Windows Safari is using Objective-C version 1.0 or 2.0? I was, and this is my little adventure on trying to find that out. The short answer is NO. Safari on Windows still uses Objective-C 1.0. In this article I’m going to build the Objective-C DLL and show you why I don’t think Safari for Windows is using version 2.0.

files

The Objective-C Runtime Environment

In my last article I showed you how to build a bare bones Objective-C program in Windows with Cygwin. In that case, the Objective-C runtime used was installed with Cygwin. You can execute the following command to take a peek at the Objective-C header and library files that Cygwin uses.

find /lib/gcc | grep objc

This is not the same Objective-C runtime that Apple uses when compiling Safari for Windows. In fact there are three Objective-C runtime environments that I know of:

  1. Cygwin
  2. Apple
  3. GnuStep

We used Cygwin in the last article and now we’re going to build Apple’s implementation on Windows. I may discuss GnuStep at a later date.

Let’s Start Building Something

The first thing we need to do is get the latest source.
http://www.opensource.apple.com/tarballs/objc4/objc4-437.tar.gz

Once you unpack the tarball it’s time to find out how to build this sucker. You won’t find a Makefile but you will find objc.vcproj. This is a Microsoft Visual Studio project file.

If you don’t have Visual Studio installed you can download the free version here. Why Microsoft insists on on continuing to charge for an IDE is beyond me. Must be a cultural thing. What you want is Visual C++ Expression Edition 2008. It may or may not work with the new 2010 Beta; I have no idea. I ran 2008 so I’m telling you to do the same.

Once you have Visual Studio installed, open up the project file and choose build –> build solution. You’re going to get some errors about AvailablityMacros.h and TargetConditionals.h not being found.

Did you really think it was going to be that easy?

Error #1 TargetConditionals.h

If you have XCode installed on a Mac you can easily find TargetConditionals.h. However, that file doesn’t support the Microsoft compiler. This is evident by the comment at the top of the file. It’s good to read the source no?

/*
     File:       TargetConditionals.h
 
     Contains:   Autoconfiguration of TARGET_ conditionals for Mac OS X and iPhone
 
                 Note:  TargetConditionals.h in 3.4 Universal Interfaces works
                        with all compilers.  This header only recognizes compilers
                        known to run on Mac OS X.
 
*/
 
#ifndef __TARGETCONDITIONALS__
#define __TARGETCONDITIONALS__

So with a little Googly magic you’ll find the TargetConditionals.h that you’re looking for here under Universal Interfaces 3.4.2. Of course, a *.img.bin file isn’t much help on a Windows operating system so you’re going to have to extract the files on a Mac and then copy them to the Windows Machine.

Oh, and the files are going to have those classic Mac line breaks instead of Unix line breaks so you’re going to have to convert them.

for file in `ls *.h`
do
  tr '\r' '\n' < $file  > tempfile.txt
  mv tempfile.txt $file
done

Put those UniversalHeaders files you worked so hard to get somewhere logical, and include the header files in your project search path. What’s that? You can’t figure out where to do that? Confusing Microsoft UI got you down?

Tools –> Options –> VC++ Directories

Then set the “Show directories for” combo box to “Include files”.

includeFiles

If you do another build then you’ll notice there are no complaints about TargetConditionals.h but Visual Studio still can’t find AvailabilityMacros.h.

Error #2 AvailabilityMacros.h

To get rid of the AvailabilityMacros.h error grab all the header files in the latest SDK you installed on your Mac. We are trying to compile the version of Objective-C that is compiled on Snow Leopard so you should probably get the 10.6 *.h files.

Create another directory for some extra includes. I called mine
/cygdrive/c/Dev/usr/include/VisualStudio, but you can call it what you like. Now copy these three files to that new directory, and add the directory to the list of include directories in Visual Studio.

  • Availability.h
  • AvailabilityInternal.h
  • AvailabilityMacros.h

Error #3 stdint.h

Stdint.h is used for making C code more portable and you need a copy.

Download a copy and put it in that new include directory you created.
http://msinttypes.googlecode.com/svn/trunk/stdint.h

Error #4 _unmap_image_nolock

Next you will see a link error about some functions in the file objc-runtime-old.m. There is some information about this issue here:
http://lists.apple.com/…/msg00075.html
http://lists.apple.com/…/msg00076.html

Basically you need to comment out three functions so that it will compile. The explanation on the mailing list is that the source is out of sync which means nobody at Apple has probably tried to build objc on Windows for a while.

  • unmap_image
  • map_images
  • load_images

Add #if !TARGET_OS_WIN32 … #endif around those three functions.

#if !TARGET_OS_WIN32
/***********************************************************************
* unmap_image
* Process the given image which is about to be unmapped by dyld.
* mh is mach_header instead of headerType because that's what 
*   dyld_priv.h says even for 64-bit.
**********************************************************************/
__private_extern__ void 
unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
{
    recursive_mutex_lock(&loadMethodLock);
    unmap_image_nolock(mh, vmaddr_slide);
    recursive_mutex_unlock(&loadMethodLock);
}
 
 
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
**********************************************************************/
__private_extern__ const char *
map_images(enum dyld_image_states state, uint32_t infoCount,
           const struct dyld_image_info infoList[])
{
    const char *err;
 
    recursive_mutex_lock(&loadMethodLock);
    err = map_images_nolock(state, infoCount, infoList);
    recursive_mutex_unlock(&loadMethodLock);
 
    return err;
}
 
 
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: acquires classLock and loadMethodLock
**********************************************************************/
__private_extern__ const char *
load_images(enum dyld_image_states state, uint32_t infoCount,
           const struct dyld_image_info infoList[])
{
    BOOL found;
 
    recursive_mutex_lock(&loadMethodLock);
 
    // Discover +load methods
    found = load_images_nolock(state, infoCount, infoList);
 
    // Call +load methods (without classLock - re-entrant)
    if (found) {
        call_load_methods();
    }
 
    recursive_mutex_unlock(&loadMethodLock);
 
    return NULL;
}
#endif

Error #5 SRCROOT & DSTROOT

We’ve almost got this DLL built, we just need to do one last thing. Several places in the project properties the variables $(SRCROOT) and $(DSTROOT) are referenced.

srcRoot

Created a batch file to define those variables right before opening up the Visual Studio Project file.

set DSTROOT=c:\Dev\builds\ObjC\
set SRCROOT=c:\Dev\sourceRoot\ObjC\
C:\cygwin\bin\cygstart.exe objc.vcproj

Now you should be able to successfully build the objc.dll library. It can be found under $DSTROOT\AppleInternal\bin\objc.dll.

Attempt to Build Objective-C 2.0

So far you’ve built Objective-C 1.0. Now let’s try to build version 2.0. If you look at the source code you see __OBJC2__ littered all over the kode. To build version 2.0 you need to define it.

Add /D “__OBJC2__” to the C/C++ command line arguments in the project properties and do a build. You will get lots of errors.

If you’re building on 32-bit windows like myself you’ll get an error about an unknown preprocessor command in objc-runtime-new.m. Apparently the Microsoft C++ compiler does not recognize the #warning directive, must be a cultural thing.

430
431
432
433
434
435
436
#if defined(__x86_64__)
    uint16_t *p = (uint16_t *)(dst + vtable_prototype_index_offset + 3);
    if (*p != 0x7fff) _objc_fatal("vtable_prototype busted");
    *p = index * 8;
#else
#   warning unknown architecture
#endif

I didn’t try too hard to build Objective-C 2.0 because I bumped into the following nugget when perusing objc-confg.h.

28
29
30
#if TARGET_OS_EMBEDDED  ||  TARGET_OS_WIN32
#   define NO_GC 1
#endif

Conclusion

If Objective-C 2.0 can be built on 32-bit Windows I don’t know how to do it. Garbage collection is one of the staple features of Objective-C 2.0 (properties and fast enumeration are just syntactic sugar) and if it’s not supported, is there really any point of building it? Therefore we can firmly conclude that Safari for Windows is not using Objective-C 2.0, at least the 32-bit version anyway.

Keep on Koding on!

References
A Quick Objective-C 2.0 Tutorial

Comments are closed.