Go calls C program and arm platform cross compilation

foreword

One of the advantages of the Go language is that it can call C code, write C code directly in the Go source code, or import an external library of C language. In this way, it can be rewritten where performance encounters bottlenecks, or some functions are missing from Go and third parties, but C language has ready-made libraries that can be used directly.
Below are several ways to demonstrate how Go calls C, and introduce the cross-compilation method ported to the arm platform (the cross-compilation method for other platforms is similar).

Embedded C code

The following code directly embeds the program written in C in the Go program. In Go, a "C" package needs to be introduced immediately after the C code. Define an add function and call it in main.

// main.go
package main

/*
int add(int a, int b)
{
    return a + b;
}
*/
import "C"
import "fmt"

func main() {
    a := C.int(10)
    b := C.int(20)
    val := C.add(a,b)
    fmt.Printf("a+b=%v\n", val)
}

A single file can be run directly using go run main.go.
Cross-compilation:

luxq@luxq-vmpc:go_call_c$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:go_call_c$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=e7912b8c5b526f52c64a9b33c3e9df751ee9b903, not stripped

Standalone C source code file

The content of each file is as follows:

// add.h
#ifndef ADD_H
#define ADD_H

int add(int a, int b);

#endif  /*ADD_H*/

The internal add function defined in add.c

// add.c 
#include "add.h"
int add(int a, int b )
{
    return a+b;
}

Define the external use interface in foo.h

// foo.h
#ifndef FOO_H
#define FOO_H

#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
    extern int Num;
    extern void foo();
    extern int f_add(int a, int b);
#ifdef __cplusplus
}
#endif

#endif  /*FOO_H*/

The interface is implemented in foo.c, and the add function defined in add.h is called at the same time.

// foo.c
#include <stdio.h>
#include "add.h"

int Num = 8;

void foo()
{
    printf("First line.\n");
}

int f_add(int a, int b)
{
    return add(a,b);
}
1. Same level directory

This situation means that C code and Go code are mixed in the same directory, and the function that calls C code in Go.
The directory structure is as follows:

go_call_c/
├── add.c
├── add.h
├── foo.c
├── foo.h
└── main.go

When all source codes are in the same directory, only the header file foo.h that directly calls the interface needs to be introduced in main.go, as follows:

// main.go 
package main

/*
#include "foo.h"
*/
import "C"
import "unsafe"
import "fmt"

func Prin(s string) {
    cs := C.CString(s)
    defer C.free(unsafe.Pointer(cs))
    C.fputs(cs, (*C.FILE)(C.stdout))
    C.fflush((*C.FILE)(C.stdout))
}

func main() {
    fmt.Println("rannum:%x\n", C.random())
    Prin("Hello CC")
    fmt.Println(C.Num)
    C.foo()

    a:=C.int(1)
    b:=C.int(2)
    value:= C.f_add(a,b)
    fmt.Println("a+b=%v\n", value);
}

Since there are multiple files, you need to use go build to compile and then execute.

luxq@luxq-vmpc:go_call_c$go build -o main
luxq@luxq-vmpc:go_call_c$./main 
rannum:%x
 1804289383
Hello CC8
First line.
a+b=3
luxq@luxq-vmpc:go_call_c$

Cross compile:

luxq@luxq-vmpc:go_call_c$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:go_call_c$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=3bd34af6f6e5543cb2867490302ab1ac307c4fae, not stripped
luxq@luxq-vmpc:go_call_c$
2. Different levels of directories

This situation means that the C code and the Go code are not in the same directory, and the function that calls the C code in Go.
The directory structure is as follows:

go_call_c/
├── c_src
│   ├── add.c
│   ├── add.h
│   ├── foo.c
│   └── foo.h
└── main.go

In this case, main.go not only needs to import the header file foo.h that directly calls the interface, but also needs to import related C files, as follows:

// main.go
package main

/*
#include "c_src/foo.h"
#include "c_src/foo.c"
#include "c_src/add.c"
*/
import "C"
import "unsafe"
import "fmt"

func Prin(s string) {
    cs := C.CString(s)
    defer C.free(unsafe.Pointer(cs))
    C.fputs(cs, (*C.FILE)(C.stdout))
    C.fflush((*C.FILE)(C.stdout))
}

func main() {
    fmt.Println("rannum:%x\n", C.random())
    Prin("Hello CC")
    fmt.Println(C.Num)
    C.foo()

    a:=C.int(1)
    b:=C.int(2)
    value:= C.f_add(a,b)
    fmt.Println("a+b=%v\n", value);
}

It is also necessary to use go build to compile and then execute the generated executable file.

luxq@luxq-vmpc:go_call_c$go build -o main
luxq@luxq-vmpc:go_call_c$./main 
rannum:%x
 1804289383
Hello CC8
First line.
a+b=3
luxq@luxq-vmpc:go_call_c$

Cross compile:

luxq@luxq-vmpc:go_call_c$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:go_call_c$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=63ca836b1b931d7dc058039fd4c86a210c347640, not stripped
luxq@luxq-vmpc:go_call_c$

external library form

This method may be the most commonly used method, which is to first compile the C code into a library file, and then reference the library in Go. The
content of each C code file is the same as the above.
The directory structure is as follows:

go_call_c/
├── c_src
│   └── foo.c
│   └── add.h
│   └── add.c
├── include
│   └── foo.h
├── lib
│   └── libfoo.so
└── src
    └── main.go

Compile the files under c_src into lib/libfoo.so (not described), and then reference the library in src/main.go.

// main.go
package main

/*
#cgo CFLAGS : -I../include/
#cgo LDFLAGS : -L../lib -lfoo

#include "foo.h"
*/
import "C"
import "unsafe"
import "fmt"

func Prin(s string) {
    cs := C.CString(s)
    defer C.free(unsafe.Pointer(cs))
    C.fputs(cs, (*C.FILE)(C.stdout))
    C.fflush((*C.FILE)(C.stdout))
}

func main() {
    fmt.Println("rannum:%x\n", C.random())
    Prin("Hello CC")
    fmt.Println(C.Num)
    C.foo()
}

Also compile main.go first and then execute it.

luxq@luxq-vmpc:src$go build -o main
luxq@luxq-vmpc:src$./main 
rannum:%x
 1804289383
Hello CC8
First line.
luxq@luxq-vmpc:src$

Cross-compilation:
When compiling, you need to ensure that the library file is already in the arm platform format.

luxq@luxq-vmpc:src$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:src$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=bae4b9df39d88a81dad2f07281ee17d1065a0549, not stripped
luxq@luxq-vmpc:src$

Summarize

Summarize yourself.

reference

Reference: http://bastengao.com/blog/2017/12/go-cgo-c.html

Guess you like

Origin blog.csdn.net/mynameislu/article/details/80080250