Use of -dead_strip_dylibs by default makes the -framework flag appear broken.
Travis Whitaker
pi.boy.travis at gmail.com
Mon Jun 18 01:59:00 UTC 2018
Hello Haskell Friends,
GHC always passes
<https://github.com/ghc/ghc/blob/master/compiler/main/DriverPipeline.hs#L1888>
-dead_strip_dylibs to the linker on macOS. This means that Haskell programs
that use Objective-C-style dynamic binding (via objc_getClass or similar)
won't actually be able to find the Objective-C methods they need at
runtime. Here's an example illustrating the problem: Consider this small
example program:
#include <stdio.h>
extern void *objc_getClass(char *n);
void test_get_class(char *n)
{
void *cp = objc_getClass(n);
if(cp == NULL)
{
printf("Didn't find class %s\n", n);
}
else
{
printf("Found class %s\n", n);
}
}
int main(int argc, char *argv[])
{
test_get_class(argv[1]);
return 0;
}
Building like this:
clang -o hasclass main.c -lobjc -L/usr/lib -framework Foundation -F
/System/Library/Frameworks/
Yields an executable that works like this:
$ ./hasclass NSObject
Found class NSObject
$ ./hasclass NSString
Found class NSString
$ ./hasclass NSDate
Found class NSDate
otool shows that we're linked against Foundation properly:
hasclass:
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current
version 228.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
(compatibility version 300.0.0, current version 1452.23.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current
version 1252.50.4)
Now consider this equivalent Haskell example:
module Main where
import Foreign.C.String
import Foreign.Ptr
import System.Environment
foreign import ccall objc_getClass :: CString -> IO (Ptr a)
testGetClass :: String -> IO ()
testGetClass n = withCString n $ \cn -> do
cp <- objc_getClass cn
let m | cp == nullPtr = "Didn't find class " ++ n
| otherwise = "Found class " ++ n
putStrLn m
main :: IO ()
main = getArgs >>= (testGetClass . head)
Building like this:
ghc -o hasclass Main.hs -lobjc -L/usr/lib -framework foundation
-framework-path /System/Library/Frameworks/
Yields an executable that works like this:
$ ./hasclass NSObject
Found class NSObject
$ ./hasclass NSString
Didn't find class NSString
$ ./hasclass NSDate
Didn't find class NSDate
otool shows that our load commands for Foundation are missing:
hasclass:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current
version 1252.50.4)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current
version 228.0.0)
/nix/store/7jdxjpy1p5ynl9qrr3ymx01973a1abf6-gmp-6.1.2/lib/libgmp.10.dylib
(compatibility version 14.0.0, current version 14.2.0)
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current
version 7.0.0)
Interestingly, the testGetClass function will work just fine in GHCi, since
it always loads all of the shared objects and frameworks it's asked to. As
far as I can tell the only way to get a hasclass executable with the
correct behavior is to do the final linking manually with Clang.
My understanding is that this behavior was introduced to work around symbol
count limitations introduced in macOS Sierra. It would be nice to wrap the
frameworks passed to the linker in some flags that spares them from
-dead_strip_dylibs. I haven't found such a feature in my limited digging
around, but perhaps someone who knows more about systems programming on
macOS will have an idea. Statically linking against the system frameworks
would be a workable stopgap solution, but I have yet to find an easy way to
do that.
I'm curious what others' thoughts are on this issue; it's very difficult to
call Objective-C methods from Haskell (without generating Objective-C)
without a fix for this.
Regards,
Travis Whitaker
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20180617/c5b39c95/attachment.html>
More information about the ghc-devs
mailing list