Search code examples
cgo

How do I build a shared library using Go that calls an undefined c function?


I want to create a shared library, using Go, to be used by a third-party software (STAR-CCM+). That software provides some utility c functions for my code to call, and expects my code to, at a minimum, define a specific function, which the third-party software will call after loading the library.

My problem is Go complains about undefined references for the utility functions:

/tmp/go-build672782048/b001/_x002.o: In function `_cgo_c4b84da031f3_Cfunc_utility':
/tmp/go-build/cgo-gcc-prolog:50: undefined reference to `utility'

How can I compile a shared library that calls a c function that is declared but not defined by my code?

The third-party software provides a header file similar to this:

uclib.h

#ifndef UCLIB_H
#define UCLIB_H

// utility function defined by third party software, declared here
extern void utility(int);

// function expected to exist in .so and defined by user
// this function is expected to call `utility` one or more times
void user_function();

#endif

Working example, c only

To test the interaction with the third party software, I build an example using only c:

usingc.c

#include "uclib.h"

void
user_function()
{
    utility(1);
}

Build:

$ gcc -fPIC -c usingc.c -o usingc.o
$ gcc -shared -o libmine.so usingc.o

This results in a libmine.so that the third party software successfully loads and registers its utility function being called with 1. Note that utility was only declared, never defined, by my code.

Problematic example, Go

I create a simple go module with the header above and two files:

go.mod

module example.com/cgo_mwe

go 1.15

usinggo.go

package main

// #include "uclib.h"
import "C"

//export user_function
func user_function() {
    C.utility(C.int(2))
}

func main() {}

I attempt to build the shared library and observe the error:

$ go build -o libmineg.so -buildmode=c-shared
# example.com/cgo_mwe
/tmp/go-build672782048/b001/_x002.o: In function `_cgo_c4b84da031f3_Cfunc_utility':
/tmp/go-build/cgo-gcc-prolog:50: undefined reference to `utility'
collect2: error: ld returned 1 exit status

Solution

  • The are three possible solutions to allow the linking with the undefined reference. The second two are found in the golang-nuts Google Group:

    1. ignore all undefined references via LDFLAGS with -shared
    // #include "uclib.h"
    // #cgo LDFLAGS: -shared
    import "C"
    
    1. ignore all undefined references via LDFLAGS with -Wl,-unresolved-symbols=ignore-all:
    // #include "uclib.h"
    // #cgo LDFLAGS: -Wl,-unresolved-symbols=ignore-all
    import "C"
    
    1. mark the declared but undefined functions as weak in the header file:
    #pragma weak utility
    // utility function defined by third party software, declared here
    extern void utility(int);
    

    The advantage of #3 is that references not marked weak are still called out as undefined.