Skip to content

Instantly share code, notes, and snippets.

@Papierkorb
Last active March 9, 2024 17:59
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Papierkorb/02d6ba53c28b5035a80bf7695f4706bb to your computer and use it in GitHub Desktop.
Save Papierkorb/02d6ba53c28b5035a80bf7695f4706bb to your computer and use it in GitHub Desktop.
Dynamic library in Crystal

Writing a dynamic library in Crystal, and calling it from C

  • Download all files.
  • Build the Crystal part:
crystal build --release --cross-compile --prelude ./no_main library.cr
              ^ You can leave this out
                        ^ We want to link ourselves!
                                        ^ Use our custom prelude!
  • Link the Crystal part into a shared library. The previous command gives you the line, though we have to modify it a bit:
gcc library.o -o libfoo.so -rdynamic -shared -lpcre -lgc -lpthread /usr/lib/crystal/ext/libcrystal.a -levent -lrt -ldl -L/usr/lib -L/usr/local/lib
                                     ^ Add this to link into a shared lib
              ^ We want a decent name for the shared lib too.
  • Build and link the C program:
gcc -o program program.c -L$PWD -lfoo
                         ^ We stored our lib right 'here'
                                ^ And we want to link to it
  • Run the program:
./program
  • Verify (Linux only):

Using ldd program, we see the dynamic dependencies of our program:

        linux-vdso.so.1 (0x00007ffc56d8c000)
        libfoo.so (0x00007f80846e9000)         <---- Here it is!
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f8084343000)
        libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f80840d0000)
        libgc.so.1 => /usr/lib/libgc.so.1 (0x00007f8083e66000)
        libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f8083c48000)
        libevent-2.1.so.6 => /usr/lib/libevent-2.1.so.6 (0x00007f80839f2000)
        librt.so.1 => /usr/lib/librt.so.1 (0x00007f80837ea000)
        libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f80835e6000)
        libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f80833cf000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8084909000)
        libatomic_ops.so.1 => /usr/lib/libatomic_ops.so.1 (0x00007f80831cc000)
        libcrypto.so.1.1 => /usr/lib/libcrypto.so.1.1 (0x00007f8082d4c000)

Enjoy!

# The function we will call from C. Add more `fun`s as you see fit.
fun hello_crystal : Void
puts "Hello from Crystal!"
end
# Copied from my prelude.cr. Only commented line 50 in this file as change.
# Entries to this file should only be ordered if macros are involved -
# macros need to be defined before they are used.
# A first compiler pass gathers all classes and methods, removing the
# requirement to place these in load order.
#
# When adding new files, use alpha-sort when possible. Make sure
# to also add them to `docs_main.cr` if their content need to
# appear in the API docs.
# This list requires ordered statements
require "lib_c"
require "macros"
require "object"
require "comparable"
require "exception"
require "iterable"
require "iterator"
require "indexable"
require "string"
# Alpha-sorted list
require "array"
require "atomic"
require "bool"
require "box"
require "char"
require "char/reader"
require "class"
require "concurrent"
require "deque"
require "dir"
require "enum"
require "enumerable"
require "env"
require "errno"
require "ext"
require "file"
require "float"
require "gc"
# require "gc/null"
require "gc/boehm"
require "hash"
require "iconv"
require "int"
require "intrinsics"
require "io"
require "kernel"
# require "main" #### We don't want this!
require "math/math"
require "mutex"
require "named_tuple"
require "nil"
require "number"
require "pointer"
require "pretty_print"
require "primitives"
require "proc"
require "process"
require "raise"
require "random"
require "range"
require "reference"
require "reflect"
require "regex"
require "set"
require "signal"
require "slice"
require "static_array"
require "struct"
require "symbol"
require "system"
require "thread"
require "time"
require "tuple"
require "unicode"
require "union"
require "value"
void hello_crystal(); // Manually declare Crystal `fun`
int main() {
hello_crystal(); // Call to Crystal
}
@faustinoaq
Copy link

faustinoaq commented Feb 20, 2018

Hi @Papierkorb Thank you for this gist, can you update the no_main.cr file? Looks like is not working on v0.24.1

Error in foo.cr:1: while requiring "./no_main"

# The function we will call from C.  Add more `fun`s as you see fit.
^

in no_main.cr:68: while requiring "signal"

require "signal"
^

in /usr/lib/crystal/signal.cr:147: undefined method 'restore_blocking_state' for Crystal:Module

fun __crystal_sigfault_handler(sig : LibC::Int, addr : Void*)
^

in /usr/lib/crystal/signal.cr:148: undefined method 'restore_blocking_state' for Crystal:Module

  Crystal.restore_blocking_state
          ^~~~~~~~~~~~~~~~~~~~~~

Oh, this trick also works with C#/Mono as well 😄

using System;
using System.Runtime.InteropServices;

static class Foo 
{
    [DllImport ("libfoo.so")]
    private static extern void hello_crystal();

    public static void Main() 
    {
        hello_crystal();
    }
}

Outputs:

➜  CSharpCrystal mcs Foo.cs
➜  CSharpCrystal ./Foo.exe
Hello from Crystal!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment