Search code examples
goluajitmusl

Build static binary with cgo, LuaJIT and musl


After reading Statically compiled Go programs, always, even with cgo, using musl I'm trying to use described method to statically link LuaJIT.

Go code I try to build:

package main

import "github.com/aarzilli/golua/lua"
import "fmt"

func test(L *lua.State) int {
    fmt.Println("hello world! from go!")
    return 0
}

func test2(L *lua.State) int {
    arg := L.CheckInteger(-1)
    argfrombottom := L.CheckInteger(1)
    fmt.Print("test2 arg: ")
    fmt.Println(arg)
    fmt.Print("from bottom: ")
    fmt.Println(argfrombottom)
    return 0
}

func main() {
    L := lua.NewState()
    defer L.Close()
    L.OpenLibs()

    L.GetField(lua.LUA_GLOBALSINDEX, "print")
    L.PushString("Hello World!")
    L.Call(1, 0)

    L.PushGoFunction(test)
    L.PushGoFunction(test)
    L.PushGoFunction(test)
    L.PushGoFunction(test)

    L.PushGoFunction(test2)
    L.PushInteger(42)
    L.Call(1, 0)

    L.Call(0, 0)
    L.Call(0, 0)
    L.Call(0, 0)

    // this will fail as we didn't register test2 function
    err := L.DoString("test2(42)")

    fmt.Printf("Ciao %v\n", err)
}

Build command with parameters and output:

$ CC="/usr/local/musl/bin/musl-gcc" go build --ldflags '-linkmode external -extldflags "-static"' basic.go
# command-line-arguments
/usr/local/go/pkg/tool/linux_amd64/link: running /usr/local/musl/bin/musl-gcc failed: exit status 1
/usr/bin/ld: cannot find -lluajit-5.1
collect2: error: ld returned 1 exit status

Using LD_DEBUG=all I can get more information (8000+ lines).

My question: what is the problem and how to solve it? I think there can be some hint here but I can't quite grasp it.

I took following steps:

1. Build static LuaJIT library with musl.

$ make STATIC_CC="/usr/local/musl/bin/musl-gcc" CCOPT="-static -fPIC" BUILDMODE="static"
...
==== Successfully built LuaJIT 2.0.4 ====

2. Build dynamic LuaJIT library with musl.

$ make DYNAMIC_CC="/usr/local/musl/bin/musl-gcc" BUILDMODE="dynamic"
...
==== Successfully built LuaJIT 2.0.4 ====

3. Check build.

$ find . -iname *.a -o -iname *.so
./src/libluajit.a
./src/libluajit.so

4. Install it.

$ sudo make install
...
==== Successfully installed LuaJIT 2.0.4 to /usr/local ====

5. Check installation.

$ pkg-config luajit --cflags
-I/usr/include/luajit-2.0
$ pkg-config luajit --libs
-lluajit-5.1

6. Modify golua lua.go file to use hardcoded cgo parameters.

I had problems overriding those values so I simply modify source code.

old C comment with cgo parameters:

/*
#cgo CFLAGS: -I ${SRCDIR}/lua
#cgo llua LDFLAGS: -llua
#cgo luaa LDFLAGS: -llua -lm -ldl
#cgo linux,!llua,!luaa LDFLAGS: -llua5.1
#cgo darwin,!luaa pkg-config: lua5.1
#cgo freebsd,!luaa LDFLAGS: -llua-5.1
#cgo windows,!llua LDFLAGS: -L${SRCDIR} -llua -lmingwex -lmingw32
#include <lua.h>
#include <stdlib.h>
#include "golua.h"
*/
import "C"

new:

/*
#cgo CFLAGS: -I/usr/include/luajit-2.0 -I${SRCDIR}/lua
#cgo LDFLAGS: -lluajit-5.1
#include <lua.h>
#include <stdlib.h>
#include "golua.h"
*/
import "C"

7. Build some example

As shown at the beggining of question.

Where LuaJIT got installed:

$ find / -iname libluajit* 2> /dev/null
/usr/local/lib/libluajit-5.1.so.2
/usr/local/lib/libluajit-5.1.a
/usr/local/lib/libluajit-5.1.so
/usr/local/lib/libluajit-5.1.so.2.0.4
/usr/lib/libluajit-5.1.so.2.0.5
/usr/lib/libluajit-5.1.so.2
/usr/lib/libluajit-5.1.a
/usr/lib/libluajit-5.1.so
/usr/lib/libluajit.s

EDIT 1

I've followed @putu's comment and changed #cgo LDFLAGS to

#cgo LDFLAGS: -L/usr/local/lib -lluajit-5.1

now I have

$ CC="/usr/local/musl/bin/musl-gcc" go build --ldflags '-linkmode external -extldflags "-static -fPIC"' basic.go
# command-line-arguments
/usr/local/go/pkg/tool/linux_amd64/link: running /usr/local/musl/bin/musl-gcc failed: exit status 1
/tmp/go-link-916770907/000000.o: In function `printf':
/usr/include/x86_64-linux-gnu/bits/stdio2.h:104: undefined reference to `__printf_chk'
collect2: error: ld returned 1 exit status

Solution

  • @putu pointed me to the right direction, all I had to do was tune CFLAGS to include musl.

    Cleaned up C comment in lua.go:

    /*
    #cgo CFLAGS: -I/usr/include/luajit-2.0 -I/usr/local/musl/include/
    #cgo LDFLAGS: -L/usr/local/lib -lluajit-5.1
    #include <lua.h>
    #include <stdlib.h>
    #include "golua.h"
    */
    import "C"
    

    Looks like everything builds just fine:

    $ CC="/usr/local/musl/bin/musl-gcc" go build --ldflags '-linkmode external -extldflags "-static"' basic.go
    $ file basic
    basic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
    $ ./basic 
    Hello World!
    test2 arg: 42
    from bottom: 42
    hello world! from go!
    hello world! from go!
    hello world! from go!
    Ciao [string "test2(42)"]:1: attempt to call global 'test2' (a nil value)