Available Languages: | Deutsch | English | Français | 日本語 (Nihongo) | 中文 (简) (Simplified Chinese) |

Porting Unix software to Darwin and Mac OS X

This document contains hints for porting Unix applications to Darwin and Mac OS X. Much of the information here applies to both Mac OS X version 10.x.x and "pure" Darwin systems. Both systems will be referred to as Darwin, since Mac OS X is actually a superset of Darwin.


1 Basics

1.1 Where Darwin came from

Darwin is a Unix-like operating system that evolved from NeXTStep / OpenStep. Lore has it that it was initially forked off 4.4BSD Lite. The BSD heritage still shows, in fact Darwin was recently modernized with code from FreeBSD and NetBSD.

Darwin's kernel is based on a combination of Mach 3.0, BSD, and proprietary functionality like the object-oriented driver layer IOKit. Although Mach originally is a micro-kernel design, the BSD kernel that sits on top of it is monolithic and the two are now so intertwined that they must be regarded as a single monolithic kernel.

The user-space tools and libraries shipped with Darwin are mostly of the BSD persuation, as opposed to the GNU tools you get with Linux. Apple is not as strict as other BSDs though, and goes for useful compromises. For example, Apple ships both BSD make and GNU make, with GNU make installed as the default.

1.2 The Compiler and Tools

Short story: The compiler is a gcc derivate, but installed as cc; you may have to patch Makefiles. Most packages won't build shared libraries. If you get errors related to macros, use the -no-cpp-precomp option.

Long story: The compiler tool chain in the Mac OS X Developer Tools is a strange beast. The compiler is based on the gcc 2.95.2 suite, with modifications to support the Objective C language and some Darwin quirks. The preprocessor (cpp) is available in two versions. One is the standard precompiler (from gcc 2.95.2), the other one is a special precompiler written by Apple, with support for precompiled headers. The latter one is used by default, because it is faster. However, some code doesn't compile with Apple's precompiler, so you must use the -no-cpp-precomp option to get the standard precompiler. (Note: I previously recommended the -traditional-cpp option. The semantics of this option have changed slightly with GCC 3, breaking most packages that use it. -no-cpp-precomp has the desired effect on both the current Developer Tools and future compilers based on GCC 3.)

The assembler says it's based on gas 1.38. The linker is not based on GNU tools. This is a problem when building shared libraries, as GNU libtool and configure scripts generated by it don't know how to handle Apple's linker.

1.3 Host type

Short story: If configure fails with 'Can't determine host type', copy config.guess and config.sub from /usr/share/libtool (/usr/libexec for OS versions prior to 10.2) into the current directory.

Long story: The GNU world uses a canonical format to specify system types. It has three parts: cpu type, manufacturer and operating system. Sometimes a fourth part is added - then the third part denotes the kernel, while the fourth denotes the operating system. All parts are lower case and concatenated using dashes. Some examples: i586-pc-linux-gnu, hppa1.1-hp-hpux10.20, sparc-sun-solaris2.6. The host type for Mac OS X 10.0 is powerpc-apple-darwin1.3. Versions of Mac OS X 10.2 bring various powerpc-apple-darwin6.x.0 and 10.3 gives powerpc-apple-darwin7.x.0, where "x" depends on the exact OS version.

