Since Mac OS X v10.4, in the system library, libSystem.dylib
, new symbols that contain dollar signs ($
) have been added. This release note explains why they are there, and how a developer might want to take advantage of them.
Software Evolution vs. Backward Compatibility
Symbol Variants
Prototypes in Header Files
Preprocessor Macros Controlling the Variants
Summary of Variants
gdb Support
Software is always changing. New demands require new features to be implemented. Bugs are discovered and fixed. Hardware and lower layers of the OS change, sometimes requiring upper layers to adapt.
For a commercial operating system like Mac OS X, third-party software expects that the system software will always act the same way. This backward compatibility prolongs the users’ investment in software and fosters the notion of greater stability in the system.
The need to evolve the software often conflicts with the desire to provide backward compatibility, but innovative solutions can allow both. If you remember the (classic) Mac OS before Mac OS X, a Classic environment was created specifically to run old applications on Mac OS X. Similarly, the transition to Intel-based hardware spurred the creation of the Rosetta dynamic translation software to provide compatibility with PowerPC-based applications.
Early in the Mac OS X v10.4 timeframe, two major software initiatives required an equally innovative solution to maintain backward compatibility. First, to be certified for UNIX™ conformance, hundreds of system routines needed to be modified. Some changes were considered just bug fixes, but many of the changes required significant change in behavior to the largely BSD-style routines. These changes would surely break existing applications.
Note: With regard to Mac OS X, the general term, UNIX™ conformance, refers to the Single UNIX Specification, Version 3 (SUSv3), a combination of IEEE, ISO and Open Group standards, including that referred to as POSIX. Furthermore, UNIX 03 is the Open Group certified brand for SUSv3 conformance, which Mac OS X 10.5 is registered. Previous brands include UNIX 95 and UNIX 98, and so we sometimes refer to UNIX 2003 just to avoid date confusion.
Secondly, up until v10.4, there was no support for real (PowerPC) long double floating-point numbers (or more correctly, the long double type was the same size as the regular 64-bit double floating-point type). Support for a real, 128-bit long double type would mean incompatible API changes. Code compiled for one type of long double would crash or produce incorrect results when using routines for the other long double type.
One way to allow routines to behave in new ways for new code but maintain the legacy behavior for previously compiled code is to use symbol versioning. In symbol versioning, different code can have the same symbol name, but have different version numbers. Unfortunately, this would require lots of changes to the compiler, linker and binary file format; a major undertaking that would have to happen before the real work could even be started.
So as an alternative, a feature of the gcc
compiler was used; the __asm
command can be used to rename a symbol. A special suffix was added to the symbol, and by using different suffixes, we can generate families of symbol variants.
That is where the dollar sign comes in. It is used as a separator between the real symbol name and the variant name. Since it can’t occur in normal C code, this avoids the possibility of symbol name collision.
This is what it looks like:
% nm /usr/lib/libSystem.dylib |
... |
00086e9f T _fputs |
0003a14f T _fputs$UNIX2003 |
... |
By Mac OS X convention, all variables and routine names are automatically prefixed with an underscore. The _fputs
symbol is the legacy variant, while the _fputs$UNIX2003
symbol is the new, UNIX™ conforming one. All programs previously built will only know about the _fputs
symbol, and will continue to use it and get legacy behavior, while new code can link to the new _fputs$UNIX2003
symbol and get UNIX™ conforming behavior.
A symbol may have more than one suffix. For instance:
000a6905 T _ftw |
000a6603 T _ftw$INODE64$UNIX2003 |
000a6dc7 T _ftw$UNIX2003 |
Note: Multiple suffixes are always ordered alphabetically so that there is only one legal arrangement of the suffixes.
It is in the header files that the real magic occurs. For instance, to generate the _fputs$UNIX2003
symbol, we would need something like:
int fputs(const char * __restrict, FILE * __restrict) __asm("_fputs$UNIX2003"); |
In <stdio.h>
, the actual prototype looks like:
int fputs(const char * __restrict, FILE * __restrict) __DARWIN_ALIAS(fputs); |
The __DARWIN_ALIAS
macro resolves to the necessary __asm
command, as appropriate for the legacy or UNIX™ conforming variant.
Important: Including the appropriate header file for each system variable and routine used is now more important than ever. Use the −Wmissing-prototypes
or equivalent option to the compiler to detect a global function without a prototype, and then see the manual page to find out what header file or files to include. (See the documentation for man(1)
and manpages(5)
for more information about manual pages.)
Important: As per convention, preprocessor macros beginning with two underscores are private to the implementation and should not be used by the user because they may change at any time. Preprocessor macros beginning with a single underscore are owned by the implementation, but can be used by the user when appropriate.
The UNIX™ conformance variants use the $UNIX2003
suffix.
Note: When the UNIX™ conformance variants are used, the feature test macro _DARWIN_FEATURE_UNIX_CONFORMANCE
is defined. Its value is 3, corresponding to SUSv3. The macro will be undefined otherwise (legacy behavior).
Important: The work for UNIX™ conformance started in Mac OS 10.4, but was not completed until 10.5. Thus, in the 10.4 versions of libSystem.dylib
, many of the conforming variant symbols (with the $UNIX2003
suffix) exist. The list is not complete, and the conforming behavior of the variant symbols may not be complete, so they should be avoided.
Because the 64-bit environment has no legacy to maintain, it was created to be UNIX™ conforming from the start, without the use of the $UNIX2003
suffix. So, for example, _fputs$UNIX2003
in 32-bit and _fputs
in 64-bit will have the same conforming behavior.
As of Mac OS X 10.5, UNIX™ conformance is on by default, and newly compiled code will link against the UNIX™ conformance variants, unless overridden with the following five macros.
_POSIX_C_SOURCE
and _XOPEN_SOURCE
The _POSIX_C_SOURCE
and _XOPEN_SOURCE
macros are often set to specify the various levels of standards support. On Mac OS X, only SUSv3 is supported, so the actual value of these macros is not used (but they are reset to appropriate values when necessary).
When either or both of these macros are set, the UNIX™ conforming variants will be used. In addition, unless _DARWIN_C_SOURCE
is also set (see below), these macros will cause the hiding of any variable, routine, structure, etc., in covered header files that are not specified in the standards. (These extra definitions are referred to as extensions to the standards.) Thus, only SUSv3 definitions will be visible in those header files.
_DARWIN_C_SOURCE
The _DARWIN_C_SOURCE
macro (defined to any value), causes the UNIX™ conforming variants to be used, but does not hide the extensions to the standards, as _POSIX_C_SOURCE
and _XOPEN_SOURCE
do. The _DARWIN_C_SOURCE
macro can be used in conjunction with the _POSIX_C_SOURCE
and _XOPEN_SOURCE
macros, with the _DARWIN_C_SOURCE
behavior overriding the other two, allowing the extensions to the standards to be visible.
In addition, the _DARWIN_C_SOURCE
macro will enable a few other extensions to the standards. These extensions occur where the SUSv3 standard puts additional limitations on the functionality beyond that of legacy (and, typically, BSD) behavior. The extension variants use the $DARWIN_EXTSN
suffix, and can also be enabled with separate macros. (See the macro descriptions below.)
_NONSTD_SOURCE
The _NONSTD_SOURCE
macro can be used to turn off the default UNIX™ conformance, and allow code to be built with legacy behavior. However, this macro will produce a compiler error when any of the above macros are set.
MACOSX_DEPLOYMENT_TARGET
When none of the previous four macros are set, the variants chosen are affected by the environment variable MACOSX_DEPLOYMENT_TARGET
or the −mmacosx-version-min=...
argument passed to the compiler. For example, you might pass −mmacosx-version-min=10.5
to the compiler or set MACOSX_DEPLOYMENT_TARGET=10.5
to target Mac OS X v10.5.
If you target version 10.5 or later, the UNIX™ conforming variants are used automatically. If you target version 10.4 or earlier, the legacy variants are used. (There are other side effects as well, such as disabling newer linker features.)
In Mac OS X 10.5 or later, if you do not use this or the previous four macros, the UNIX™ conforming variants are used by default.
In addition, if you target version 10.5 or later (or by default if you do not target a specific version), the Mac OS X 10.5 variants (those with the $1050
suffix) are used. These variants have significant new behavior that might cause previously compiled programs to misbehave. For example, the (legacy) _select
routine imposed a minimum timeout value of 10 milliseconds; the new _select$1050
routine has no such minimum.
MACOSX_DEPLOYMENT_TARGET
(32-bit PowerPC only)Setting MACOSX_DEPLOYMENT_TARGET
to 10.4 or later (or passing −mmacosx-version-min=10.4
or later to the compiler) enables 128-bit long double support. Routines that pass long doubles either directly (like strtold
) or indirectly (like printf
and family) have the 128-bit long double variant which uses the $LDBL128
suffix, while the legacy 64-bit long double variants are unadorned.
Important: There is also a variant with the $LDBL64
suffix; these symbols are currently unused, and might be used in the future, or even removed. Do not use them.
What happens when MACOSX_DEPLOYMENT_TARGET
is set to 10.3 or earlier (or is not set at all) is more complicated. During the development of Mac OS X v10.4, it was desired to have 128-bit long double support be the default. However, when MACOSX_DEPLOYMENT_TARGET
is not set, things would default to the behavior three releases before—that of 10.1. While it was possible to back-port the standard C library routines that used long double support to 10.3 (though not the math routines), they couldn’t be back-ported to 10.1 or 10.2.
As a compromise, when MACOSX_DEPLOYMENT_TARGET
is not set, or set to 10.3 or earlier, the header files would use a different variant, with the $LDBLStub
suffix. The compiler would instruct the loader to link against /usr/lib/libSystemStubs.a
, where the $LDBLStub
suffixed symbols are defined. At runtime, these assembly language stubs try to lookup the symbol with the same base name and a $LDBL128
suffix, and if it finds it, uses it. Otherwise, it will call the unadorned symbol name. This allows the code to adapt to whichever symbols are actually available in the current system library.
Note: This is not without risk. Code compiled using 128-bit long doubles may still crash or give incorrect results on systems without 128-bit long double support. However, given that the actual use of long doubles is minimal, this scheme allows 128-bit long doubles to be the default, and at the same time, if long doubles are not actually used (or if the code adapts to using 64-bit long doubles), the code will safely run on previous versions of Mac OS X without 128-bit long double support.
Note: While the compiler will instruct the loader to link against /usr/lib/libSystemStubs.a
, the loader itself does not know to do this. Projects that call the loader directly may fail to resolve the $LDBLStub
variants. In these cases, calls to the loader should be replaced with calls to the compiler to do the linking.
On Mac OS X 10.5, not setting MACOSX_DEPLOYMENT_TARGET
and not using −mmacosx-version-min
will result in 10.5 behavior by default, so the $LDBL128
variants are used instead of the $LDBLStub
variants. However, as before, if MACOSX_DEPLOYMENT_TARGET
is set to 10.3 or earlier, the $LDBLStub
variants are used.
_DARWIN_UNLIMITED_SELECT
Setting the _DARWIN_UNLIMITED_SELECT
macro will select the extension variants of select()
and pselect()
, which uses the $DARWIN_EXTSN
suffix. The extended versions do not fail if the first argument is greater than FD_SETSIZE
. This was the original BSD behavior.
_DARWIN_BETTER_REALPATH
Setting the _DARWIN_BETTER_REALPATH
macro selects the extension variant of realpath()
, which uses the $DARWIN_EXTSN
suffix. The extended version uses a fast shortcut to determine the current working directory, but this shortcut does not fail if the parent directories are not readable, as is dictated by the standards.
_DARWIN_USE_64_BIT_INODE
Setting the _DARWIN_USE_64_BIT_INODE
macro selects the 64-bit inode variants (those with a $INODE64
suffix) of routines like stat()
and readdir()
, which return an ino_t
value. The current default is to return legacy 32-bit ino_t
values, but 64-bit ino_t
values will become the default in the future. Structures containing ino_t
fields, like struct stat
, are larger than the 32-bit ino_t
versions, may have different ordering of the fields (to improve packing efficiency) and may have entirely new fields.
Note: When 64-bit inode support is used, the feature test macro _DARWIN_FEATURE_64_BIT_INODE
is defined and set to 1. It is undefined otherwise (for 32-bit inodes)
Important: Some of the routines returning ino_t
values also have symbols ending in “64”, like stat64()
and getmntinfo64()
. These routines always return 64-bit ino_t
values, and were used for early adoption before the 64-bit inode variants were available. These “early adoption” symbols will likely be deprecated in the future, so the use of the _DARWIN_USE_64_BIT_INODE
macro for 64-bit inode variants is the recommended way to get 64-bit inodes.
Below is a table that summarizes the variant suffixes and the corresponding, controlling and feature test macros.
Variant Suffix | Controlling Preprocessor Macro | Feature Test Macro | Description |
---|---|---|---|
|
| Mac OS X 10.5 and later behavior change | |
|
| Extended behavior beyond standards | |
|
|
| 64-bit |
|
|
| 128-bit long double support (32-bit PowerPC only) |
|
|
| UNIX™ conformance |
| (used internally) | ||
| (unused; may be removed) |
When setting a breakpoint at a routine that has multiple variants, gdb
will set a breakpoint at each variant. The delete
command can be used to remove any unwanted breakpoint at any of the variants. For example:
(gdb) b select Breakpoint 1 at 0x85edd Breakpoint 2 at 0x54592 Breakpoint 3 at 0x4ff50 Breakpoint 4 at 0x37b44 Breakpoint 5 at 0x37b09 warning: Multiple breakpoints were set. Use the "delete" command to delete unwanted breakpoints. (gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x00085edd <select+6> 2 breakpoint keep y 0x00054592 <select$UNIX2003+6> 3 breakpoint keep y 0x0004ff50 <select$DARWIN_EXTSN> 4 breakpoint keep y 0x00037b44 <select$DARWIN_EXTSN$NOCANCEL> 5 breakpoint keep y 0x00037b09 <select$NOCANCEL$UNIX2003+6> |
© 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-10-16)
|