OSX, ghci, dylib, what is the correct way? - macos

I need to build some C code and then reference that C code via the FFI. I would like to use my binding from inside ghci on osx. On of my constraints is that I cannot just hand the C sources to ghc in the .cabal file. This is due to a limitation with ghc/cabal that may be fixed in the next release of ghc (but I want my code to work now and on older releases). See this bug for details.
The gist of that bug is that the C code needs to be compiled with some Objective-C modules and ghc misinterprets those as linker scripts. I've tried many things and building the files myself with a makefile is the only thing that has worked. Really, this shouldn't be an issue though because it should be the same as if I decided to use a external C library that I didn't build myself. For the sake of this issue, let's pretend it's a separate C library that I can easily rebuild with different options.
If I build the C library as a .a, then ghci comlains that it cannot open the .dylib. My first question is: Why does ghci need a .dylib and does it really use it?
When I build a dylib I get a segfault when loading the code into ghci.
Keep in mind, this binding works already on other platforms, both linux and windows, and the binding works fine on osx when I'm compiling instead of using ghci. This problem specific to the osx/ghci combo.
In that trace above, I'm using gdb but it crashes regardless of whether I use gdb. I tracked it down to the lines that cause the crash:
void _glfwClearWindowHints( void )
{
memset( &_glfwLibrary.hints, 0, sizeof( _glfwLibrary.hints ) );
}
The trouble maker is that memset line, well actually the problem is that when running inside ghci writing to the hints structure of _glfwLibrary is a memory access violation. The hints struct is simply a bunch of ints. It's very flat and simple, and thus I think the problem is an issue either with how I'm linking things or with the way ghci is loading the code.
Here are the bits of my makefile that I use to build the dylib and the .a:
GCCFLAGS := $(shell ghc --info | ghc -e "fmap read getContents >>= \
putStrLn . unwords . read . Data.Maybe.fromJust . lookup \
\"Gcc Linker flags\"")
FRAMEWORK := -framework Cocoa -framework OpenGL
GLFW_FLAG := $(GCCFLAGS) -O2 -fno-common -Iglfw/include -Iglfw/lib \
-Iglfw/lib/cocoa $(CFLAGS)
all: $(BUILD_DIR)/static/libglfw.a $(BUILD_DIR)/dynamic/libglfw.dylib
$(BUILD_DIR)/dynamic/libglfw.dylib: $(OBJS)
$(CC) -dynamiclib -Wl,-single_module -compatibility_version 1 \
-current_version 1 \
$(GLFW_FLAG) -o $# $(OBJS) $(GLFW_SRC) $(FRAMEWORK)
$(BUILD_DIR)/static/libglfw.a: $(OBJS)
ar -rcs $# $(OBJS)
Most of the flags are taken straight from the GLFW Makefile so I think they should be correct for that library.
The first line looks a bit weird but it's the solution I used for this problem.
Platform details:
OSX 10.6.6
x86_64
4 cores
GHC version 7.0.3 installed via Haskell Platform installer
Source repo: https://github.com/dagit/GLFW-b
Edit: Here are my questions:
Should this work with ghci?
If so, what am I doing wrong or how can I fix the crash?
Can I just get by with the static .a version of the library with ghci?