Many packages that use autoconf want to know the host type of the system they are compiled on. (Side note: to support cross-compiling and porting, there are actually three types - the host type, the build type and the target type. Usually, they're all the same.) You can either pass the host type to the configure script as a parameter or you can let it guess.

The configure script uses two companion scripts to determine host types. config.guess tries to guess the host type, config.sub is used to validate and canonicalize the host type. These scripts are maintained as separate entities, but they are included in every package that uses them. Until very recently, these scripts didn't know about Darwin or Mac OS X. If you have a package that doesn't recognize Darwin, you must replace the config.guess and config.sub included in the package. Luckily, Apple put working versions in /usr/share/libtool (/usr/libexec for pre-10.2 OS), so you can just copy them from there.

If you are constructing a Fink package, you can use the UpdateConfigGuess and/or UpdateConfigGuessInDirs fields in your .info package description file to do this update automatically.

1.4 Libraries

Short story: You can safely remove -lm from Makefiles, but you don't need to.

Long story: Mac OS X doesn't have separate libc, libm, libcurses, libpthread etc. libraries. Instead, they're all part of the system library, libSystem. (In earlier versions, this actually was the System framework.) However, Apple placed appropriate symlinks in /usr/lib, so linking with -lm will work. The only exception is -lutil. On other systems, libutil contains functions related to pseudo-terminals and login accounting. These functions are in libSystem, but there is no symlink to provide a libutil.dylib.

1.5 Other sources of information

Another source of information for porting is the Wiki at MetaPkg Wiki.

You can also read Apple Technical Note TN2071: "Porting Command Line Unix Tools to Mac OS X".

2 Shared Code

2.1 Shared Libraries vs. Loadable Modules

One Mach-O feature that hits many people by surprise is the strict distinction between shared libraries and dynamically loadable modules. On ELF systems both are the same; any piece of shared code can be used as a library and for dynamic loading. Use otool -hv some_file to see the filetype of some_file.

Mach-O shared libraries have the file type MH_DYLIB and carry the extension .dylib. They can be linked against with the usual static linker flags, e.g. -lfoo for libfoo.dylib. However, they can not be loaded as a module. (Side note: Shared libraries can be loaded dynamically through an API. However, that API is different from the API for bundles and the semantics make it useless for an dlopen() emulation. Most notably, shared libraries can not be unloaded.)

Loadable modules are called "bundles" in Mach-O speak. They have the file type MH_BUNDLE. Since no component involved cares about it, they can carry any extension. The extension .bundle is recommended by Apple, but most ported software uses .so for the sake of compatibility. Bundles can be dynamically loaded and unloaded via dyld APIs, and there is a wrapper that emulates dlopen() on top of that API. It is not possible to link against bundles as if they were shared libraries. However, it is possible that a bundle is linked against real shared libraries; those will be loaded automatically when the bundle is loaded.

2.2 Version Numbering

On an ELF system, version numbers are usually appended to the file name of the shared library after the extension, e.g. libqt.so.2.3.0. On Darwin, the version numbers are placed between the library name and the extension, e.g. libqt.2.3.0.dylib. Note that this allows you to request a specific version of the library when linking, using -lqt.2.3.0 for the example above.

When creating a shared library, you can specify a name to be used when searching for the library at run time. This is usual practice and allows several major versions of a library to be installed at the same time. On ELF systems this is called the soname. What's different on Darwin is that you can (and should) specify a full path along with the file name. This eliminates the need for "rpath" options and the ldconfig/ld.so.cache system. To use a library that is not yet installed, you can set the DYLD_LIBRARY_PATH environment variable; see the dyld man page for details.

The Mach-O format also offers real minor version checking, unknown on ELF systems. Every Mach-O library carries two version numbers: a "current" version and a "compatibility" version. Both version numbers are written as three numbers separated by dots, e.g. 1.4.2. The first number must be non-zero. The second and third number can be omitted and default to zero. When no version is specified, it will default to 0.0.0, which is some kind of wildcard value.

The "current" version is for informational purposes only. The "compatibility" version is used for checking as follows. When an executable is linked, the version information from the library is copied into the executable. When that executable is run, the stored version information is checked against the version information in the library that is loaded. dyld generates a run-time error and terminates the program unless the loaded library version is equal to or newer than the one used during linking.

2.3 Compiler Flags

The generation of position-independent code (PIC) is the default on Darwin. Actually, PowerPC code is position-independent by design, so there is no performance or space penalty involved. So, you don't need to specify a PIC option when compiling code for a shared library or module. However, the linker doesn't allow "common" symbols in shared libraries, so you must use the -fno-common compiler option.

2.4 Building a Shared Library

To build a shared library, you invoke the compiler driver with the -dynamiclib option. This is best demonstrated by a comprehensive example. We'll build a library called libfoo, composed of the source files source.c and code.c. The version number is 2.4.5, where 2 is the major revision (incompatible API change), 4 is the minor revision (backwards-compatible API change) and 5 is the bugfix revision count (some people call this the "teeny" revision, it denotes fully compatible changes). The library depends on no other shared libraries and will be installed in /usr/local/lib.

cc -fno-common -c source.c
cc -fno-common -c code.c
cc -dynamiclib -install_name /usr/local/lib/libfoo.2.dylib \
 -compatibility_version 2.4 -current_version 2.4.5 \
 -o libfoo.2.4.5.dylib source.o code.o
rm -f libfoo.2.dylib libfoo.dylib
ln -s libfoo.2.4.5.dylib libfoo.2.dylib
ln -s libfoo.2.4.5.dylib libfoo.dylib

Note which parts of the version are used where. When linking against this library, one would normally use the -lfoo flag, which accesses the libfoo.dylib symlink. Regardless of which symlink or file is specified, though, it is the install_name that is written into one's program. That means one can delete the "higher" (less version-specific) symlink libfoo.dylib after compiling. During a minor-revision library upgrade, one just changes the target of the libfoo.2.dylib symlink that is used by the runtime dynamic linker.

2.5 Building a Module

To build a loadable module, you invoke the compiler driver with the -bundle option. If the module uses symbols from the host program, you'll have to specify -undefined suppress to allow undefined symbols, and -flat_namespace along with it to make the new linker in Mac OS X 10.1 happy. A comprehensive example:

cc -fno-common -c source.c
cc -fno-common -c code.c
cc -bundle -flat_namespace -undefined suppress \
 -o mymodule.so source.o code.o

Note that no version numbering is used. It is possible to use it in theory, but in practice it's pointless. Also note that there are no naming restrictions for bundles. Some packages prefer to prepend "lib" anyway because some other systems require it; this is harmless, since a program would use the full filename when loading a module.

3 GNU libtool

GNU packages that build libraries use GNU libtool to hide platform-dependent procedures for library building and installation.

3.1 The Situation

In the wild, one can find four strands of libtool:

In conclusion, libtool 1.3.x and packages that use it (which happens to be the majority of libtool-using packages out there) need a patch to build shared libraries on Darwin. Apple includes a patched version of libtool 1.3.5 in Mac OS X, but it will not work correctly in most cases. Christoph Pfisterer improved that patch to hardcode the correct path and to do full versioning. The changes were incorporated into upstream libtool releases and development versions starting with 1.4. Members of the Fink team continue to make improvements and forward them to the libtool maintainers. The versioning scheme is compatible across all libtool versions.

Side note: The libltdl library included with all libtool versions will only work on Darwin when dlcompat is installed. This is included with OS X starting with 10.3. For previous versions, one can install the fink "dlcompat" family of packages.

3.2 The 1.3.5 Patch

If you are building libtool 1.3.5 for yourself, you will need to apply this patch [updated 2002-06-09] to the libtool 1.3.5 source and then delete the files ltconfig and ltmain.sh. (They will be recreated from the appropriate .in files when you run configure and make.) This is done automatically, by the way, in the current Fink package for libtool 1.3.5.

But that's only half the work - every package using libtool comes with its own copies of ltconfig and ltmain.sh. So you must replace these in every package that you want to build as a shared library. Note that you must do this before running the configure script. For your convenience, you can get the two files right here: ltconfig (98K) and ltmain.sh (110K) [both updated 2002-06-09].

3.3 Fixing 1.4.x

There are at least three different versions of libtool 1.4.x now in wide use (1.4.1, 1.4.2, and later development snapshots). They all have some issues on Darwin, though the exact changes required to fix them differ. The "libtool14" package shipped via Fink has all required patches already applied to it. However, you still have to manually fix the ltmain.sh and configure files of affected packages in order to get them working.

  1. The flat_namespace bug: This problem only occurs if you use libtool on Mac OS X10.1 and later. What happens is that libtool tries to use the -undefined suppress to allow undefined symbols, but doesn't specify along with it the -flat_namespace option. Starting with 10.1 this won't work anymore. A typical patch looks like this:
    diff -Naur gdk-pixbuf-0.16.0.old/configure gdk-pixbuf-0.16.0.new/configure
    --- gdk-pixbuf-0.16.0.old/configure	Wed Jan 23 10:11:48 2002
    +++ gdk-pixbuf-0.16.0.new/configure	Thu Jan 31 03:19:54 2002
    @@ -3334,7 +3334,7 @@
       darwin* | rhapsody*)
    -    allow_undefined_flag='-undefined suppress'
    +    allow_undefined_flag='-flat_namespace -undefined suppress'
         # FIXME: Relying on posixy $() will cause problems for
         #        cross-compilation, but unfortunately the echo tests do not
         #        yet detect zsh echo's removal of \ escapes.
  2. The loadable module bug: This bug is caused by the non-standard behaviour of zsh (which is the default shell in 10.0 and 10.1; starting in 10.2 bash is the default). Zsh's non-standard quoting behaviours prevents loadable module from being built correctly, they end up as shared libraries instead (unlike Linux, these are reall different things on Darwin). A typical fix for this (cut off, so you can't use it unmodified):
    diff -Naur gnome-core- gnome-core-
    --- gnome-core-	Sun Jan 27 08:19:48 2002
    +++ gnome-core-	Fri Feb  8 01:10:21 2002
    @@ -4020,7 +4020,7 @@
         # FIXME: Relying on posixy $() will cause problems for
         #        cross-compilation, but unfortunately the echo tests do not
         #        yet detect zsh echo's removal of \ escapes.
    -    archive_cmds='$nonopt $(test "x$module" = xyes && echo -bundle || echo -dynamiclib) ...'
    +    archive_cmds='$nonopt $(test x$module = xyes && echo -bundle || echo -dynamiclib) ...'
         # We need to add '_' to the symbols in $export_symbols first
         #archive_expsym_cmds="$archive_cmds"' && strip -s $export_symbols'

    This problem is fixed in some post-1.4.2 versions of libtool.

  3. The convenience library bug: Under some conditions, libtool will fail to link convenience libraries, giving "multiple definitions" errors. This is caused by a more fundamental problem in libtool it seems. For now as a workaround (curing the symptoms not the actual problem, but with great success anyway), you can use this fix (thanks to Dave Vasilevsky):
    --- ltmain.sh.old       2002-04-27 00:01:23.000000000 -0400
    +++ ltmain.sh   2002-04-27 00:01:45.000000000 -0400
    @@ -2894,7 +2894,18 @@
            if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
              eval cmds=\"$archive_expsym_cmds\"
    +         save_deplibs="$deplibs"
    +         for conv in $convenience; do
    +       tmp_deplibs=
    +       for test_deplib in $deplibs; do
    +         if test "$test_deplib" != "$conv"; then
    +           tmp_deplibs="$tmp_deplibs $test_deplib"
    +         fi
    +       done
    +       deplibs="$tmp_deplibs"
    +         done
              eval cmds=\"$archive_cmds\"
    +         deplibs="$save_deplibs"
            save_ifs="$IFS"; IFS='~'
            for cmd in $cmds; do
  4. The DESTDIR bug: Certain packages which set DESTDIR and use libtool 1.4.2 have problems with relinking. The problems are discussed in these email messages:




    and a patch for the problem is discussed in:


