Compiler and Other Technical Issues in the Mac OS X 10.0.x Platform


Several compiler and operating system issues were encountered during the portingof the Abstraction and Infrastructure Layers of OpenOffice.org. They are described below for your reference. They pertain to Mac OS X 10.0.x and Mac OS X 10.0.x Developer Tools 10.0.x. The solutions discussed in this document are current as of the OO638C release tag and are specifically coded for the Mac OS X 10.0.x platform only. Hence, when implementing the solutions discussed in this document, you need to enclose any corrective code within the following preprocessor statements:

#ifdef MACOSX
...
#endif
#if defined(__GNUC__) && defined(__APPLE__)
...
#endif

Static data members in template classes

As the Mac OS X 10.0.x C++ compiler behaves somewhat differently from GCC compilers for other platforms, the static datat members in template classes, if not explicitly initialized, will cause undefined symbols at link time. For example:

template <abc>
class ImplHelper: public ImplHelperBase<abc>
{ 
    static ClassData s_aCD;
    ...
}

Each instantiation of s_aCD needs to be explicitly initialized once and only once somewhere. Note that it can only be instantiated once or you will get duplicate symbol errors that will crash an executable at runtime. So a mechanism (solenv/unxmacxp/bin/init-static-template-data) was created to automatically detect and collect these static members into a set of files and initialized them. One set of files is created for each source directory. For example, the following files are created for the source directory in the cppuhelper module:

Then, immediately before an executable or shared library is linked, the standard build recipes merge the above files with the same files from all of the other source directories that have been built to create an aggregate list of static data members that require initialization. These merged files are then compiled and linked into a shared library called libstaticmxp.dylib. Since all executables and shared libraries generated by the Mac OS X 10.0.x build are linked to the library, all executables are guaranteed to have each required static data member initialized once and only once.

As of the OO638C release tag, only the following template classes are supported by this mechanism:

Should any new static data members be found in template classes, you will need to implement support for these new static data members in the following script:

Static automatic variables in inline functions

Each time an inline function is inlined, a static automatic variable's definition is generated once. So we usually end up having multiple copies of these variable definitions. The compiler may or may not generate a warning about this. However, the assembler will generate error messages like the following:

    {standard input}:unknown:Can't emit reloc {- symbol "L37$pb"} @ file address 36.
    {standard input}:unknown:Can't emit reloc {- symbol "L37$pb"} @ file address 32.

To solve this problem, you must either move the static automatic variable in an inline function to outside of the function (i.e. make it a file scope variable) or reimplement the inline function so that a static automatic variable is no longer used.

For examples of how to implement the first option, take a look at any of the *.hpp files that are generated by the build.

Static automatic variables in C++ class destructors

In certain situations, when the following 2 conditions exist:

The virtual methods of the static variable will be treated as non-virtual. As a result, the incorrect code may be called. This can be illustrated by the following sample code.

    #include <stdio.h>

    // Create a class with a pure virtual method

    class MyAbstractClass
    {
    public:
        virtual void print() = 0;
    protected:
        MyAbstractClass() { }
        virtual ~MyAbstractClass() { }
    };

    // Create a subclass of the class with a pure virtual method

    class MyConcreteClass : public MyAbstractClass
    {
    public:
        MyConcreteClass() { }
        virtual ~MyConcreteClass() { }
        virtual void print() { printf( "In MyConcreteClass\n" ); }
    };

    // Create a class that invokes the subclasses method in it's destructor

    class MyWrapperClass
    {
    public:
        MyWrapperClass() { }
        ~MyWrapperClass();
        static MyAbstractClass getClass();
    };

    MyAbstractClass MyWrapperClass::getClass()
    {
        static MyConcreteClass rClass;
        return rClass;
    }

    MyWrapperClass::~MyWrapperClass()
    {
        MyAbstractClass& aClass = getClass();
        aClass.print();
    }

    // Create problem where the MyWrapperClass' destructor accidentally invokes
    // the base class' print method instead of the subclass' print method.

    MyWrapperClass aClass;

    int main( int argc, char **argv )
    {
        MyWrapperClass bClass;
    }

When this sample program is executed, the program generates the following output:

    In MyConcreteClass
    pure virtual method called
    Abort

instead of the expected output:

    In MyConcreteClass
    In MyConcreteClass

To solve this problem, you must either move the static automatic variable in an inline function to outside of the function (i.e. make it a file scope variable) or reimplement the inline function so that a static automatic variable is no longer used.

In the above example, we would change the following lines:

    MyAbstractClass MyWrapperClass::getClass()
    {
        static MyConcreteClass rClass;
        return rClass;
    }
to:
    static MyConcreteClass rClass;

    MyAbstractClass MyWrapperClass::getClass()
    {
        return rClass;
    }


Optimization bugs when exceptions are turned on

When -fexceptions and optimization flags (e.g. -O or -On) are both turned on while compiling a source file, the runtime code execution can be very unpredictable. The exact cause of this compiler bug is unknown. The solution is to turn off optimization whenever the -fexception flag is set (i.e. when either the ENABLE_EXCEPTIONS or EXCEPTIONSFILES macros are set in a makefile.mk file).

As of the OO638C release tag, the standard build recipes will automatically turn off optimization whenever exceptions are turned on.


Duplicate symbol bug in the dynamic library loader

When loading two different modules/functions with the same symbol name from two different libraries, the Mac OS X 10.0.x dynamic loader resolves to the symbol of the library first loaded even though we wanted two different versions from the two different libraries. In addition, there is no way to unload a library once it is loaded, so we can not unload the first and then load the second. This bug renders OpenOffice.org's UNO plugin technology unusable since the UNO plugin technology needs each UNO shared library to implement the following standard functions:

The solution is to prepend "lib(library name)" onto each of these standard functions so that each UNO shared library's standard functions are uniquely name. Then, at runtime, the library loading functions implemented in the sal/osl/unx/module.c source file will automatically prepend the library name to the requested function before retrieving a pointer to the function from the library.

For this solution to work, developers must implement the following after porting a module:

  1. Determine if any of the above standard functions exist in any of the shared libraries built in the module. Note that the functions may already contain an incorrect "lib(some text)" prefix. These functions need to be included in the next step.
  2. For each standard function that does exist in the shared libraries build in the module, determine which source file implements the function.
  3. In the makefile.mk file in each of the directories that contains a source file that implements one or more of the standard functions, add the following macro definition:
    .IF "$(OS)"=="MACOSX"
    SYMBOLPREFIX=(library name)
    .ENDIF
    
    In the above macro definition, substitute "(library name)" with portion of the shared library name after stripping off the leading "lib" and trailing ".dylib". For example, if the shared library name is libi18n625mxp.dylib, "(library name)" should be replaced with "i18n625mxp".
  4. Perform a clean build of the module using "dmake -u" to verify that standard functions have been correctly renamed.

The SYMBOLPREFIX macro is used by the build to create "-D" options for the compiler. These defines are used to rename the standard functions. For a list of these "-D" options that the SYMBOLPREFIX macro affects, take a look at the following file:

Bugs in tempnam() and mktemp() system functions

In Mac OS X 10.0.x, multiple invocations of either the tempnam() or mktemp() system functions will return the same filename. On other platforms, these functions generate a unique filename with each invocation. This Mac OS X 10.0.x bug will cause unpredictable results at runtime.

The solution is to use the tmpnam() function instead of the tempnam() function and to use the mkstemp() function instead of the mktemp() function. Both tmpnam() and mkstemp() return a unique filename with each invocation. Note that since the default behavior for both tmpnam() and mkstemp() is to create their files in the /var/tmp directory, you need to make sure that the /var/tmp directory is readable and writable by all users. The Mac OS X 10.0.x installation program, /var/tmp is set to be readable and writable only by root.

Special packaging requirements for shared libraries

Most Unix platforms use ".so" as the suffix for shared libraries. However, the Mac OS X 10.0.x linker expects ".dylib" as the suffix for shared libraries.

In addition, the library loading functions in the CoreFoundation framework expect that a shared library be "bundled" within the a libname.dylib.framework directory. For example, "libmyshared.dylib" needs to be put into a directory named "libmyshared.dylib.framework".

Lastly, the Java call System.loadLibrary expects ".jnilib" as the suffix for shared libraries.

As of the OO638C release tag, the standard build recipes will automatically create the matching lib*.dylib.framework/lib*.dylib and lib*.jnilib files for each lib*.dylib file that is linked. However, developers need to be aware that the source code may use ".so" in the shared library name. In such cases, developers should modify the code to use ".dylib.framework" in the shared library name for Mac OS X 10.0.x.