Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Interfacing C libraries with GO

Interfacing C libraries with GO

The slide I presented in OSDC.TW 2014

Cheng-Lung Sung

April 11, 2014
Tweet

More Decks by Cheng-Lung Sung

Other Decks in Programming

Transcript

  1. About me A software developer, half-retired FreeBSD ports committer. Previously

    a start-up co-founder. 腸 胃炎,所以 osdc.tw 的名產:美⻝⾷食..... Current working in ... coding ... Interfacing C libraries with GO
  2. What I will not talk about... Introduction to GO Interfacing

    C++ libraries with GO GO interface to C++ code, or vice versa. Advance cgo topics such as DLL-linking with cgo We're hiring 逐條審查(?) Interfacing C libraries with GO
  3. What I will talk about... What is cgo Why use

    cgo How to use cgo hello world examples using cgo Some openssl examples (Heartbleed Bug (http://heartbleed.com) 正夯!!!) Interfacing C libraries with GO
  4. Introduction to cgo Cgo enables the creation of Go packages

    that call C code. Ability to use existing C code Tool to generate code to access C library functions and global variables Interfacing C libraries with GO
  5. C++ The link to the Go FAQ: Do Go programs

    link with C/C++ programs (http://golang.org/doc/go_faq.html#Do_Go_programs_link_with_Cpp_programs) Interfacing C libraries with GO
  6. If you need to work with C++ Use SWIG 22.1

    Overview Go is a compiled language, not a scripting language. However, it does not support direct calling of functions written in C/C++. The cgo program may be used to generate wrappers to call C code from Go, but there is no convenient way to call C++ code. SWIG fills this gap. There are (at least) two different Go compilers. One is the gc compiler, normally invoked under the names 6g, 8g, or 5g. The other is the gccgo compiler, which is a frontend to the gcc compiler suite. The interface to C/C++ code is completely different for the two Go compilers. SWIG supports both, selected by a command line option. Reference SWIG and Go (http://www.swig.org/Doc2.0/Go.html) Interfacing C libraries with GO
  7. How to use cgo call C from GO $ go

    run foo.go $ go build call GO from C $ go build; $ ./<package_name> Interfacing C libraries with GO
  8. A typical `Hello world` example in Go 講 Go 的東⻄西,就是要⽤用

    Go present 啦!!! $ present 2014/04/11 14:55:59 Open your web browser and visit http://127.0.0.1:3999/ package main import "fmt" func main() { fmt.Println("Hello World") } Run Interfacing C libraries with GO
  9. call C from GO create a new .go embed the

    C code block you want to interface as comment block following with import "C" write some code $ go build xxxx.go run it $ ./xxxx Interfacing C libraries with GO
  10. #cgo directives In .go: `CFLAGS`, `CPPFLAGS`, `CXXFLAGS`, `LDFLAGS` // #cgo

    CFLAGS: -DPNG_DEBUG=1 // #cgo amd64 386 CFLAGS: -DX86=1 // #cgo LDFLAGS: -lpng If using makefile or ENV `CGO_CFLAGS`, `CGO_CPPFLAGS`, `CGO_CXXFLAGS`, `CGO_LDFLAGS` Interfacing C libraries with GO
  11. call C from GO package main // #include <stdio.h> //

    #include <stdlib.h> import "C" import "unsafe" func main() { str := C.CString("Hello, world!\n") defer C.free(unsafe.Pointer(str)) C.printf(str) } Run Interfacing C libraries with GO
  12. call C from GO package main // #include <stdio.h> //

    #include <stdlib.h> import "C" import "unsafe" import "fmt" func main() { str := C.CString("Hello, world!\n") defer C.free(unsafe.Pointer(str)) fmt.Println(C.GoString(str)) } Run Interfacing C libraries with GO
  13. call C code from GO /* # #i in nc

    cl lu ud de e < <s st td di io o. .h h> > # #i in nc cl lu ud de e < <s st td dl li ib b. .h h> > # #i in nc cl lu ud de e < <s st tr ri in ng g. .h h> > c ch ha ar r * * f fo or rm ma at t( (c ch ha ar r* * s s) ) { { char *buf; buf = malloc(strlen(s)); sprintf(buf, "%s", s); return buf; } */ import "C" Interfacing C libraries with GO
  14. call C code from GO func main() { c cs

    s : := = C C. .f fo or rm ma at t( (C C. .C CS St tr ri in ng g( (" "H He el ll lo o w wo or rl ld d\ \n n" ") )) ) defer C.free(unsafe.Pointer(cs)) fmt.Println(C.GoString(cs)) } Hello world Program exited. Run Kill Close Interfacing C libraries with GO
  15. common mistake beware the newline between C code and import

    "C" package main /* #include "stdio.h" */ i im mp po or rt t " "C C" " func main() { C.printf(C.CString("Hello world\n")) } Run Interfacing C libraries with GO
  16. call GO from C (.go) extern the function from c

    code /* #include <stdio.h> e ex xt te er rn n v vo oi id d A AC CF Fu un nc ct ti io on n( () ); ; */ add export / // /e ex xp po or rt t A AG Go oF Fu un nc ct ti io on n func AGoFunction() { fmt.Println("Hello GO World") } Interfacing C libraries with GO
  17. call GO from C (.c) call from C #include "_cgo_export.h"

    void ACFunction() { printf("Hello C World\n"); AGoFunction(); } command line $ go build $ ./goc Hello C World Hello GO World Interfacing C libraries with GO
  18. Dynamic linked library (OpenSSL) /* #include <stdlib.h> # #i in

    nc cl lu ud de e < <o op pe en ns ss sl l/ /c cr ry yp pt to o. .h h> > #cgo CFLAGS: -I/usr/local/opt/openssl/include #cgo LDFLAGS: -L/usr/local/opt/openssl/lib -lcrypto */ func Version(t C.int) string { return C.GoString( C C. .S SS SL Le ea ay y_ _v ve er rs si io on n( (t t) )) ) } func main() { fmt.Println(Version(0)) } Run Interfacing C libraries with GO
  19. Static linked library binding (OpenSSL) /* #include <stdlib.h> #include <openssl/crypto.h>

    #cgo CFLAGS: -I/usr/local/opt/openssl/include # #c cg go o L LD DF FL LA AG GS S: : / /u us sr r/ /l lo oc ca al l/ /o op pt t/ /o op pe en ns ss sl l/ /l li ib b/ /l li ib bc cr ry yp pt to o. .a a # # s st ta at ti ic c l li ib br ra ar ry y */ func main() { fmt.Println(Version(0)) } Run Interfacing C libraries with GO
  20. Pkg-config (OpenSSL) /* #include <stdlib.h> #include <openssl/crypto.h> # #c cg

    go o p pk kg g- -c co on nf fi ig g: : o op pe en ns ss sl l # # p pk kg g- -c co on nf fi ig g g gi iv ve es s i in nf fo or rm ma at ti io on n # #c cg go o L LD DF FL LA AG GS S: : - -l lc cr ry yp pt to o # # o om mi it t d di ir re ec ct to or ri ie es s */ func main() { fmt.Println(Version(0)) } Run Interfacing C libraries with GO
  21. Show linked libraries dynamic linked $ otool -L openssl openssl:

    /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1) static-linked $ otool -L openssl_s openssl_s: /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0) pkg-config $ otool -L openssl_pkg /usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 50.0.0) /usr/lib/libssl.0.9.8.dylib (compatibility version 0.9.8, current version 50.0.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1) Interfacing C libraries with GO
  22. String/Array convension //Go string to C string; result must be

    freed with C.free func C.CString(goString string) *C.char // C string to Go string func C.GoString(cString *C.char) string // C string, length to Go string func C.GoStringN(cString *C.char, length C.int) string // A new Go byte slice from a C array (pointer) func C.GoBytes(cArray unsafe.Pointer,length C.int) []byte Interfacing C libraries with GO
  23. Type mappings strings/array (see previous slide) integers int -> C.int

    unsigned short -> C.ushort ... pointer unsafe.Pointer -> void * struct see RSA example Interfacing C libraries with GO
  24. OpenSSL RSA encrypt/decrypt func main() { m m : :=

    = N Ne ew w( () ) f fm mt t. .P Pr ri in nt tl ln n( (" "M Ma ax x m me es ss sa ag ge e l le en ng gt th h i is s : : " ", , m m. .S Si iz ze e( () )) ) plaintext := "I am plain text" fmt.Println("Plain: ", plaintext) c ci ip ph he er rt te ex xt t : := = m m. .R RS SA A_ _p pu ub bl li ic c_ _e en nc cr ry yp pt t( ([ [] ]b by yt te e( (p pl la ai in nt te ex xt t) )) ) fmt.Println("Cipher: ", string(ciphertext)) fmt.Println("Cipher in bytes: ", ciphertext) p pl la ai in n : := = m m. .R RS SA A_ _p pr ri iv va at te e_ _d de ec cr ry yp pt t( ([ [] ]b by yt te e( (c ci ip ph he er rt te ex xt t) )) ) fmt.Println("Decrypted: ", string(plain)) F Fr re ee e( (m m) ) } Run Interfacing C libraries with GO
  25. OpenSSL RSA declaration // #include <stdlib.h> // #include <openssl/rsa.h> //

    #include <openssl/engine.h> // #cgo CFLAGS: -I/usr/local/opt/openssl/include // #cgo LDFLAGS: -L/usr/local/opt/openssl/lib -lcrypto import "C" import ( "fmt" "unsafe" ) type M struct { c ct tx x * *C C. .R RS SA A } Interfacing C libraries with GO
  26. OpenSSL RSA initial and final functions func New() *M {

    m := &M{} m.ctx = C.RSA_generate_key(2048, 3, nil, nil) return m } func Free(m *M) { C.RSA_free(m.ctx) C.CRYPTO_cleanup_all_ex_data() } func (m *M) Size() int { return int(C.RSA_size(m.ctx)) } Interfacing C libraries with GO
  27. OpenSSL RSA RSA_public_encrypt in C int RSA_public_encrypt(int flen, unsigned char

    *from, unsigned char *to, RSA *rsa, int padding); in GO func (m *M) RSA_public_encrypt(plain []byte) []byte { cipher := (unsafe.Pointer)(C.malloc(C.size_t(C.RSA_size(m.ctx)))) defer C.free(unsafe.Pointer(cipher)) ret := C.RSA_public_encrypt( C C. .i in nt t( (l le en n( (p pl la ai in n) )) ), , / // / f fl le en n ( (* *C C. .u uc ch ha ar r) )( (& &p pl la ai in n[ [0 0] ]) ), , / // / f fr ro om m ( (* *C C. .u uc ch ha ar r) )( (c ci ip ph he er r) ), , / // / t to o m m. .c ct tx x, , / // / R RS SA A * *r rs sa a C C. .R RS SA A_ _P PK KC CS S1 1_ _P PA AD DD DI IN NG G) ) / // / p pa ad dd di in ng g return C.GoBytes(cipher, ret) } Interfacing C libraries with GO
  28. OpenSSL RSA RSA_private_decrypt in C int RSA_private_decrypt(int flen, unsigned char

    *from, unsigned char *to, RSA *rsa, int padding); in GO func (m *M) RSA_private_decrypt(cipher []byte) []byte { p pl la ai in n : := = ( (u un ns sa af fe e. .P Po oi in nt te er r) )( (C C. .m ma al ll lo oc c( (C C. .s si iz ze e_ _t t( (C C. .R RS SA A_ _s si iz ze e( (m m. .c ct tx x) )) )) )) ) d de ef fe er r C C. .f fr re ee e( (u un ns sa af fe e. .P Po oi in nt te er r( (p pl la ai in n) )) ) ret := C.RSA_private_decrypt( C.int(len(cipher)), (*C.uchar)(&cipher[0]), (*C.uchar)(plain), m.ctx, C.RSA_PKCS1_PADDING) return C.GoBytes(plain, ret) } Interfacing C libraries with GO
  29. OpenSSL RSA encrypt/decrypt func main() { m m : :=

    = N Ne ew w( () ) f fm mt t. .P Pr ri in nt tl ln n( (" "M Ma ax x m me es ss sa ag ge e l le en ng gt th h i is s : : " ", , m m. .S Si iz ze e( () )) ) plaintext := "I am plain text" fmt.Println("Plain: ", plaintext) c ci ip ph he er rt te ex xt t : := = m m. .R RS SA A_ _p pu ub bl li ic c_ _e en nc cr ry yp pt t( ([ [] ]b by yt te e( (p pl la ai in nt te ex xt t) )) ) fmt.Println("Cipher: ", string(ciphertext)) fmt.Println("Cipher in bytes: ", ciphertext) p pl la ai in n : := = m m. .R RS SA A_ _p pr ri iv va at te e_ _d de ec cr ry yp pt t( ([ [] ]b by yt te e( (c ci ip ph he er rt te ex xt t) )) ) fmt.Println("Decrypted: ", string(plain)) F Fr re ee e( (m m) ) } Max message length is : 256 Plain: I am plain text Cipher: u��"�E��ժ`�0<�^C���7��H|��P�\?���Xĩ��gS��$RY`�MpH���px�M� [�����L��|�������c������dV��t"�j��w���A��A��-�=h�q�� zY�s#��~-��� �K���3�4��qP��ny'��0�̠ A��p�=0��-rAĞ�1G�leB�Y���p}\�@U�/ʨS���4�5�I?Y[ Cipher in bytes: [117 25 19 248 190 34 195 69 135 212 213 170 96 Decrypted: I am plain text Program exited. Run Kill Close Interfacing C libraries with GO
  30. Useful links Command cgo doc (http://golang.org/cmd/cgo/) Search "cgo" in A

    list of Go projects (http://code.google.com/p/go-wiki/wiki/Projects) Cgo examples (http://golang.org/misc/cgo/) Interfacing C libraries with GO