3.4 Further Notes

For more information on libtool itself and what it does, see the libtool homepage.

Side note: Apple's Developer Tools contain a program also called libtool, which is used by the compiler driver to build shared libraries. However, this is completely unrelated with GNU libtool. The GNU libtool that Apple ships is installed as glibtool instead. This can be achieved by configuring GNU libtool with --program-transform-name=s/libtool/glibtool/.

4 Preparing for 10.2

4.1 The bash shell

Fink made the transition from OS X 10.0 to OS X 10.1 fairly easily, thanks in part to planning ahead for the changes that were coming. We will try to do the same for the next transition, but not many details are known yet.

We understand that OS X 10.2 will use bash rather than zsh to provide /bin/sh functionality. This has at least three implications for fink.

4.2 The gcc3 compiler

Mac OS X 10.2 uses the gcc3 compiler.

Some packages which have loadable modules and use libtool fail with an install_name error, because libtool passes the -install_name flag even along with the -bundle flag (when it is not strictly needed). This behavior was accepted by the gcc2 compiler but is not being accepted by the gcc3 compiler. The fix can be found here. Note that you do not need the patch if your package uses libtool-1.3.5 (for example, if you are using UpdateLibtool: True) since it has already been incorporated into a revised version of fink's ltconfig file (available in pre-release versions of fink).

