(Editor's note: as of XP7.62RC2, Mach-O plugins are supported in the sim (the 7.62RC2 DLLs are hosed, so you may need to download the Mach-O developers SDK); what follows now serves as an architectural description of how Mach-O plugins work.)
! Mach-O or CFM - which one?
The Macintosh has two "ABIs" (application binary interfaces) or ways to compile and link a plugin.
CFM (Code Fragment Manager) is the ABI from Mac OS 9, and is supported on OS X for compatibility. CFM plugins can run on both OS 9 and OS X, but can only use CFM-accessable functions in the operating system ("carbon" APIs, usually ones present on OS 9 and OS X).
Mach-O is the ABI from OS X's native Unix subsystem and is supported only on OS X. Mach-O plugins can only run on OS X, but can access any operating system routine on OS X.
Which one do you need? Well, first of all, consider your tools:
- CodeWarrior 7, 8 and 9 can create either Mach-O or CFM plugins.
- Older CodeWarriors (5 and 6) can only make CFM plugins.
- XCode, ProjectBuilder, and gcc can only make Mach-O plugins. (XCode and PB are wrappers around gcc.)
So you may not have a choice. Other considerations:
- Use CFM if you must run on OS 9.
- Use CFM if you need to run on really old versions of X-Plane; Mach-O plugin support is not available for X-Plane 6 or early versions of X-Plane 7.
- You may want to use Mach-O if you need to use a lot of OS X operating system routines.
Some OS X APIs (for example, HIDManager and all of I/O Kit, AudioUnits, and BSD sockets) are only available to Mach-O code. There are three ways to use these routines, in order of simplicity:
- If you have the tools and don't need OS 9, just make a Mach-O plugin and use the APIs normally.
- If you only need a few API calls, use the CFBundle OS routines to find the framework that the system calls live in, open it, and get function pointers one at a time. This works well for a few calls, but badly when you need a lot of system headers. X-Plane uses this technique to get access to the OS X BSD call "mmap" to do memory mapped I/O.
- If you need a lot of API calls, or a lot of complex headers, create your plugin in two pieces; a Mach-O bundle calls lots of OS X routines and provides a simple interface to them; the CFM plugin then finds, loads and calls the Mach-O bundle at runtime. X-Plane uses this technique to access HIDmanager for joystick support on OS X; the HID headers are too complex to use from CFM code.
Techniques 2 and 3 can be used to make a plugin that runs on OS 9 and X and uses OS X routines when optional; when you are on OS 9, attempting to load either your Mach-O bundle (technique 3) or an OS X system framework (technique 2), the load will simply fail, so you must check for NULL bundles and ptrs.
! Mach-O Plugin Support for X-Plane plugins
Plugins in X-Plane are DLLs that export symbols called by the sim to allow hooked functionality. Previously plugins had to be CFM shared libraries (shlbs) since X-Plane and the plugin management DLLs were also shlbs.
With Mach-O support, plugins may also be Mach-O bundles or dylibs. A bundle is a mach-o dynamic library (dylib) of header type 'bundle' wrapped in a series of folders; the root folder appears as a single entity to the user, but not to programs. A dylib is the Mach-O version of a DLL, the single file that actually has plugin code.
The bundle flag on the dylib is part of Apple's bundle conventions and it tells the Mach-O dylib loader that functions in the bundle are not to be used to resolve other dylibs. (In other words, if you have a private function called "BreakString" in your bundle plugin, and another bundle requires BreakString from some random support library, your bundle will not be accidentally used as someone else's support library.)
We will use bundles or naked dylibs for Mach-O plugins because:
- Some IDEs may provide integrated bundling.
- Command line tools do not bundle.
- This gives developers flexibility to adopt Apple or Unix conventions.
Plugins will not link against a stub library or framework; instead they will simply link with a flat namespace and undefined symbols suppressed. This will allow them to find XPLM symbols at load time as long as they are resident in memory. X-Plane's plugin system will guarantee that some kind of dylib (with exported symbols) is present in the proccess's memory when plugins are loaded.
__WARNING:__ While there are frameworks for the XPLM and XPWidgets in the plugins folder, __do not__ link against them; doing so will cause your plugin to not load. These frameworks are present to implement the SDK APIs, but are not suitable as export libraries.
We are not using a framework to provide access to SDK symbols because:
- A framework would have to be deposited in a public place outside the X-System folder; this goes against X-Plane's installation procedures.
- The partial location of the framework would be compiled into plugins, limiting options for resolution later.
Developers will be able to build plugins via gcc or any of the tools layered on top of it, such as X-Code or ProjectBuilder. Developers will also be able to use CodeWarrior 8 to build Mach-O plugins, but will have to use the Apple linker, as teh Metrowerks linker does not allow symbols to go unresolved.
The full set of SDK APIs will be available to Mach-O plugins; function calls between CFM and Mach-O runtimes will be handled transparently by the SDK.
__WARNING:__ Previously plugins could pass function pointers to each other as datarefs or inter-plugin message parameters, and then call them. This will no longer be safe as a Mach-O plugin could pass a function pointer directly to a CFM plugin and vice-versa. Plugins should use plugin messages to invoke each other, not function pointers. (Since plugins might be implemented in Java, passing function pointers wasn't really a good idea anyway!)