m1 macOS build fails: undefined symbols

Issue #77 resolved
Robert Lemmens created an issue

When building on macOS (m1 cpu) I get linking errors:

[ 51%] Linking CXX shared library libode.dylib
Undefined symbols for architecture arm64:
  "_Microseconds", referenced from:
      getClockCount(unsigned long*) in timer.cpp.o
ld: symbol(s) not found for architecture arm64

When looking at the offending code (Microseconds definition) I do see something of a deprecation notice as well:

__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_8,__IPHONE_NA,__IPHONE_NA);

Steps to reproduce:

  1. clone repo
  2. cmake CMakeLists.txt
  3. cmake --build . --config Debug

Now I must admit that its only recently that I've gotten a Mac for development but I expected the fresh compilation to work. Is there something I missed in the documentation / m1 Mac specifics or does it not compile ootb on m1 Macs?

Thanks for your work!

Comments (9)

  1. Oleh Derevenko

    I’m sorry, I’m not using any Macs in my daily duties so sources may get outdated now and then as Mac evolves. Let’s try to fix your issue.

    Could you by a chance help me with a bit of info about your system?
    What is the proper way of detecting the starting system version on which Microseconds() call is not available?
    Do you know what is the replacement call that can be used to obtain system clock value with the finest available resolution and what is that resolution?

  2. Robert Lemmens reporter

    Not a problem, this is a first for me doing development on Macs as well because I use linux almost exclusively, but these m1 chips battery life is too good!

    System information:

    • OS: MacOS Montery (12.2.1)
    • CPU: Apple M1 (ARM64 architecture)
    • Kernel info (uname): Darwin Kernel Version 21.3.0

    I’ve went ahead and looked into it a bit and it looks like the entire “Carbon” api from apple is/has been removed and Microseconds was part of them. I presume since 10.8 it is no longer available: https://developer.apple.com/documentation/coreservices/1550773-microseconds?changes=latest_6

    It looks like Mac has some macro’s that can help with checking the macOS version, found in AvailabilityMacros.h:

    example:

    #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
    ....
    #else
    ....
    #endif
    

    and a bunch of other useful ones:

    As for this specific broken function; I’ve changed the relevant function to be the same as the Linux variant and that seems to solve the problem.

    Old Mac version (broken on my machine):

    #else // macintosh
    
    #include <CoreServices/CoreServices.h>
    #include <ode/Timer.h>
    
    static inline void getClockCount (unsigned long cc[2])
    {
        UnsignedWide ms;
        Microseconds (&ms);
        cc[1] = ms.lo / 1000000;
        cc[0] = ms.lo - ( cc[1] * 1000000 );
    }
    
    #endif
    

    Changed to linux variant:

    #else // macintosh
    
    #include <sys/time.h>
    #include <ode/Timer.h>
    
    static inline void getClockCount (unsigned long cc[2])
    {
        struct timeval tv;
        gettimeofday (&tv,0);
        cc[0] = tv.tv_usec;
        cc[1] = tv.tv_sec;
    }
    
    #endif
    

    And that compiles just fine. sys/time is the same header on linux so they can even be consolidated I think.

    I am not to sure if this actually breaks anything important because I’m not well versed in how a physics engine works internally and what this functionality is used for. I’ve ran some of the demo’s and the hinges still hinge and the buggy still drives around 🙂.

    I could not find a specific replacement for Microseconds, but besides time of day there are a couple of options available. There is also a thing called mach/mach_time.h which seems to provide the highest resolution clocks available. I know nothing about this, but found this on a somewhat old SO post. The header exists still on my system and the functions as well: https://stackoverflow.com/questions/23378063/how-can-i-use-mach-absolute-time-without-overflowing

    If there is anything else you'd like to know or see let me know!

  3. Oleh Derevenko

    I've made a commit to master in attempt to fix the problem with the info I’ve found on internet.
    Please try it and let me know if it works for you.

  4. Oleh Derevenko

    Please try building with these functions variants

    static inline double loadClockCount (unsigned long a[2])

    {

    uint64_t absTime = (uint64_t)(a[0] | ((uint64_t)a[1] << 32));

    mach_timebase_info_data_t timeInfo;

    double resultTime = mach_timebase_info(&timeInfo) == 0 ? absTime * ((double)timeInfo.numer / timeInfo.denom) : 0.0;

    return resultTime;

    }

    double dTimerResolution()

    {

    mach_timebase_info_data_t timeInfo;

    double nanoResolution = 1e-9;
    

    double resultResolution = mach_timebase_info(&timeInfo) == 0 ? nanoResolution * ((double)timeInfo.numer / timeInfo.denom) : nanoResolution;

    return resultResolution;
    

    }

  5. Robert Lemmens reporter

    Alright, cloned a fresh copy and

    • cmake -Bbuild CMakeLists.txt
    • cmake --build build

    completes succesfully!

    Thanks for the help. Issue can be closed!

  6. Log in to comment