Another issue with the gcc3 compiler is an incompatibility for C++ ABIs between gcc2 and gcc3. In practice, this means that C++ programs compiled with gcc3 cannot link to libraries compiled with gcc2.

5 Preparing for 10.3

5.1 Perl

In OS X 10.2, /usr/bin/perl was perl 5.6.0 and the architecture string was "darwin". In OS X 10.3, /usr/bin/perl was upgraded to perl 5.8.1 and the architecture string was changed to "darwin-thread-multi-2level". These changes probably do not affect ordinary uses of the perl executable for package creation since each perl executable knows where to find its own modules. Maintainers of perl-module ("-pm") packages who follow the current Perl Modules packaging policy and are careful to follow the CompileScript and InstallScript documentation will already have things set up correctly.

5.2 New symbol definitions

Starting in Mac OS X 10.3, there is now always a complete definition for the socklen_t type. To get this typedef defined, you may need to add to your program:

#include <sys/types.h>
#include <sys/socket.h>

5.3 New builtin system libraries

Mac OS X 10.3 includes several libraries that were not in previous system releases, and so were provided as fink packages:


The files /usr/lib/libpoll.dylib and /usr/include/poll.h are now included, however the OS X-supplied implementation of this library is not complete. If it is sufficient for your purposes, you can remove dependencies on the Fink "libpoll" and "libpoll-shlibs" packages. The library code is actually incorporated into libSystem, which is automatically linked. That means that -lpoll is not necessary if you wish to use the OS X implementation. Be aware that OS X supplies a libpoll.dylib, so -lpoll may give different results depending on whether you have the Fink "libpoll" package installed or not.


