以下の内容はhttps://devlights.hatenablog.com/entry/2025/01/14/073000より取得しました。


Goメモ-530 (cgoメモ-21)(CGOヘッダーで指定出来るCFLAGS, LDFLAGS)

関連記事

Goメモ-502 (cgoメモ-01)(cgoヘッダ) - いろいろ備忘録日記

Goメモ-506 (cgoメモ-02)(cgoヘッダ) - いろいろ備忘録日記

Goメモ-507 (cgoメモ-03)(C.int) - いろいろ備忘録日記

Goメモ-508 (cgoメモ-04)(C言語の構造体) - いろいろ備忘録日記

Goメモ-509 (cgoメモ-05)(C.CString)(Cの文字列) - いろいろ備忘録日記

Goメモ-510 (cgoメモ-06)(C.GoString)(Cの文字列をGoの文字列へ) - いろいろ備忘録日記

Goメモ-511 (cgoメモ-07)(C.CBytes)([]byteをCのバイト列に) - いろいろ備忘録日記

Goメモ-512 (cgoメモ-08)(C.GoBytes)(Cのバイト列をGoの[]byteへ) - いろいろ備忘録日記

Goメモ-514 (cgoメモ-09)(C.GoStringN)(C.GoStringのサイズ指定版) - いろいろ備忘録日記

Goメモ-515 (cgoメモ-10)([]byteを(void *)へ変換) - いろいろ備忘録日記

Goメモ-516 (cgoメモ-11)([]byteを(char *)へ変換) - いろいろ備忘録日記

Goメモ-518 (cgoメモ-12)(Cのmallocをcgo経由で呼び出し) - いろいろ備忘録日記

Goメモ-519 (cgoメモ-13)(ポインタ演算) - いろいろ備忘録日記

Goメモ-520 (cgoメモ-14)(Goの関数をCの世界に公開 (export)) - いろいろ備忘録日記

Goメモ-522 (cgoメモ-15)(Goでsoファイルを作成してC言語から呼び出し) - いろいろ備忘録日記

Goメモ-525 (cgoメモ-16)(C側にて関数ポインタを引数に要求する関数にGo側で定義した関数を設定) - いろいろ備忘録日記

Goメモ-526 (cgoメモ-17)(cgoとdlopen関数を使って既存ライブラリの呼び出しをフックする) - いろいろ備忘録日記

Goメモ-527 (cgoメモ-18)(cgoを利用している場合のinit関数について) - いろいろ備忘録日記

Goメモ-528 (cgoメモ-19)(cgoを利用して作成したsoファイル経由でのinit関数の呼び出し) - いろいろ備忘録日記

Goメモ-529 (cgoメモ-20)(C言語のNULLをcgoから渡す) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

今回から複数回に渡って cgo についてメモしていこうと思います。

cgo は、文字通りGoからCにアクセスすることが出来るようになるものなのですが、とても便利な反面、結構クセが強いのでメモでも残しておかないとすぐ頭から消えてしまいそうだなって思いました。

Cgo is not Go

という格言があったりするので、Go界隈で標準で推奨されていない技術かもしれません。が、実務ではC言語で作成されたライブラリなどは山のようにあります。んで、プロジェクトの方針でGoで作り直すことも出来ない場合も多々あります。そのような場合に非常に便利です。

これからのサンプルは以下のリポジトリにアップしてありますので、良ければご参考ください。

github.com

今回は cgoヘッダーで指定できる CFLAGS, LDFLAGS について。C言語ではお馴染みのフラグですね。

上記のフラグは cgoヘッダー でも指定できます。

今回のサンプルはちょっとファイル数が多いです。C言語でsoファイルを作成して、それをC言語アプリとcgoを使ったGoアプリから呼び出して試しています。

ファイル構成

今回のサンプルは以下のような構成としています。

$ tree .
.
├── capp
│   ├── main.c
│   └── Makefile
├── goapp
│   └── main.go
├── lib
│   ├── lib.c
│   ├── lib.h
│   └── Makefile
├── README.md
└── Taskfile.yml

3 directories, 8 files
  • capp
    • C言語のアプリ
  • goapp
    • Go言語のアプリ
  • lib
    • 共有ライブラリ

共有ライブラリ

lib.h

#ifndef __LIB_H
#define __LIB_H

extern void myPrint(const char *msg);

#endif /* __LIB_H */

lib.c

#include <stdio.h>

void myPrint(const char *msg) {
    printf("[C ] %s\n", msg);
}

Makefile

DESTDIR = .
PROGRAM = libmylib.so
SRCS    = $(shell find $(PWD) -name "*.c" -type f)
OBJS    = $(SRCS:%.c=%.o)
CC      = gcc
INCDIRS = 
CFLAGS  = -g -O0 -Wall -Wextra -std=c17
LDFLAGS =
LDLIBS  =

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@ $(INCDIRS)

