cgo의 C 및 Go를 사용한 링크의 뒷면(2)
14275 단어 Go
본문에서 반대로 C의 함수에서 Go를 호출하는 코드, 즉 Go의 함수 export를 C로 호출하는 상황을 처리한다.
하지만 여기서 말하는 것은 결국'Go의 일부 포장을 C로 구현할 때 C코드는 Go의 기능을 활용할 수 있다'는 것이다.C 프로그램에 Go 라이브러리를 삽입하면
-buildmode=c-archive
,-buildmode=c-shared
와 기타는 별도로 처리한다.이번 토크에서 전체 프로그램은 시종일관 Go를 전제로 지난번과 마찬가지로 그 중 일부만 C로 쓰겠다고 구상했다.
샘플 코드
이번에는 다음 4개 문서로 구성된 포장github.com/yugui/cgo-explained/example2을 고려한다.//export
C가 디렉토리goVersion
로 export를 진행하도록 지시합니다.
export_example.gopackage main
import (
"C"
"runtime"
)
//export goVersion
func goVersion() string {
return runtime.Version()
}
use_exported.hvoid print_go_version(void);
use_exported.c#include <stdio.h>
#include "_cgo_export.h"
void print_go_version(void) {
const GoString version = goVersion();
printf("%.*s\n", (int)version.n, version.p);
}
main.gopackage main
// #include "use_exported.h"
import "C"
func main() {
C.print_go_version()
}
exported.go
의 기능은 주로 use_exported.c
에 사용된다.그러나 프로그램 자체가 고로 써야 하기 때문에 고의func main
가 어느 곳에서importuse_exported.c
의 기능이 더 이상 없으면 존재use_exported.c
의 의미가 없다.그래서 main.go
는 이렇다.
원본 코드의 의존 관계는 아래 그림과 같다.
구축 프로세스
구축 과정 자체는import의 경우와 큰 차이가 없다.
package main
import (
"C"
"runtime"
)
//export goVersion
func goVersion() string {
return runtime.Version()
}
void print_go_version(void);
#include <stdio.h>
#include "_cgo_export.h"
void print_go_version(void) {
const GoString version = goVersion();
printf("%.*s\n", (int)version.n, version.p);
}
package main
// #include "use_exported.h"
import "C"
func main() {
C.print_go_version()
}
구축 과정 자체는import의 경우와 큰 차이가 없다.
go tool cgo export_example.go
go tool cgo main.go
gcc -c SOURCES
gcc -o _cgo_.o OBJS
go tool cgo -dynimport ....
go tool compile -o example2.a -pack GO_FILES
gcc -o _all.o OBJS
go tool pack r example2.a _all.o
go tool link -o example2 example2.a
이것도 import의 상황과 대체적으로 같은 구조이지만 다음 두 가지는 다르다.
go tool cgo
에 마운트된 소스 파일이 2개로 늘어남에 따라 생성된 파일이 복잡해짐use_exported.c
에서 온 대상 파일 use_exported.o
이 추가되었습니다.이 파일을 컴파일할 때 go tool cgo
생성된 _cgo_export.h
은 #include
이다.use_exported.c
우선 export의 Go 기능을 이용한 C소스use_exported.c
는 아래와 같다(재대출).
use_exported.c#include <stdio.h>
#include "_cgo_export.h"
void print_go_version(void) {
const GoString version = goVersion();
printf("%.*s\n", (int)version.n, version.p);
}
여기#include
에서 만든 것_cgo_export.h
은 go tool cgo
과export_example.go
에서 생성된 것이다.
_cgo_export.h/* Created by "go tool cgo" - DO NOT EDIT. */
/* (中略) */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
/* (中略) */
typedef signed char GoInt8;
/* (中略) */
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
/* (中略) */
extern GoString goVersion();
이 문서는 주로 세 부분으로 구성되어 있다.
#include <stdio.h>
#include "_cgo_export.h"
void print_go_version(void) {
const GoString version = goVersion();
printf("%.*s\n", (int)version.n, version.p);
}
/* Created by "go tool cgo" - DO NOT EDIT. */
/* (中略) */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
/* (中略) */
typedef signed char GoInt8;
/* (中略) */
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
/* (中略) */
extern GoString goVersion();
main.go
앞의 댓글에 적힌 preamble부의 복사본preamble에
import "C"
같은 것을 쓰면 //#include <stdio.h>
이 부분으로 복사됩니다.선고
#include <stdio.h>
의 Go의 함수에 대응하는 함수 원형//export
을 이용하면 use_exported.c
내의 export_example.go
를 호출할 수 있다.그러나 정확히 말하면 함수 원형 중의 func goVersion() string
와 func goVersion() string
는 같지 않다.그건 다음 절에서 봐요.그룹 코드
먼저 C에서 볼 수 있듯이 goVersion()
생성된 goVersion()
에서 볼 수 있다.매개변수를 적절하게 채워 Go의 세계로 넘기기 위한 그룹 코드다.
_cgo_export.c/* Created by cgo - DO NOT EDIT. */
#include "_cgo_export.h"
extern void crosscall2(void (*fn)(void *, int), void *, int);
extern void _cgo_wait_runtime_init_done();
extern void _cgoexp_5e3e4b09c83e_goVersion(void *, int);
GoString goVersion()
{
_cgo_wait_runtime_init_done();
struct {
GoString r0;
} __attribute__((__packed__)) a;
crosscall2(_cgoexp_5e3e4b09c83e_goVersion, &a, 16);
return a.r0;
}
_cgo_export.c
의 실질은 crosscall2
레지스터 회피 등이다.runtime.cgo.crosscall2
이름과 같이 _cgo_wait_runtime_init_done
봉인된 초기화가 끝나기 전에 블록을 진행한다.
그리고 여기 호출된runtime
은_cgoexp_5e3e4b09c83e_goVersion
에 있습니다.
_cgo_gotypes.go/* (略) */
//go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr)
/* (略) */
//go:cgo_export_dynamic goVersion
//go:linkname _cgoexp_5e3e4b09c83e_goVersion _cgoexp_5e3e4b09c83e_goVersion
//go:cgo_export_static _cgoexp_5e3e4b09c83e_goVersion
//go:nosplit
//go:norace
func _cgoexp_5e3e4b09c83e_goVersion(a unsafe.Pointer, n int32) {
fn := _cgoexpwrap_5e3e4b09c83e_goVersion
_cgo_runtime_cgocallback(**(**unsafe.Pointer)(unsafe.Pointer(&fn)), a, uintptr(n));
}
func _cgoexpwrap_5e3e4b09c83e_goVersion() (r0 string) {
defer func() {
_cgoCheckResult(r0)
}()
return goVersion()
}
_cgo_gotypes.go
창고 주위를 조정하는 동시에 최종 호출runtime.cgocallback
.
C의 함수goVersion
에서 상기 그룹 코드 Go가 정의한 함수goVersion
를 호출할 수 있습니다.
총결산
export의 경우 import의 상황과 구축 가설도 큰 차이가 없다.import의 경우 할애print_go_version
만 중요해진다.
실행할 때 _cgo_export.[ch]
와 _cgo_export.c
를 통해 각각 정의된 두 단계의 그룹 코드를 C의 세계에서 Go의 세계로 옮기는 것을 제어한다.
Reference
이 문제에 관하여(cgo의 C 및 Go를 사용한 링크의 뒷면(2)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/yugui/items/cc490d080e0297251090
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
/* Created by cgo - DO NOT EDIT. */
#include "_cgo_export.h"
extern void crosscall2(void (*fn)(void *, int), void *, int);
extern void _cgo_wait_runtime_init_done();
extern void _cgoexp_5e3e4b09c83e_goVersion(void *, int);
GoString goVersion()
{
_cgo_wait_runtime_init_done();
struct {
GoString r0;
} __attribute__((__packed__)) a;
crosscall2(_cgoexp_5e3e4b09c83e_goVersion, &a, 16);
return a.r0;
}
/* (略) */
//go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr)
/* (略) */
//go:cgo_export_dynamic goVersion
//go:linkname _cgoexp_5e3e4b09c83e_goVersion _cgoexp_5e3e4b09c83e_goVersion
//go:cgo_export_static _cgoexp_5e3e4b09c83e_goVersion
//go:nosplit
//go:norace
func _cgoexp_5e3e4b09c83e_goVersion(a unsafe.Pointer, n int32) {
fn := _cgoexpwrap_5e3e4b09c83e_goVersion
_cgo_runtime_cgocallback(**(**unsafe.Pointer)(unsafe.Pointer(&fn)), a, uintptr(n));
}
func _cgoexpwrap_5e3e4b09c83e_goVersion() (r0 string) {
defer func() {
_cgoCheckResult(r0)
}()
return goVersion()
}
export의 경우 import의 상황과 구축 가설도 큰 차이가 없다.import의 경우 할애
print_go_version
만 중요해진다.실행할 때
_cgo_export.[ch]
와 _cgo_export.c
를 통해 각각 정의된 두 단계의 그룹 코드를 C의 세계에서 Go의 세계로 옮기는 것을 제어한다.
Reference
이 문제에 관하여(cgo의 C 및 Go를 사용한 링크의 뒷면(2)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/yugui/items/cc490d080e0297251090텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)