Mac OS binary compatibility

669 views
Skip to first unread message

squis...@gmail.com

unread,
Jan 3, 2014, 12:09:42 PM1/3/14
to golan...@googlegroups.com
It looks like a binary compiled on the latest Mac OS X version does not run on at least some prior versions of the OS. Is this expected? If so, is there a workaround? I'd like to be able to distribute my program in binary form, partly because installing compilers and building programs from source isn't an approach every user can follow.

Details follow.

I am using go 1.2, as installed by homebrew. My development machine is using Mac OS X 10.9.1, and binaries built there do not run on a machine running Mac OS X 10.7.5.

My program is named scoot. Here is the output of various file-inspection commands on my 10.9.1 box:

$ ./bitscooter/scoot -version
Bitscooter v. TEST.20140103.0418 for darwin amd64
 
$ file ./bitscooter/scoot
./bitscooter/scoot: Mach-O 64-bit executable x86_64
 
$ otool -L ./bitscooter/scoot
./bitscooter/scoot: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 855.11.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55471.0.0)

Here is the output of the same commands on the 10.7.5 box:

$ ./bitscooter/scoot -version
Illegal instruction: 4

$ file ./bitscooter/scoot
./bitscooter/scoot: Mach-O 64-bit executable x86_64

$ otool -L ./bitscooter/scoot
./bitscooter/scoot:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 855.11.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55471.0.0)

Note that otool reports that the binary wants to link to the modern versions of the libraries, even on the older OS version. I suspect this is the cause of the problem.  Here is what otool reports for the libraries the binary depends on, on 10.7.5:

$ otool -L  /usr/lib/libSystem.B.dylib
/usr/lib/libSystem.B.dylib:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
<dependencies cut>

$ otool -L /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation 
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 635.21.0)
<snip>

$ otool -L /System/Library/Frameworks/Security.framework/Versions/A/Security
/System/Library/Frameworks/Security.framework/Versions/A/Security:
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55148.6.0)
<snip>

andrey mirtchovski

unread,
Jan 4, 2014, 1:50:46 AM1/4/14
to squis...@gmail.com, golang-nuts
here are a couple of options:

- compile on the earliest platform you are targeting (10.7)
- build the go environment without CGO (set CGO_ENABLED=0 before
./make.bash) if possible. this creates a truly statically linked
executable but it may have issues with networking and encryption. this
isn't advisable on macs but i'm using it successfully.

there may be other ways.

Carlos Castillo

unread,
Jan 4, 2014, 1:41:54 PM1/4/14
to golan...@googlegroups.com, squis...@gmail.com
AFAICT, the issue is with external linking, as the external (ie: system) linker by default will link against the latest OSX only. I have two solutions that worked for me building on 10.9 for 10.6, but YMMV:
  1. Don't use the external linker, this can be done by either not using CGO, as any user-built cgo package will switch to external linking, or by forcing the internal linker to be used (eg: go build -ldflags -linkmode=internal).
  2. Set the environment variable MACOSX_DEPLOYMENT_TARGET=10.6 (10.7 in your case).
The drawback with the first solution is that the purpose of external linking was to provide better support for user-made CGO packages than the internal linker could provide, but AFAIK, the majority of problems than can occur will manifest at link time, or at program startup. If you can minimize or altogether eliminate your use of CGO, your program will be more portable and easier to build elsewhere.

The first solution changes the output of "otool -L" to have versions of "0.0.0" across the board (I assume the internal linker can't express version requirements very well), but the second solution didn't change the version strings at all.

Both solutions will obviously fail if you attempt to use 10.9 exclusive functionality, intentionally or not, in your code. I believe that if you use MACOSX_DEPLOYMENT_TARGET (which uses weak linking), you don't find out about unresolved symbols until they are actually used at runtime. I'm not certain about how an internally linked binary (option 1) would fail; it might do the same as option 2, or it might be "nicer" and refuse to start.

Also, I only tested an app that used stdlib cgo packages (net, and crypto/tls in a http server). I did force external linking, and it did "successfully" fail to start on the 10.6 machine with a similar error you what got (Illegal instruction).
Reply all
Reply to author
Forward
0 new messages