$(PROGRAM): $(OBJS)
    $(CC) $(CFLAGS) -fPIC -shared $(LDFLAGS) -o $(DESTDIR)/$(PROGRAM) $(OBJS) $(LDLIBS)

.PHONY: build
build: $(PROGRAM)

.PHONY: clean
clean:
    $(RM) $(OBJS) $(DESTDIR)/$(PROGRAM)

C言語アプリ

main.c

#include "lib.h"

int main() {
    myPrint("hello world");
}

Makefile

DESTDIR = .
PROGRAM = cApp
SRCS    = $(shell find $(PWD) -name "*.c" -type f)
OBJS    = $(SRCS:%.c=%.o)
CC      = gcc
INCDIRS = -I../lib
CFLAGS  = -g -O0 -Wall -Wextra -std=c17
LDFLAGS = -L../lib
LDLIBS  = -lmylib

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@ $(INCDIRS)

$(PROGRAM): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $(DESTDIR)/$(PROGRAM) $(OBJS) $(LDLIBS)

.PHONY: build
build: $(PROGRAM)

.PHONY: clean
clean:
    $(RM) $(OBJS) $(DESTDIR)/$(PROGRAM)

Go言語アプリ (cgo)

main.go

package main

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

#include <stdlib.h>
#include "lib.h"
*/
import "C"
import "unsafe"

func main() {
    var (
        goStr = "hello world"
        cStr  = C.CString(goStr)
    )
    defer C.free(unsafe.Pointer(cStr))

    C.myPrint(cStr)
}

Taskfile.yml

# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - task: build-lib
      - task: build-capp
      - task: build-goapp
      - task: run-capp
      - task: run-goapp
      - task: list-symbols
      - task: ldd
  clean:
    cmds:
      - make -C lib clean
      - make -C capp clean
      - rm -f goapp/goApp
  build-lib:
    dir: lib
    cmds:
      - make build
  build-capp:
    dir: capp
    cmds:
      - make build
  build-goapp:
    dir: goapp
    cmds:
      - go build -o goApp
  run-capp:
    dir: capp
    cmds:
      - LD_LIBRARY_PATH=../lib ./cApp
  run-goapp:
    dir: goapp
    cmds:
      - LD_LIBRARY_PATH=../lib ./goApp
  list-symbols:
    cmds:
      - nm --extern-only capp/cApp   | grep -E ' [T|U] (main|myPrint)'
      - nm --extern-only goapp/goApp | grep -E ' [T|U] (main|myPrint)'
  ldd:
    cmds:
      - LD_LIBRARY_PATH=lib ldd capp/cApp
      - ldd goapp/goApp

実行

$ task
task: [build-lib] make build
gcc -g -O0 -Wall -Wextra -std=c17 -c /workspace/try-golang-cgo/20.C_CFLAGS_LDFLAGS/lib/lib.c -o /workspace/try-golang-cgo/20.C_CFLAGS_LDFLAGS/lib/lib.o 
gcc -g -O0 -Wall -Wextra -std=c17 -fPIC -shared  -o ./libmylib.so /workspace/try-golang-cgo/20.C_CFLAGS_LDFLAGS/lib/lib.o 


task: [build-capp] make build
gcc -g -O0 -Wall -Wextra -std=c17 -c /workspace/try-golang-cgo/20.C_CFLAGS_LDFLAGS/capp/main.c -o /workspace/try-golang-cgo/20.C_CFLAGS_LDFLAGS/capp/main.o 
gcc -g -O0 -Wall -Wextra -std=c17 -L../lib -o ./cApp /workspace/try-golang-cgo/20.C_CFLAGS_LDFLAGS/capp/main.o -lmylib


task: [build-goapp] go build -o goApp


task: [run-capp] LD_LIBRARY_PATH=../lib ./cApp
[C ] hello world


task: [run-goapp] LD_LIBRARY_PATH=../lib ./goApp
[C ] hello world


task: [list-symbols] nm --extern-only capp/cApp   | grep -E ' [T|U] (main|myPrint)'
0000000000001149 T main
                 U myPrint


task: [list-symbols] nm --extern-only goapp/goApp | grep -E ' [T|U] (main|myPrint)'
00000000004642a0 T main
                 U myPrint


task: [ldd] LD_LIBRARY_PATH=lib ldd capp/cApp
        linux-vdso.so.1 (0x00007ffffa77c000)
        libmylib.so => lib/libmylib.so (0x00007f5b71474000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5b71244000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f5b71480000)


task: [ldd] LD_LIBRARY_PATH=lib ldd goapp/goApp
        linux-vdso.so.1 (0x00007fffd0998000)
        libmylib.so => lib/libmylib.so (0x00007f776fe38000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f776fc08000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f776fe3f000)

ちゃんと、 ビルド時に CFLAGSLDFLAGS の指定が反映されてビルドされていますね。

参考情報

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。




以上の内容はhttps://devlights.hatenablog.com/entry/2025/01/14/073000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14