Initial Questions
Should this work with ghci?
If so, what am I doing wrong or how can I fix the crash?
On OSX 10.6.7 (using the Haskell Platform /w GHC 7.0.2) I could load your built shared lib into ghci as follows:
➜ GLFW-b git:(master) ✗ ghci dist/build/Graphics/UI/GLFW.hs -Lbuild/dynam
ic -lglfw
GHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Loading object (dynamic) glfw ... done
final link ... done
[1 of 1] Compiling Graphics.UI.GLFW ( dist/build/Graphics/UI/GLFW.hs, inte
rpreted )
Ok, modules loaded: Graphics.UI.GLFW.
*Graphics.UI.GLFW> initialize
True
Note: I built the glfw libs using your provided Makefile, and additionally used your .cabal file to process src/Graphics/UI/GLFW.hsc and build dist/build/Graphics/UI/GLFW.hs (i.e. I'd previously run cabal configure/build).
Can I just get by with the static .a version of the library with ghci?
Yes, support for loading static libs was included in GHC 7.0.2 (GHC Manual). compiler/ghci/Linker.lhs is a great read, and will give you a high-level sense of how ghci decides what to make of the command line arguments passed to it. Additionally, when navigating various platform support issues, I've found this documentation exceedingly useful.
Linking static archives with ghci.
As of writing, line 1113 of compiler/ghci/Linker.hs demonstrates that ghci currently requires that static archives be built by the package system (i.e. named HSlibname.a)
locateOneObj :: [FilePath] -> String -> IO LibrarySpec
locateOneObj dirs lib
| not ("HS" `isPrefixOf` lib)
-- For non-Haskell libraries (e.g. gmp, iconv) we assume dynamic library
= assumeDll
| not isDynamicGhcLib
-- When the GHC package was not compiled as dynamic library
-- (=DYNAMIC not set), we search for .o libraries or, if they
-- don't exist, .a libraries.
= findObject `orElse` findArchive `orElse` assumeDll
Further investigation of cmd line argument parsing indicates that libraries specified are collected at line 402 in the reallyInitDynLinker function:
; classified_ld_inputs <- mapM classifyLdInput cmdline_ld_inputs
where classifyLdInput is defined over
classifyLdInput :: FilePath -> IO (Maybe LibrarySpec)
classifyLdInput f
| isObjectFilename f = return (Just (Object f))
| isDynLibFilename f = return (Just (DLLPath f))
| otherwise = do
hPutStrLn stderr ("Warning: ignoring unrecognised input `" ++ f ++ "'")
return Nothing
This means that outside of a package specification, there currently is no direct way to link an archive file in ghci (or said differently, there currently is no cmd-line argument to do so).
Fixing your cabal package
In your .cabal package specification, you are attempting to build two conflicting libraries:
A: link in a pre-built library (built according to your specification in Setup.hs and Makefile, and linked as per the extra-libraries and extra-lib-dirs directives)
B: build a new library inline (the c-sources and frameworks directives).
A simple fix for the error above is to simply remove all directives enabling B when building for Mac OSX, as follows:
include-dirs:
glfw/include
glfw/lib
- c-sources:
- glfw/lib/enable.c
- glfw/lib/fullscreen.c
- glfw/lib/glext.c
- glfw/lib/image.c
- glfw/lib/init.c
- glfw/lib/input.c
- glfw/lib/joystick.c
- glfw/lib/stream.c
- glfw/lib/tga.c
- glfw/lib/thread.c
- glfw/lib/time.c
- glfw/lib/window.c
+
+ if !os(darwin)
+ c-sources:
+ glfw/lib/enable.c
+ glfw/lib/fullscreen.c
+ glfw/lib/glext.c
+ glfw/lib/image.c
+ glfw/lib/init.c
+ glfw/lib/input.c
+ glfw/lib/joystick.c
+ glfw/lib/stream.c
+ glfw/lib/tga.c
+ glfw/lib/thread.c
+ glfw/lib/time.c
+ glfw/lib/window.c
and
if os(darwin)
- include-dirs:
- glfw/lib/cocoa
- frameworks:
- AGL
- Cocoa
- OpenGL
extra-libraries: glfw
- extra-lib-dirs: build/static build/dynamic
+ extra-lib-dirs: build/dynamic
I've not tested anything beyond verifying that the following now works properly:
➜ GLFW-b git:(master) ✗ ghci
GHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Prelude> :m + Graphics.UI.GLFW
Prelude Graphics.UI.GLFW> initialize
Loading package GLFW-b-0.0.2.6 ... linking ... done.
True
Prelude Graphics.UI.GLFW>

Related

How to include (msvc) libc when building c code with the Zig compiler

I've recently discovered zig and find it very interesting. I'm now trying to learn how to use zig as a cross compiler and, the following builds and runs fine (on Windows)
zig cc -Wno-everything src/ctest.c
however, when I use the build-exe command or build script, the (Windows) libc cannot be found and linked
c:\zigctest>zig build
Zig is unable to provide a libc for the chosen target 'x86_64-unknown-windows-msvc'.
The target is non-native, so Zig also cannot use the native libc installation.
Choose a target which has a libc available, or provide a libc installation text file.
See `zig libc --help` for more details.
The following command exited with error code 1:
c:\zigctest\zig.exe build-exe --library c --c-source -Wno-everything C:\zigctest\src\ctest.c --cache-dir C:\zigctest\zig-cache --name ctest -target x86_64-windows-msvc --cache on
exec failed
C:\zigctest\lib\zig\std\build.zig:768:36: 0x7ff76fece654 in std.build.Builder::std.build.Builder.exec (build.obj)
std.debug.panic("exec failed")
...
If I could see what zig cc really does, maybe I could figure it out (but zig cc does not seem to allow the --verbose-cc flag). Or how can I get zig to link with msvc (or any other working libc) on Windows? For completeness, the build.zig script is effectively:
...
const cflags = [][]const u8{
"-Wno-everything",
};
const exe = b.addExecutable("ctest", null);
exe.linkSystemLibrary("c");
exe.setBuildMode(mode);
exe.setTarget(builtin.Arch.x86_64, .windows, .msvc);
exe.addCSourceFile("src/ctest.c",cflags);
...
Here's the relevant issue for this: https://github.com/ziglang/zig/issues/514
Once the Windows libc portion of this issue is implemented, your example will work. Until then, cross compiling for Windows code that wants to link libc would need a cross compiling environment.

Haskell package missing c library

I'm having trouble building the hmatrix library on OS X Lion. Looking at the .cabal file, it requires the gsl library, so I installed it with macports. The .a files are in /opt/local/lib and the .h files are in /opt/local/include/gsl
As suggested here I changed the built-type from Custom to Simple. (without that change I get a similar error).
When I use cabal configure I get the following output:
* Missing C library: gsl
This problem can usually be solved by installing the system package that
provides this library (you may need the "-dev" version). If the library is
already installed but in a non-standard location then you can use the flags
--extra-include-dirs= and --extra-lib-dirs= to specify where it is.
So I tried cabal --extra-include-dirs=/opt/local/include --extra-lib-dirs=/opt/local/lib configure, but I still get the same error. I can compile and link a c program that includes gsl. What files is cabal looking for? If I have the right files, how do I tell it how to find them?
libgsl.a is a universal binary:
$ file /opt/local/lib/libgsl.a
/opt/local/lib/libgsl.a: Mach-O universal binary with 2 architectures
/opt/local/lib/libgsl.a (for architecture x86_64): current ar archive random library
/opt/local/lib/libgsl.a (for architecture i386): current ar archive random library
ghc looks like it's 64-bit:
$ ghc --info
[("Project name","The Glorious Glasgow Haskell Compilation System")
,("GCC extra via C opts"," -fwrapv")
,("C compiler command","/usr/bin/llvm-gcc")
,("C compiler flags"," -m64 -fno-stack-protector -m64")
,("ar command","/usr/bin/ar")
,("ar flags","clqs")
,("ar supports at file","NO")
,("touch command","touch")
,("dllwrap command","/bin/false")
,("windres command","/bin/false")
,("perl command","/usr/bin/perl")
,("target os","OSDarwin")
,("target arch","ArchX86_64")
,("target word size","8")
,("target has GNU nonexec stack","False")
,("target has subsections via symbols","True")
,("Project version","7.4.2")
,("Booter version","7.4.2")
,("Stage","2")
,("Build platform","x86_64-apple-darwin")
,("Host platform","x86_64-apple-darwin")
,("Target platform","x86_64-apple-darwin")
,("Have interpreter","YES")
,("Object splitting supported","NO")
,("Have native code generator","YES")
,("Support SMP","YES")
,("Unregisterised","NO")
,("Tables next to code","YES")
,("RTS ways","l debug thr thr_debug thr_l thr_p dyn debug_dyn thr_dyn thr_debug_dyn")
,("Leading underscore","YES")
,("Debug on","False")
,("LibDir","/usr/local/Cellar/ghc/7.4.2/lib/ghc-7.4.2")
,("Global Package DB","/usr/local/Cellar/ghc/7.4.2/lib/ghc-7.4.2/package.conf.d")
,("Gcc Linker flags","[\"-m64\"]")
,("Ld Linker flags","[\"-arch\",\"x86_64\"]")
]
As an alternative to mac-ports you can use the nix package manager for mac. It does a pretty good job of taking care of the c dependancies for for the libraries available through it. In general I have been more happy with it then any other package manager on mac.
Unfortunately mac(darwin) unlike for linux does not have as many binaries available through nix so installing ghc often means waiting for it to compile.
The commands to install ghc and hmatrix after installation of nix are:
nix-env -iA nixpkgs-unstable.haskellPackages.ghc
nix-env -iA nixpkgs-unstable.haskellPackages.hmatrix
All of the needed dependencies will be taken care of for you.
I just tried it on my macbook pro and hmatrix seems to be working correctly in ghci after trying commands from the first few pages of the tutorial.
I'm not a mac person, but it really sounds like you haven't installed the "-dev" version. For a mac, I suspect you need to install gsl-devel in addition to gsl. If the problem persists, verify that you have libgsl0-dev on your library path.

GHC error with System on OS X Lion

I tried to compile and link simple program using ghc, but it failed during linking:
import System (getArgs)
main = do
args <- getArgs
print args
I tried to compile with
% ghc -c -O Main.hs
% ghc -o Main Main.o
ld: warning: could not create compact unwind for .LFB3: non-standard register 5 being saved in prolog
Undefined symbols for architecture i386:
"___stginit_haskell98zm1zi1zi0zi1_System_", referenced from:
___stginit_Main_ in Main.o
ld: symbol(s) not found for architecture i386
collect2: ld returned 1 exit status
zsh: exit 1 ghc -o Main Main.o
However, when compiling with --make:
% ghc --make Main.hs
everything works (besides tons of ld warnings)
Some more informations about environment:
% ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.0.3
From Haskell Platform for Mac OS X 10.6 (Intel, 32 bit GHC)
System: Max OS X Lion 10.7.2
Any ideas what's wrong?
(Btw, I tried to install HP x64 but it failed during installation)
Michael is historically right. With --make, ghc figures out which packages it has to use and link in by itself (unless two installed packages expose the same module name, then it can't figure out which one to use), without --make, you have to tell it. However, as of 7.0, --make is the default mode of ghc, so plain ghc Main.hs is now the same as ghc --make Main.hs. The difference here is the two-step compilation. I don't know the precise details, but the cause is that the module System is in the haskell98 package (a propos, please use the hierarchical modules, getArgs should be imported via System.Environment, as of 7.2, haskell98 can't be used together with base), which by default is not linked in. So ghc -o Main Main.o doesn't find the symbol in the default packages. You'd have to tell it explicitly to look in the haskell98 package, ghc -c -O Main.hs; ghc -package haskell98 -o Main Main.o should work (and it works here, I've tested with 7.0.4 to make sure).
Perhaps it's because you're using something from System? ghc --make probably auto-detects which Haskell libraries it needs to link, and ghc by itself does not.

Haskell Parsec compile error

I've installed Haskell via the pre built installer v6.8.2.
When trying to compile this sample file with GHC
module Main where
import Text.ParserCombinators.Parsec
import System.Environment
main :: IO ()
main = do args <- getArgs
putStrLn ("Hello")
I get the following error:
D:\src\Haskell>ghc -o read read.hs
ghc -o read read.hs
read.o(.text+0x1b5):fake: undefined reference to `__stginit_parseczm2zi1zi0zi0_TextziParserCombinatorsziParsec_'
collect2: ld returned 1 exit status
I have installed Parsec via cabal.
Does anyone have any idea's as to what is wrong?
Try ghc --make -o read read.hs. GHC will take care of linker dependencies.
I'll put out one other way to make this work
ghc -package parsec -o read read.hs
From the ghc documentation
-package P
This option causes the installed package P to be exposed. The package P can be
specified in full with its version number (e.g. network-1.0) or the version number
can be omitted if there is only one version of the package installed. If there are
multiple versions of P installed, then all other versions will become hidden.
The -package P option also causes package P to be linked into the resulting
executable or shared object. Whether a packages' library is linked statically or
dynamically is controlled by the flag pair -static/-dynamic.
see http://www.haskell.org/ghc/docs/latest/html/users_guide/packages.html
According to the Parsec docs (section 1.2.1 Compiling with GHC), you should do this:
When your linking the files together,
you need to tell GHC where it can find
libraries (-L) and to link with the
Parsec library too (-l):
ghc -o myprogram myfile1.o myfile2.o -Lc:\parsec -lparsec
This documentation on the Haskell compiler may help.

How do I create a dynamic library (dylib) with Xcode?

I'm building few command-line utilities in Xcode (plain C, no Cocoa). I want all of them to use my customized version of libpng, and I want to save space by sharing one copy of the library among all executables (I don't mind re-distributing .dylib with them).
Do I need to do some magic to get libpng export symbols?
Does "Link Binary With Libraries" build phase link statically?
Apple's docs mention loading of libraries at run time with dlopen, but how I can make Xcode create executable without complaining about missing symbols?
I think I've figured it out:
libpng wasn't linking properly, because I've built 32/64-bit executables and 32-bit library. Build settings of the library and executables must match.
libpng's config.h needs to have tons of defines like #define FEATURE_XXX_SUPPORTED
"Link Binary With Libraries" build phase handles dynamic libraries just fine, and DYLD_FALLBACK_LIBRARY_PATH environmental variable is neccessary for loading .dylibs from application bundle.
Dynamic linking on Mac OS X, a tiny example
Steps:
create a library libmylib.dylib containing mymod.o
compile and link a "callmymod" which calls it
call mymod from callmymod, using DYLD_LIBRARY_PATH and DYLD_PRINT_LIBRARIES
Problem: you "just" want to create a library for other modules to use.
However there's a daunting pile of programs -- gcc, ld, macosx libtool, dyld --
with zillions of options, some well-rotted compost, and differences between MacOSX and Linux.
There are tons of man pages (I count 7679 + 1358 + 228 + 226 lines in 10.4.11 ppc)
but not much in the way of examples, or programs with a "tell me what you're doing" mode.
(The most important thing in understanding is to make a simplified
OVERVIEW for yourself: draw some pictures, run some small examples,
explain it to someone else).
Background: apple OverviewOfDynamicLibraries,
Wikipedia Dynamic_library
Step 1, create libmylib.dylib --
mymod.c:
#include <stdio.h>
void mymod( int x )
{
printf( "mymod: %d\n", x );
}
gcc -c mymod.c # -> mymod.o
gcc -dynamiclib -current_version 1.0 mymod.o -o libmylib.dylib
# calls libtool with many options -- see man libtool
# -compatibility_version is used by dyld, see also cmpdylib
file libmylib.dylib # Mach-O dynamically linked shared library ppc
otool -L libmylib.dylib # versions, refs /usr/lib/libgcc_s.1.dylib
Step 2, compile and link callmymod --
callmymod.c:
extern void mymod( int x );
int main( int argc, char** argv )
{
mymod( 42 );
}
gcc -c callmymod.c
gcc -v callmymod.o ./libmylib.dylib -o callmymod
# == gcc callmymod.o -dynamic -L. -lmylib
otool -L callmymod # refs libmylib.dylib
nm -gpv callmymod # U undef _mymod: just a reference, not mymod itself
Step 3, run callmymod linking to libmylib.dylib --
export DYLD_PRINT_LIBRARIES=1 # see what dyld does, for ALL programs
./callmymod
dyld: loaded: libmylib.dylib ...
mymod: 42
mv libmylib.dylib /tmp
export DYLD_LIBRARY_PATH=/tmp # dir:dir:...
./callmymod
dyld: loaded: /tmp/libmylib.dylib ...
mymod: 42
unset DYLD_PRINT_LIBRARIES
unset DYLD_LIBRARY_PATH
That ends one tiny example; hope it helps understand the steps.
(If you do this a lot, see GNU Libtool
which is glibtool on macs,
and SCons.)
You probably need to ensure that the dynamic library you build has an exported symbols file that lists what should be exported from the library. It's just a flat list of the symbols, one per line, to export.
Also, when your dynamic library is built, it gets an install name embedded within it which is, by default, the path at which it is built. Subsequently anything that links against it will look for it at the specified path first and only afterwards search a (small) set of default paths described under DYLD_FALLBACK_LIBRARY_PATH in the dyld(1) man page.
If you're going to put this library next to your executables, you should adjust its install name to reference that. Just doing a Google search for "install name" should turn up a ton of information on doing that.
Unfortunately, in my experience Apple's documentation is antiquated, redundant and missing a LOT of common information that you would normally need.
I wrote a bunch of stuff on this on my website where I had to get FMOD (Sound API) to work with my cross platform game that we developed at uni. Its a weird process and I'm surprised that Apple do not add more info on their developer docs.
Unfortunately, as "evil" as Microsoft are, they actually do a much better job of looking after their devs with documentation ( this is coming from an Apple evangelist ).
I think basically, what you are not doing is AFTER you have compiled your .app Bundle. You then need to run a command on the executable binary /MyApp.app/contents/MacOS/MyApp in order to change where the executable looks for its library file. You must create a new build phase that can run a script. I won't explain this process again, I have already done it in depth here:
http://brockwoolf.com/blog/how-to-use-dynamic-libraries-in-xcode-31-using-fmod
Hope this helps.
Are you aware of the Apple reference page Dynamic Library Programming Topics? It should cover most of what you need. Be aware that there a shared libraries that get loaded unconditionally at program startup and dynamically loaded libraries (bundles, IIRC) that are loaded on demand, and the two are somewhat different on MacOS X from the equivalents on Linux or Solaris.

Resources