Go golang Learning 7: Compiled as a library that can be invoked by C

Keywords: C Go

Explain

When you compile code using the go build or go install commands, you can use the -buildmode l to specify what file to generate.

go build -buildmode=<mode>
perhaps
go install -buildmode=<mode>

Use go help buildmode l to view all supported buildmodel options.

        -buildmode=archive
                Build the listed non-main packages into .a files. Packages named
                main are ignored.

        -buildmode=c-archive
                Build the listed main package, plus all packages it imports,
                into a C archive file. The only callable symbols will be those
                functions exported using a cgo //export comment. Requires
                exactly one main package to be listed.

        -buildmode=c-shared
                Build the listed main package, plus all packages it imports,
                into a C shared library. The only callable symbols will
                be those functions exported using a cgo //export comment.
                Requires exactly one main package to be listed.

        -buildmode=default
                Listed main packages are built into executables and listed
                non-main packages are built into .a files (the default
                behavior).

        -buildmode=shared
                Combine all the listed non-main packages into a single shared
                library that will be used when building with the -linkshared
                option. Packages named main are ignored.

        -buildmode=exe
                Build the listed main packages and everything they import into
                executables. Packages not named main are ignored.

        -buildmode=pie
                Build the listed main packages and everything they import into
                position independent executables (PIE). Packages not named
                main are ignored.

        -buildmode=plugin
                Build the listed main packages, plus all packages that they
                import, into a Go plugin. Packages not named main are ignored.

Under the classification instructions:

  • -buildmode=shared is used to generate libraries that are used during go compilation and compilation, conceptually similar to dynamic libraries in the c language. The option-linkshared is required to compile go files using these libraries.

  • -buildmode=c-archive and-buildmode=c-shared are used to generate libraries and header files for C, corresponding to static libraries and dynamic libraries, respectively.

  • -buildmode=plugin is used to generate dynamic libraries that can be loaded while the go language is running (currently only loaded, not uninstalled, can they be uninstalled by destroying the protocol?)

Several other options are temporarily unclear.

Compile go script to C language library

Compiling go source code into a C language library requires the following conditions

  1. Compile options using -buildmode=c-archive or -buildmode=c-shared
  2. main package is required in compilation source
  3. Source must import "C"
  4. The export symbol is in the main package with //export NAME on the previous line

Since the exported symbols can only be located in the main package, the main() function must be defined, but it can be empty. Moreover, the parameters and return value types of the exported function can only use the golang base type, and complex types such as structs inside the go language cannot be used.

Now define a simple go file to generate a library for the C language

package main

import (
    "C"
	"fmt"
)

//export hello 
func hello(num int32, name string, size float32) uint8 {
   fmt.Printf("Hello, world, %v, %v, %v\n", num, name, size)
   return 0
}

func main() {
	fmt.Printf("Hello, in main.\n")
	hello(1, "Yuan", 3.14)
}

Under Direct Run Test:

$ go run main.go
Hello, in main.
Hello, world, 1, Yuan, 3.14

Now generate libraries that the C language can call:

$ go build -buildmode=c-archive -o hello.a  main.go
$ ls
go.mod  hello.a  hello.h  main.go

As you can see, two files have been generated: hello.a and hello.h. Partners in C language development are certainly new.

go generated header file

Let's see what's in the header file generated in the last step:

/* Code generated by cmd/cgo; DO NOT EDIT. */

/* package command-line-arguments */


#line 1 "cgo-builtin-export-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#endif

/* Start of preamble from import "C" comments.  */




/* End of preamble from import "C" comments.  */


/* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;

/*
  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue.  */

#ifdef __cplusplus
extern "C" {
#endif

extern GoUint8 hello(GoInt32 num, GoString name, GoFloat32 size);

#ifdef __cplusplus
}
#endif

You can see that the basic types in the go language have been redefined by typedef, where GoString is slightly more complex and is designated as a structure with pointers and lengths:

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif

Calling the golang-generated Library in C

Now write a simple C file that calls the hello() function from the library you just generated.

#include <stdio.h>

#include "hello.h"

int main()
{
    GoInt32 num = 3;
    GoFloat32 size = 3.14;
    GoString name = {0};
    name.p = "Yuan";
    name.n = 4; //Length of string above
    
    GoUint8 ret = hello(num, name, size);
    printf("ret=%d\n", ret);
    
    return 0;
}

Now compile and run:

$ mv hello.a libhello.a
$ gcc hello_test_go.c  -L ./  -l  hello -lpthread
$ ./a.out
Hello, world, 3, Yuan, 3.14
ret=0

As you can see, the functions in the go language run perfectly.

Reference material

Golang's construction

golang plug-in system

C-source archive mode (c-archive) static library mode for Go language compilation mode

Posted by maybl8r03 on Sun, 26 Sep 2021 09:09:44 -0700