The files /usr/lib/libdl.dylib and /usr/include/dlfcn.h are now included, so you should not need dependencies on the Fink "dlcompat", "dlcompat-dev", and "dlcompat-shlibs" packages. The library code is actually incorportated into libSystem, which is automatically linked. That means that -ldl is not necessary (but has no effect if used).

GNU getopt

This library, including the getopt_long() function, has been incorportated into libSystem and /usr/include/getopt.h, so you may not need to use the Fink "libgnugetopt" and "libgnugetopt-shlibs" packages. Because libSystem is automatically linked and /usr/include is automatically searched for headers, you could remove any -lgnugetopt and -I/opt/sw/include/gnugetopt flags that were manually added in order to access Fink's "libgnugetopt".

When migrating a package to OS X 10.3, try to remove these deprecated dependencies, as those packages may be removed from these newer package trees in the future. This means you will need a separate package description file for each tree. As always, the Revision must be increased when making changes to a package. In this manner, a user who upgrades from OS X 10.2 to 10.3 will see 10.3-specific packages as "newer" than his existing 10.2 ones. By convention, the Revision should be increased by 10 when changes are made for migration to a higher tree inn order to leave space for the lower-tree package to be updated in the future.

When testing a migrated package, make sure to uninstall the packages whose BuildDepends you removed. Otherwise the compiler may still link the Fink-supplied libraries.

6 Preparing for 10.4

6.1 Perl

/usr/bin/perl is now perl 5.8.6; the architecture string is still "darwin-thread-multi-2level".

6.2 New symbol definitions

Mac OS X 10.4 has changed the types of many symbols. If you previously set a type explicitly, for example, defining socklen_t as int, that definition may no longer be correct.

6.3 New builtin system libraries

The poll() function in Mac OS X 10.3 was actually an emulation implemented using select(). In Mac OS X 10.4, poll() is a real function implemented in the kernel, however it is broken when used with sockets. Consider ignoring the system's poll() completely. Fink's glib2 package has been patched to use a fully functional emulation, so it is safe to use that library's implementation of poll-like functions.

Copyright Notice

Copyright (c) 2001 Christoph Pfisterer, Copyright (c) 2001-2020 The Fink Project. You may distribute this document in print for private purposes, provided the document and this copyright notice remain complete and unmodified. Any commercial reproduction and any online publication requires the explicit consent of the author.

Generated from $Fink: porting.en.xml,v 1.11 2023/08/04 5:08:13 nieder Exp $