GoからCの関数をよぶ(でけた)

GoからCの関数を呼ぶためのメモ。でけた。dylibでもいいらしい。


GoからCの関数を呼ぶためには、呼びたいソース中に以下のものを含める必要がある

  • 必要なCのヘッダファイルに対する#include
  • "C"パッケージのimport
  • 関数呼び出し

呼びたいCの関数が定義されているヘッダファイルは以下のようにコメントアウトしてソースの先頭部分に記述する

     1	package main
     2	
     3	// #include<stdio.h>

次に"C"パッケージをインポートすることで以下に述べる関数呼び出しを使えるようにする

import "C"

上記の"C"パッケージのインポートにより、C側の任意の関数をC.xxx(xxxは関数名)として呼び出すことができる

     6	func main(){
     7		p := C.CString("hello");  
     8		C.puts(p);     //関数呼び出し
     9	}

これを実行可能なようにコンパイルする。未完なのであとでしらべて補完すること
以上のようなソースファイルprint.goを作成したら

$ cgo print.go

以上のようにこのファイルに対してコマンドを打つと以下のように4つほど新しくファイルができる

$ ls
print.cgo1.go print.cgo2.go print.cgo3.c print.cgo4.c print.go

それぞれの中身は

  • print.cgo1.go
// Created by cgo - DO NOT EDIT
//line print.go:1
package main

// #include<stdio.h>

func main() {
	p := _C_CString("hello");
	_C_puts(p);
}
  • print.cgo2.go
// Created by cgo - DO NOT EDIT
package main

import "unsafe"

type _ unsafe.Pointer

type _C_int int32
type _C_char int8
type _C_void [0]byte
func _C_puts(*_C_char) _C_int
func _C_CString(string) *_C_char
  • print.cgo3.c
#include "runtime.h"
#include "cgocall.h"

#pragma dynld initcgo initcgo "/Users/enukane/Development/others/go/hg//pkg/darwin_386/libcgo.so"
#pragma dynld libcgo_thread_start libcgo_thread_start "/Users/enukane/Development/others/go/hg//pkg/darwin_386/libcgo.so"
#pragma dynld _cgo_malloc _cgo_malloc "/Users/enukane/Development/others/go/hg//pkg/darwin_386/libcgo.so"
#pragma dynld _cgo_free free "/Users/enukane/Development/others/go/hg//pkg/darwin_386/libcgo.so"

void
main&#183;_C_GoString(int8 *p, String s)
{
	s = gostring((byte*)p);
	FLUSH(&s);
}

void
main&#183;_C_CString(String s, int8 *p)
{
	p = cmalloc(s.len+1);
	mcpy((byte*)p, s.str, s.len);
	p[s.len] = 0;
	FLUSH(&p);
}

#pragma dynld _cgo_puts _cgo_puts "/Users/enukane/Development/others/go/hg//pkg/darwin_386/main_print.so"
void (*_cgo_puts)(void*);

void
main&#183;_C_puts(struct{uint8 x[8];}p)
{
	cgocall(_cgo_puts, &p);
}
  • print.cgo4.c
#include<stdio.h>

typedef struct { char *p; int n; } _GoString_;
_GoString_ GoString(char *p);
char *CString(_GoString_);


// Usual nonsense: if x and y are not equal, the type will be invalid
// (have a negative array count) and an inscrutable error will come
// out of the compiler and hopefully mention "name".
#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];

// Check at compile time that the sizes we use match our expectations.
#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n)

__cgo_size_assert(char, 1)
__cgo_size_assert(short, 2)
__cgo_size_assert(int, 4)
typedef long long __cgo_long_long;
__cgo_size_assert(__cgo_long_long, 8)
__cgo_size_assert(float, 4)
__cgo_size_assert(double, 8)

void
_cgo_puts(void *v)
{
	struct {
		char* p0;
		int r;
	} *a = v;
	a->r = puts(a->p0);
}

print.cgo1.goがメインの処理部、print.cgo4.cがCの関数を実際に呼ぶ部分で、残り2つはその間を埋めるためのもの?

現状実行までこぎ着けてないがコンパイルっぽいところまでできたのでそのログ下

$ 8g print.cgo1.go print.cgo2.go
$ ls
print.cgo1.8 print.cgo1.go print.cgo2.go print.cgo3.c print.cgo4.c print.go
$ 8g print.cgo2.go
$ ls
print.cgo1.8 print.cgo1.go print.cgo2.8 print.cgo2.go print.cgo3.c print.cgo4.c print.go
$ 8c -I$GOROOT/src/pkg/runtime print.cgo3.c
$ ls
print.cgo1.8 print.cgo1.go print.cgo2.8 print.cgo2.go print.cgo3.8 print.cgo3.c print.cgo4.c print.go
$ 8l print.cgo1.8 print.cgo2.8 print.cgo3.8
cassandra:print enukane$ ls
8.out print.cgo1.8 print.cgo1.go print.cgo2.8 print.cgo2.go print.cgo3.8 print.cgo3.c print.cgo4.c print.go

これで実行ファイル8.outが出力されるが、実行してみるとやはりprint.cgo4.cで実現している関数呼び出しの部分がないので

$ ./8.out
dyld: Library not loaded: $GOROOT//pkg/darwin_386/main_print.so
Referenced from: ./8.out
Reason: image not found
Trace/BPT trap

おそらく
#pragma dynld _cgo_puts _cgo_puts "/Users/enukane/Development/others/go/hg//pkg/darwin_386/main_print.so"
この部分で怒られているっぽい

共有ライブラリとしてprint.cgo4.cをコンパイル&配置すればいいんかな?
この場合、macのdylibでもいいんだろうか





[追記:11/12]
コンパイル->実行までできたので記載
print.cgo4.cについては以下のようにする
まず、オブジェクトファイルに

cc -c print.cgo4.c

次にこのオブジェクトファイルから共有ライブラリを作る。Macなので-dynamiclibオプション付けで作成

$ cc -o libprint.dylib -dynamiclib print.cgo4.o

このlibprint.dylibを適切な位置に適切な名前で配置する必要がある。
これは先ほどライブラリなしで8.outを実行したときに怒られた、main_print.soの位置に置く。

$ mv libprint.dylib $GOROOT/pkg/darwin_386/main_print.so

これで8.outを実行すると

$ ./8.out
hello

[まとめ]
cgoを使うとソースから実行ファイルを生成するためのコード3つと呼び出すライブラリのためのコードが1つ出てくる
今回の場合は

  • print.cgo1.go, print.cgo2.go, print.cgo3.c

8.out(実行形式)を生成するためのソース。8g, 8cでコンパイル後, 8lでリンクする。

  • print.cgo4.c

print.cgo3.cに記載されているライブラリ(main_print.so)を生成するためのコード。普通にccなりでコンパイルして共有ライブラリを作ればok