Help us understand the problem. What is going on with this article?

GoからCのライブラリを呼ぶ

More than 3 years have passed since last update.

Go言語でアプリケーションを開発しているときに、どうしても既存のCライブラリを使いたくなる場合があります。
Go言語では簡単にC言語を呼び出せる仕組みが提供されています。
詳しくは cgo などを参照してください。

Go言語側からC言語側(ライブラリ)への参照だけでなく、C言語側からGo言語側を参照することもできますが、今回はGo言語側からOpenSSLを呼び出してみます。

やること

  • Go言語から OpenSSLのライブラリ内にある SHA256 系の関数を使って ABCDEFG という文字列のダイジェスト値を得る。

補足

  • 結果はバイナリなので、Base64した値を表示して確認します(面倒なのでBase64はGoでやります)。
  • ちなみに結果は 6akqLtDVNzKsE7AxonsHGBQjHIYzyfQYRMy6iE1IKxY= です。
  • コマンドラインでやるとしたら、以下の感じです。
$ echo -n 'ABCDEFG' |openssl sha -sha256 -binary |base64
6akqLtDVNzKsE7AxonsHGBQjHIYzyfQYRMy6iE1IKxY=

おためし

とりあえずソース

main.go
package main

// #cgo CFLAGS: -I/usr/local/Cellar/openssl/1.0.2j/include
// #cgo LDFLAGS: -L/usr/local/Cellar/openssl/1.0.2j/lib -lssl -lcrypto
// #include <openssl/sha.h>
import "C"
import "fmt"
import "encoding/base64"

func main() {
  ibuf := []byte { 'A', 'B', 'C', 'D', 'E', 'F', 'G' }
  obuf := make([]byte, C.SHA256_DIGEST_LENGTH)

  ctx := &C.struct_SHA256state_st{}
  C.SHA256_Init(ctx)
  _ = C.SHA256_Update(ctx, C.CBytes(ibuf), C.size_t(len(ibuf)))
  _ = C.SHA256_Final((*C.uchar)(&obuf[0]), ctx)

  b64 := base64.StdEncoding.EncodeToString(obuf)
  fmt.Println(b64)
}

実行

$ go run main.go
6akqLtDVNzKsE7AxonsHGBQjHIYzyfQYRMy6iE1IKxY=

ポイント

import "C" と cgo への命令

// #cgo CFLAGS: -I/usr/local/Cellar/openssl/1.0.2j/include
// #cgo LDFLAGS: -L/usr/local/Cellar/openssl/1.0.2j/lib -lssl -lcrypto
// #include <openssl/sha.h>
import "C"
  • import "C" の上に怪しげなコメントがありますが、これが cgo というツールに渡されます。ですので、ただのコメントではなく、意味があります。今回はOpenSSLのヘッダファイルとライブラリの場所をそれぞれ CFLAGS, LDFLAGS で指定しています。これらの値は cgo がコンパイラに指定する時に使われます。
  • #include <openssl/sha.h> からはC言語のソースになります。ちなみに #include だけでなく、普通にCのコードが書けます。ですので構造体や関数の定義がその場でできます。今回は定義されているものを使うだけでしたので、 sha.h の include しかしていません。

呼び出し

  ctx := &C.struct_SHA256state_st{}
  C.SHA256_Init(ctx)
  _ = C.SHA256_Update(ctx, C.CBytes(ibuf), C.size_t(len(ibuf)))
  _ = C.SHA256_Final((*C.uchar)(&obuf[0]), ctx)
  • ヘッダファイル(今回で言うと sha.h )で定義されている構造体や関数は C.なになに で使えます。今回の場合、 ctx := &C.struct_SHA256state_st{} で、SHA256state_st を使用しています。C.struct_構造体名 となっていますが、構造体や共用体、列挙型にはプリフィクスがつくことになっており、(そのままですが)それぞれ以下の通りです。
    • struct ... C.struct_
    • union ... C.union_
    • enum ... C.enum_
  • C.SHA256_InitC.SHA256_UpdateC.SHA256_Final はライブラリ内の関数呼び出しをしています。ただし、CとGoで型が合わない場合が多いので、Cパッケージが変換用にいくつか関数を用意してくれています。以下は例です。
    • func C.CString(string) *C.char
    • func C.CBytes([]byte) unsafe.Pointer
    • func C.GoString(*C.char) string
    • func C.GoStringN(*C.char, C.int) string
    • func C.GoBytes(unsafe.Pointer, C.int) []byte

まとめ

Go言語からのCライブラリを利用するには以下を行います。

  • import "C" の上にコメントで必要なライブラリの指定コンパイラオプションを指定する。
  • 同じくimport "C"の上にコメントで、必要なCのコード(#include等)を書く。
  • 型の変換をなんとかして呼び出す。
    • 呼び出しは C.関数名
    • 構造体は C.struct_ 、他。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away