RustGoほがれロのオヌバヌヘッドでGoからRustを呌び出す

Goはアセンブラヌ関数の呌び出しを適切にサポヌトしおおり、暙準ラむブラリの倧量の非垞に高速な暗号化コヌドは、実際には、20倍以䞊の速床を提䟛する最適化されたアセンブラヌです。


しかし、アセンブラヌコヌドの蚘述は䟝然ずしお困難であり、それを分析するこずはさらに難しく、暗号化は間違いを蚱したせん 。 これらの関数をより高いレベルの蚀語で曞くこずができたら玠晎らしいず思いたせんか


この投皿は、アセンブラヌの呌び出しず比范できるほど高速に実行しようずしお、GoからRustコヌドを呌び出すためのわずかに䞍適切な実隓に぀いおです。 Rustやコンパむラの内郚を知る必芁はなく、リンカずは䜕かを理解するだけです。


なぜ錆びたのですか


私はすぐに認めたす-私はRustを知りたせん、そしお、それに぀いお曞くずいう考えは私にあたり魅力的ではありたせん。 それでも、Rustは非垞によく調敎され最適化された蚀語であり、アセンブラヌよりも読みやすいこずを知っおいたす。 最終的には、すべおがアセンブラよりも読みやすくなりたす


Goでは、通垞、デフォルト倀はメむンタスクに合うように遞択され、デフォルトでは高速であるこずが保蚌されおいる機胜のみが含たれたす-これは、倚くのパラメヌタを必芁ずする絶え間なく成功する闘争に圹立ちたす。 私は圌が倧奜きです。 しかし、その埌、今日行うこずのために、無効化されたセキュリティチェックでスタックのみの関数を生成するように芁求した堎合に、瞬きしない蚀語が必芁になりたす。


そしお、アセンブラヌのように振る舞うために十分に制限し、アセンブラヌず同じくらい効率的になるように最適化できるような蚀語がある堎合、それはおそらくRustになりたす。


最終的に、Rustは安党で、積極的に開発されおおり、重芁なこずずしお、䜿甚可胜な高速暗号化コヌドの優れた゚コシステムを既に持っおいたす。


なぜcgoではありたせんか


Goには、 Foreign Function Interfaceがあり 、そのたた䜿甚できたす。 cgoを䜿甚するず、Goプログラムは最も自然な方法でC関数を呌び出すこずができたす。残念ながら、これはたったく自然なこずではありたせん。 私はcgoに぀いお私が望んでいるよりも倚くのこずを知っおいたす、そしお、私を信じお、これは党く面癜くないです。


C ABIをFFIの「lingua franca」ずしお䜿甚するず、どこからでも呌び出すこずができたす。RustはC ABIず互換性のあるラむブラリにコンパむルでき、cgoはそれを䜿甚できたす。 これは愚かですが、動䜜したす。


逆に、GoをCラむブラリにコンパむルしお、さたざたな蚀語から呌び出すこずもできたす。たずえば、私はPythonをトリックずしお実行したした 。 人々、それは単なるトリックでした、真剣に受け取らないでください


しかし、cgoはGoの自然さを少し远加するために内郚で倚くのこずを行いたす.Cコヌドのスタックを線成し、Go呌び出しでパニックが発生した堎合に正しく動䜜するように延期呌び出しをセットアップしたす...これに぀いおは別の投皿を曞くこずができたす。


しかし、その結果、各cgo呌び出しのコストは、今日お話ししおいるような、軜快な小さな関数の堎合には高すぎたす 。


すべおを䞀緒に線む


䞀般に、この考えアセンブラヌ、Rustのコヌドなどの分離コヌドがある堎合、理論的には、アセンブラヌず同様にそれを䜿甚しお盎接呌び出すこずができるはずです。 薄い局でもかたいたせん。


䞭皋床のIRレベルで䜜業するこずは望たしくありたせん。Goコンパむラヌは、 Go 1.3から始めおも喜ぶ前に、Goコヌドずアセンブラヌの䞡方をマシンコヌドに倉換したす。


これは、システムリンカヌを䜿甚しおGoでプログラムをリンクするずきに「倖郚リンク」などの抂念が存圚するこずで確認されたす。 これは、cgoの動䜜ずたったく同じです。最初に、CはCコンパむラ、Go-Goコンパむラでコンパむルされ、これらはすべおclangたたはgccを䜿甚しおリンクされたす。 CGO_LDFLAGSを介しお、戊艊にフラグを盎接枡すこずもできたす。


cgoのすべおのセキュリティ察策の裏では、もちろん、最終的には倚蚀語の課題自䜓が芋぀かりたす。


しかし、もちろん、コンパむラを倉曎せずにこれを行う方法を芋぀けるこずは玠晎らしいこずです。 たず、GoプログラムをRustアヌカむブにリンクする方法を芋おみたしょう。


#cgoディレクティブを䜿甚する堎合を陀き、 go buildを䜿甚しお倖郚バむナリにリンクする通垞の方法を芋぀けるこずができたせんでしたその理由は。 ただし、cgo呌び出しは.sファむルを䜜成したす。このファむルは、GoではなくCコンパむラに枡されたす。぀たり、Goアセンブラが必芁であるこずを意味したす。


幞いなこずに、 go / buildは単なるフロント゚ンドです Goは、プログラムのコンパむルずリンクのための䞀連の䜎レベルナヌティリティを提䟛したす。gobuildは、ファむルを収集しお束にし、これらのナヌティリティを実行go buildだけです。 -xフラグで䜕が起こるかを远跡できたす。


-x -ldflags "-v -linkmode=external '-extldflags=-v'"するずきに-x -ldflags "-v -linkmode=external '-extldflags=-v'"呌び出しに䌌た小さなMakefileを䜜成したした。


 rustgo: rustgo.a go tool link -o rustgo -extld clang -buildmode exe -buildid b01dca11ab1e -linkmode external -v rustgo.a rustgo.a: hello.go hello.o go tool compile -o rustgo.a -p main -buildid b01dca11ab1e -pack hello.go go tool pack r rustgo.a hello.o hello.o: hello.s go tool asm -I "$(shell go env GOROOT)/pkg/include" -D GOOS_darwin -D GOARCH_amd64 -o hello.o hello.s 

これにより、1぀のGoファむル hello.go ずアセンブラヌGoファむル hello.s で構成される単玔なメむンパッケヌゞが䜜成されたす。


さお、オブゞェクトをRustにリンクしたい堎合、たず静的ラむブラリずしおビルドする必芁がありたす...


 libhello.a: hello.rs rustc -g -O --crate-type staticlib hello.rs 

...そしお、倖郚リンカにそれらをリンクするように指瀺したす。


 rustgo: rustgo.a libhello.a go tool link -o rustgo -extld clang -buildmode exe -buildid b01dca11ab1e -linkmode external -v -extldflags='-lhello -L"$(CURDIR)"' rustgo.a 

 $ make go tool asm -I "/usr/local/Cellar/go/1.8.1_1/libexec/pkg/include" -D GOOS_darwin -D GOARCH_amd64 -o hello.o hello.s go tool compile -o rustgo.a -p main -buildid b01dca11ab1e -pack hello.go go tool pack r rustgo.a hello.o rustc --crate-type staticlib hello.rs note: link against the following native artifacts when linking against this static library note: the order and any duplication can be significant on some platforms, and so may need to be preserved note: library: System note: library: c note: library: m go tool link -o rustgo -extld clang -buildmode exe -buildid b01dca11ab1e -linkmode external -v -extldflags="-lhello -L/Users/filippo/code/misc/rustgo" rustgo.a HEADER = -H1 -T0x1001000 -D0x0 -R0x1000 searching for runtime.a in /usr/local/Cellar/go/1.8.1_1/libexec/pkg/darwin_amd64/runtime.a searching for runtime/cgo.a in /usr/local/Cellar/go/1.8.1_1/libexec/pkg/darwin_amd64/runtime/cgo.a 0.00 deadcode 0.00 pclntab=166785 bytes, funcdata total 17079 bytes 0.01 dodata 0.01 symsize = 0 0.01 symsize = 0 0.01 reloc 0.01 dwarf 0.02 symsize = 0 0.02 reloc 0.02 asmb 0.02 codeblk 0.03 datblk 0.03 sym 0.03 headr 0.06 host link: "clang" "-m64" "-gdwarf-2" "-Wl,-headerpad,1144" "-Wl,-no_pie" "-Wl,-pagezero_size,4000000" "-o" "rustgo" "-Qunused-arguments" "/var/folders/ry/v14gg02d0y9cb2w9809hf6ch0000gn/T/go-link-412633279/go.o" "/var/folders/ry/v14gg02d0y9cb2w9809hf6ch0000gn/T/go-link-412633279/000000.o" "-g" "-O2" "-lpthread" "-lhello" "-L/Users/filippo/code/misc/rustgo" 0.34 cpu time 12641 symbols 5764 liveness data 

Rustぞ


リンクしたしたが、シンボル自䜓は、単にバむナリファむルで隣り合っお座っおいるだけでは䜕もできたせん。 GoコヌドからRust関数を䜕らかの方法で呌び出す必芁がありたす。


GoからGo関数を呌び出す方法は既に知っおいたす。 アセンブラヌでは、この呌び出しはCALL hello(SB)ようになりたす。ここで、SBはすべおのグロヌバル文字でアクセス可胜な仮想レゞスタです。


Goからアセンブラヌで関数を呌び出す堎合、コンパむラヌにそのこずを知らせる必芁がありたす-Cヘッダヌのようなもので、関数の本䜓なしでfunc hello()のみを蚘述したす。


倖郚Rust関数の呌び出しの䞊蚘の組み合わせをすべお詊したしたが、関数のシンボルたたは本䜓が衚瀺されないずいう䞍満がありたした。


しかし、最終的には単なる倧きなコヌドゞェネレヌタヌであるcgoは、どういうわけかこの゚むリアン関数を呌び出すこずができたす しかし、どのように


私は数日埌に答えに出䌚いたした 。


 //go:cgo_import_static _cgoPREFIX_Cfunc__Cmalloc //go:linkname __cgofn__cgoPREFIX_Cfunc__Cmalloc _cgoPREFIX_Cfunc__Cmalloc var __cgofn__cgoPREFIX_Cfunc__Cmalloc byte var _cgoPREFIX_Cfunc__Cmalloc = unsafe.Pointer(&__cgofn__cgoPREFIX_Cfunc__Cmalloc) 

面癜いプラグマのように芋えたす //go:linknameは、ロヌカルスコヌプ プラむベヌト関数を呌び出すために䜿甚できる 内の文字の゚むリアスを䜜成するだけです。ここでのbyteトリックは、ある皮のアドレス操䜜のためだけであるず確信しおいbyte //go:cgo_import_static ...倖字をむンポヌトしたす


この新しい知識ず䞊蚘のMakefileを䜿甚しお、Rusthello.rsから関数を呌び出すこずができたす。


 #[no_mangle] pub extern fn hello() { println!("Hello, Rust!"); } 

マングル/パブ/゚クスタヌンのない魔術は、 このチュヌトリアルから取られおいたす 


そしお、Go hello.go でこのプログラムから呌び出したす


 package main //go:cgo_import_static hello func trampoline() func main() { println("Hello, Go!") trampoline() } 

このアセンブラヌの䟋を䜿甚 hello.s 


 TEXT ·trampoline(SB), 0, $2048 JMP hello(SB) RET 

CALLは掗緎されすぎお䜿甚できたせんでしたが、単玔なJMPを䜿甚しお...


 Hello, Go! Hello, Rust! panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x0] 

さお、終了しようずしたずきにプログラムがクラッシュしたした。 そしお今、この$2048倀はRustが䞎えたスタック党䜓でありスタックをあるべき堎所に眮く堎合、Rustが少なくずもヒヌプに觊れようずした堎合に䜕が起こるかは尋ねたせん...しかし、いたいたしい、私はそれがたったく機胜するこずに驚いおいたす


呌び出し芏玄


プログラムをきれいに終了し、ある皮の匕数を枡したい堎合は、Go and Rustの呌び出し芏玄をさらに詳しく調べる必芁がありたす。 これらの芏則により、関数呌び出し間で匕数ず戻り倀が配眮される堎所が決たりたす。


Goの呌び出し芏則に぀いおは、 ここずここで説明したす 。 Rustの堎合、 FFIの暙準を調べる必芁がありたすが、これはCの暙準的な合意にすぎたせん。


続行するには、デバッガが必芁です。 LLDBはGoをサポヌトしおいたすが、MacOS Xではブレヌクポむントが倱敗するため、特暩Dockerコンテナヌ内でこれを行う必芁がありたした



通話契玄



Goの呌び出し芏玄はほずんど文曞化されおいたせんが、さらに進むにはそれを理解する必芁があるため、この逆アセンブラヌリストamd64から孊ぶこずができたす。 非垞に単玔な関数を芋おみたしょう


 // func foo(x, y uint64) uint64 TEXT ·foo(SB), 0, $256-24 MOVQ x+0(FP), DX MOVQ DX, ret+16(FP) RET 

fooは2560x100バむトのロヌカルフレヌム、16バむトの匕数、8バむトの戻り倀があり、最初の匕数のみを返したす。


 func main() { foo(0xf0f0f0f0f0f0f0f0, 0x5555555555555555) 

 rustgo[0x49d785]: movabsq $-0xf0f0f0f0f0f0f10, %rax rustgo[0x49d78f]: movq %rax, (%rsp) rustgo[0x49d793]: movabsq $0x5555555555555555, %rax rustgo[0x49d79d]: movq %rax, 0x8(%rsp) rustgo[0x49d7a2]: callq 0x49d8a0 ; main.foo at hello.s:14 

䞊蚘の呌び出しコヌドはほずんど䜕もしたせんフレヌムの䞋郚にある逆の順序でスタックに匕数を眮き rspから16(rsp)に、スタックが倧きくなるこずを忘れないでください、 CALLを呌び出したす。 CALL呌び出すず、戻り倀ぞのポむンタヌがスタックにプッシュされ、ゞャンプしたす。 ここには呌び出し関数のクリアはなく、最埌に単玔なRETがありたす。


rsp修正されおいるこずに泚意しおください。ここではmovq代わりにmovqがありたす


 rustgo`main.foo at hello.s:14: rustgo[0x49d8a0]: movq %fs:-0x8, %rcx rustgo[0x49d8a9]: leaq -0x88(%rsp), %rax rustgo[0x49d8b1]: cmpq 0x10(%rcx), %rax rustgo[0x49d8b5]: jbe 0x49d8ee ; main.foo + 78 at hello.s:14 [...] rustgo[0x49d8ee]: callq 0x495d10 ; runtime.morestack_noctxt at asm_amd64.s:405 rustgo[0x49d8f3]: jmp 0x49d8a0 ; main.foo at hello.s:14 

最初の4぀の関数呜什ず最埌の2぀の関数呜什は、スタックに十分なスペヌスがあるかどうかをチェックし、スペヌスがない堎合は、 runtime.morestackを呌び出しruntime.morestack 。 NOSPLITずラベル付けされた関数では、それらはおそらくスキップされたす


 rustgo[0x49d8b7]: subq $0x108, %rsp [...] rustgo[0x49d8e6]: addq $0x108, %rsp rustgo[0x49d8ed]: retq 

次に、 rspコントロヌルを䜿甚したす。このコントロヌルでは、0x108が枛算され、䞀床にフレヌム甚の0x100バむトずポむンタヌ甚の8バむトのスペヌスが解攟されたす。 その結果、 rspは関数フレヌムの䞋郚終了を指し、呌び出された関数によっお制埡されたす。 戻る前に、 rspは元の堎所リタヌンポむンタの盎埌に戻りたす。


 rustgo[0x49d8be]: movq %rbp, 0x100(%rsp) rustgo[0x49d8c6]: leaq 0x100(%rsp), %rbp [...] rustgo[0x49d8de]: movq 0x100(%rsp), %rbp 

最埌に、フレヌムぞのポむンタヌは、本質的に、リタヌンポむンタヌの盎埌にスタックにrbpれ、 rbpで曎新されrbp 。 rbpも呌び出された関数によっお保存され、スタックをスピンできるようにするために、呌び出された関数がrbpを保存した堎所で曎新する必芁があるこずがrbpたした。


 rustgo[0x49d8ce]: movq 0x110(%rsp), %rdx rustgo[0x49d8d6]: movq %rdx, 0x120(%rsp) 

その結果、関数本䜓から、戻り倀が匕数のすぐ䞊にあるこずがわかりたした。


仮想レゞスタ


Goのドキュメントでは、 SPずFPはrspずrbp゚むリアスではなく、仮想レゞスタであるずrbpたす。


GoアセンブラからSPを䜿甚するず、 SPがフレヌムの䞋郚ではなく䞊郚を指すように、すべおのオフセットが実際のレゞスタrspに察しお再蚈算されるこずは明らかです。 これは、フレヌムのサむズが倉曎されたずきにすべおのオフセットを倉曎できない可胜性があるこずを意味するため、䟿利ですが、これは実際には単なる構文䞊のシュガヌです。 ベアレゞストリアクセス MOV SP, DX は、 rsp盎接アクセスしたす。


仮想FPレゞスタもrsp関連しお再rspたす。 これは、呌び出し偎の関数のフレヌムの䞀番䞋を指し、匕数があり、盎接アクセスできたせん。


泚Goはrbpおよびフレヌムポむンタヌを保存しおデバッグを支揎したすが、仮想FP rspおよびrsp固定omit-stack-pointerスタむルomit-stack-pointerを固定しお䜿甚しomit-stack-pointer 。 フレヌムぞのポむンタヌず、アダムラングレヌによるこの投皿での䜿甚方法に぀いお詳しく読むこずができたす。


Cの呌び出し芏玄


Cでは、暙準のx86-64呌び出し芏玄はsysv64であり、たったく異なりたす。



rustcも同じように機胜したす -gずずもにrustcを䜿甚しお生成されたす。


すべおをたずめる


2぀の契玄の間に単玔な螏み台を䜜成するこずは難しくありたせん。 むンスピレヌションに぀いおはasmcgocallを芋るこずができたす。それはasmcgocallに぀いおのみです。


Goがアセンブラ関数のスタックを䜿甚するようにしたいのは芚えおおく必芁がありたす。Goが存圚するこずを保蚌しおいるからです。 これを行うには、スタックの最埌にrspを返す必芁がありたす。


 package main //go:cgo_import_static increment func trampoline(arg uint64) uint64 func main() { println(trampoline(41)) } 

 TEXT ·trampoline(SB), 0, $2048-16 MOVQ arg+0(FP), DI // Load the argument before messing with SP MOVQ SP, BX // Save SP in a callee-saved registry ADDQ $2048, SP // Rollback SP to reuse this function's frame ANDQ $~15, SP // Align the stack to 16-bytes CALL increment(SB) MOVQ BX, SP // Restore SP MOVQ AX, ret+8(FP) // Place the return value on the stack RET 

 #[no_mangle] pub extern fn increment(a: u64) -> u64 { return a + 1; } 

macOSでの呌び出し


実際、 CALLはmacOSずあたり仲が良くありたせん。 䜕らかの理由で、関数呌び出しがcgo_thread_start䞭間呌び出しに眮き換えられたした。これは、 cgo_import_staticず呌ばれるものを䜿甚し、GoアセンブラヌでもCALL cgo_import_staticいるこずを考えるず、それほど奇劙ではありたせん。


 callq 0x40a27cd ; x_cgo_thread_start + 29 

//go:linknameを䜿甚しおこの「ヘルプ」を回避できたす。これは、暙準ラむブラリで関数ぞのポむンタを取埗し、次のように関数ポむンタを呌び出したす。


 import _ "unsafe" //go:cgo_import_static increment //go:linkname increment increment var increment uintptr var _increment = &increment 

  MOVQ ·_increment(SB), AX CALL AX 

速いですか


この実隓の党タスクは、暗号操䜜のためにアセンブラヌの代わりにRustを呌び出すこずでした楜しんでください。 したがっお、rustgo呌び出しは、アセンブラヌ呌び出しず同じくらい高速である必芁がありたす。


ベンチマヌク時間


むンラむンバヌゞョンでのuint64倉数の増加を、 //go:noinlineディレクティブ、rustgo呌び出し、およびRust関数自䜓のcgo呌び出しず比范したす。


Rustは-g -Oフラグを䜿甚しおコンパむルされ、ベンチマヌクは2.9GHz Intel Code i5プロセッサヌ䞊のmacOSで実行されたした。


 name time/op CallOverhead/Inline 1.72ns ± 3% CallOverhead/Go 4.60ns ± 2% CallOverhead/rustgo 5.11ns ± 4% CallOverhead/cgo 73.6ns ± 0% 

rustgoは、通垞のGo関数を呌び出すよりも11遅く、cgoよりもほが15倍高速です


Linuxではポむンタヌの問題がなく、結果はさらに良くなり、2だけ遅くなりたす。


 name time/op CallOverhead/Inline 1.67ns ± 2% CallOverhead/Go 4.49ns ± 3% CallOverhead/rustgo 4.58ns ± 3% CallOverhead/cgo 69.4ns ± 0% 

実際の䟋


実際の䟋ずしお、すばらしいラむブラリcurve25519-dalekを遞択したした。具䜓的には、曲線の開始点にスカラヌを掛けお、そのEdwards衚珟を返すタスクを遞択したした。


貚物のベンチマヌクは、プロセッサの呚波数が動的に倉化するため、打ち䞊げ間で倧きく異なりたすが、操䜜が22.9µs±17を占めるずおおよそ玄束したす。


 test curve::bench::basepoint_mult ... bench: 17,276 ns/iter (+/- 3,057) test curve::bench::edwards_compress ... bench: 5,633 ns/iter (+/- 858) 

Go偎では、単玔なAPIを远加したす。


 func ScalarBaseMult(dst, in *[32]byte) 

Rust偎では、これは通垞のFFIのむンタヌフェむスを構築するこずず倧差ありたせん。


率盎に蚀っお、Rustでこれを機胜させるには氞遠に時間がかかりたした。


 #![no_std] extern crate curve25519_dalek; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::constants; #[no_mangle] pub extern fn scalar_base_mult(dst: &mut [u8; 32], k: &[u8; 32]) { let res = &constants::ED25519_BASEPOINT_TABLE * &Scalar(*k); dst.clone_from(res.compress_edwards().as_bytes()); } 

.aを䜜成するには、 Cargo.toml cargo build --releaseをCargo.tomlで実行しCargo.tomlこれは、䟝存関係を瀺し、フレヌムぞのポむンタヌを含み、暙準ラむブラリなしで最も高床な数孊を䜿甚するようにcurve25519-dalekを構成したす。


 [package] name = "ed25519-dalek-rustgo" version = "0.0.0" [lib] crate-type = ["staticlib"] [dependencies.curve25519-dalek] version = "^0.9" default-features = false features = ["nightly"] [profile.release] debug = true 

それでも、スプリングボヌドを修正しお、2぀の匕数を取り、䜕も返さないようにする必芁がありたす。


 TEXT ·ScalarBaseMult(SB), 0, $16384-16 MOVQ dst+0(FP), DI MOVQ in+8(FP), SI MOVQ SP, BX ADDQ $16384, SP ANDQ $~15, SP MOVQ ·_scalar_base_mult(SB), AX CALL AX MOVQ BX, SP RET 

その結果、Goからの透過的な呌び出しが行われ、玔粋なGoのベンチマヌクに匹敵する速床で、cgoよりもほが6高速になりたす。


 name old time/op new time/op delta RustScalarBaseMult 23.7µs ± 1% 22.3µs ± 4% -5.88% (p=0.003 n=5+7) 

比范のために、Goパッケヌゞgithub.com/agl/ed25519/edwards25519同様の機胜-玔粋なGo実装では、ほが3倍の時間がかかりたす。


 h := &edwards25519.ExtendedGroupElement{} edwards25519.GeScalarMultBase(h, &k) h.ToBytes(&dst) 

 name time/op GoScalarBaseMult 66.1µs ± 2% 

すべおたずめお梱包する


これで本圓にうたくいくこずがわかりたした しかし、これを実際に䜿甚できるようにするには、゜リュヌションは、泥だらけのビルドプロセスを䜿甚しおpackage main匷制的に挿入するのではなく、むンポヌトできるパッケヌゞの圢匏にする必芁がありたす。


そしおここ//go:binary-only-packageが登堎したす。 この泚釈により、゜ヌスコヌドを無芖し、以前に収集した$GOPATH/pkg .aラむブラリファむルのみを䜿甚するこずができたす。


ネむティブのGoリンカヌ cmd / link 、 内郚リンカヌずも呌ばれたすで動䜜する.aファむルを構築できれば、 これを配垃できたす。これにより、ナヌザヌはクロスコンパむルを含むネむティブコヌドであるかのようにパッケヌゞをむンポヌトできたす 暗黙的にこのプラットフォヌム甚に.aを䜜成したす


倚くの堎合、Goはシンプルで、既にアセンブラヌずRustのペアがありたす。 go docで衚瀺できるようにドキュメントを远加するこずもできたす。


 //go:binary-only-package // Package edwards25519 implements operations on an Edwards curve that is // isomorphic to curve25519. // // Crypto operations are implemented by calling directly into the Rust // library curve25519-dalek, without cgo. // // You should not actually be using this. package edwards25519 import _ "unsafe" //go:cgo_import_static scalar_base_mult //go:linkname scalar_base_mult scalar_base_mult var scalar_base_mult uintptr var _scalar_base_mult = &scalar_base_mult // ScalarBaseMult multiplies the scalar in by the curve basepoint, and writes // the compressed Edwards representation of the resulting point to dst. func ScalarBaseMult(dst, in *[32]byte) 

makefileはわずかに倉曎されたす。これは、ラむブラリを構築しなくなったためgo tool link䜿甚を停止できgo tool link 。


.aアヌカむブ.aは、文字テヌブルずずもに叀代圢匏のオブゞェクト.oファむルのコレクションです。 シンボルをlibed25519_dalek_rustgo.aからlibed25519_dalek_rustgo.aアヌカむブにlibed25519_dalek_rustgo.a 、 go tool compile libed25519_dalek_rustgo.aできるようにすれば、目暙を達成できたす。


.aアヌカむブは、UNIXナヌティリティarたたはGo- cmd / packの内郚類䌌物 go tool pack で動䜜したす。 もちろん、2぀の圢匏は非垞にわずかに異なりたす。 libed25519_dalek_rustgo.aにはar 、 libed25519_dalek_rustgo.aにはcmd/packを䜿甚する必芁がありたす。


たずえば、私のmacOSのarは、 BSD芏則を䜿甚しお#1/LENファむルを呌び出し、このファむルの先頭に長さLENのファむル名を挿入しお、最倧ファむル長の16バむトをバむパスしたす。これは玛らわしいです。


これらの2぀のラむブラリをリンクするために、最も単玔なreadcrutchメ゜ッドを䜜成しようずしたしたlibed25519_dalek_rustgo.aを別のフォルダヌに抜出し、そのオブゞェクトをedwards25519.aたす。


 edwards25519/edwards25519.a: edwards25519/rustgo.go edwards25519/rustgo.o target/release/libed25519_dalek_rustgo.a go tool compile -N -l -o $@ -p main -pack edwards25519/rustgo.go go tool pack r $@ edwards25519/rustgo.o # from edwards25519/rustgo.s mkdir -p target/release/libed25519_dalek_rustgo && cd target/release/libed25519_dalek_rustgo && \ rm -f *.o && ar xv "$(CURDIR)/target/release/libed25519_dalek_rustgo.a" go tool pack r $@ target/release/libed25519_dalek_rustgo/*.o .PHONY: install install: edwards25519/edwards25519.a mkdir -p "$(shell go env GOPATH)/pkg/darwin_amd64/$(IMPORT_PATH)/" cp edwards25519/edwards25519.a "$(shell go env GOPATH)/pkg/darwin_amd64/$(IMPORT_PATH)/" 

うたくいったずきの驚きを想像しおみおください


.aファむルを適切な堎所に眮いお、このパッケヌゞを䜿甚する簡単なプログラムを䜜成したす。


 package main import ( "bytes" "encoding/hex" "fmt" "testing" "github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519" ) func main() { input, _ := hex.DecodeString("39129b3f7bbd7e17a39679b940018a737fc3bf430fcbc827029e67360aab3707") expected, _ := hex.DecodeString("1cc4789ed5ea69f84ad460941ba0491ff532c1af1fa126733d6c7b62f7ebcbcf") var dst, k [32]byte copy(k[:], input) edwards25519.ScalarBaseMult(&dst, &k) if !bytes.Equal(dst[:], expected) { fmt.Println("rustgo produces a wrong result!") } fmt.Printf("BenchmarkScalarBaseMult\t%v\n", testing.Benchmark(func(b *testing.B) { for i := 0; i < bN; i++ { edwards25519.ScalarBaseMult(&dst, &k) } })) } 

go build実行しgo build 


 $ go build -ldflags '-linkmode external -extldflags -lresolv' $ ./ed25519-dalek-rustgo BenchmarkScalarBaseMult 100000 19914 ns/op 

たあ、それはほずんど働いた。 私は少しポン匕きをしなければなりたせんでした。 バむナリファむルは、 libresolvずリンクするたでコンパむルされlibresolv 。 正盎に蚀うず、Rustコンパむラヌはそれを蚀おうずしたした。 しかし、Rustコンパむラが蚀うこずを誰がたたたたしたすか


 note: link against the following native artifacts when linking against this static library note: the order and any duplication can be significant on some platforms, and so may need to be preserved note: library: System note: library: resolv note: library: c note: library: m 

システムラむブラリずのリンクは、内郚リンカヌずクロスコンパむルでは決しお起こらないため、問題になりたす...


しかし、ちょっず、lib resolve なぜそれがno_std 、「アセンブラのようであるべきだ」、RustラむブラリスタックのみがDNS名を解決するために暙準ラむブラリを䜿甚しようずしおいたすか


no_stdず蚀った


ここでの問題は、ラむブラリが実際にはno_stdはないこずno_std 。 ここですべおを芋おください アロケヌタヌは必芁ありたせん。


 $ ar t target/release/libed25519_dalek_rustgo.a __.SYMDEF ed25519_dalek_rustgo-742a1d9f1c101d86.0.o ed25519_dalek_rustgo-742a1d9f1c101d86.crate.allocator.o curve25519_dalek-03e3ca0f6d904d88.0.o subtle-cd04b61500f6e56a.0.o std-72653eb2361f5909.0.o panic_unwind-d0b88496572d35a9.0.o unwind-da13b913698118f9.0.o arrayref-2be0c0ff08ae2c7d.0.o digest-f1373d68da35ca45.0.o generic_array-95ca86a62dc11ddc.0.o nodrop-7df18ca19bb4fc21.0.o odds-3bc0ea0bdf8209aa.0.o typenum-a61a9024d805e64e.0.o rand-e0d585156faee9eb.0.o alloc_system-c942637a1f049140.0.o libc-e038d130d15e5dae.0.o alloc-0e789b712308019f.0.o std_unicode-9735142be30abc63.0.o compiler_builtins-8a5da980a34153c7.0.o absvdi2.o absvsi2.o absvti2.o [... snip ...] truncsfhf2.o ucmpdi2.o ucmpti2.o core-9077840c2cc91cbf.0.o 

だから、どのようにすべおのno_stdを行うのでしょうか これは別の冒険であるこずが刀明したしたが、結論だけを曞きたす。



lib.rs :


 #![no_std] #![feature(lang_items, compiler_builtins_lib, core_intrinsics)] use core::intrinsics; #[allow(private_no_mangle_fns)] #[no_mangle] // rust-lang/rust#38281 #[lang = "panic_fmt"] fn panic_fmt() -> ! { unsafe { intrinsics::abort() } } #[lang = "eh_personality"] extern fn eh_personality() {} extern crate compiler_builtins; // rust-lang/rust#43264 extern crate rlibc; 

, go build (!!!) macOS.


Linux


Linux .


fmax , , , :


 $ ld -r -o linux.o target/release/libed25519_dalek_rustgo/*.o $ nm -u linux.o U _GLOBAL_OFFSET_TABLE_ U abort U fmax U fmaxf U fmaxl U logb U logbf U logbl U scalbn U scalbnf U scalbnl 

, , --gc-sections , , . , , ( ):


 $ go build -ldflags '-extld clang -linkmode external -extldflags -Wl,--gc-sections' 

, , Makefile , --gc-sections ? , .a man- .


.o , , ld -r --gc-sections -u $SYMBOL . -r -u "", . $SYMBOL scalar_base_mult .


macOS? , , , macOS .


 $ ld -e _scalar_base_mult target/release/libed25519_dalek_rustgo/*.o Undefined symbols for architecture x86_64: "___assert_rtn", referenced from: _compilerrt_abort_impl in int_util.o "_copysign", referenced from: ___divdc3 in divdc3.o ___muldc3 in muldc3.o "_copysignf", referenced from: ___divsc3 in divsc3.o ___mulsc3 in mulsc3.o "_copysignl", referenced from: ___divxc3 in divxc3.o ___mulxc3 in mulxc3.o "_fmax", referenced from: ___divdc3 in divdc3.o "_fmaxf", referenced from: ___divsc3 in divsc3.o "_fmaxl", referenced from: ___divxc3 in divxc3.o "_logb", referenced from: ___divdc3 in divdc3.o "_logbf", referenced from: ___divsc3 in divsc3.o "_logbl", referenced from: ___divxc3 in divxc3.o "_scalbn", referenced from: ___divdc3 in divdc3.o "_scalbnf", referenced from: ___divsc3 in divsc3.o "_scalbnl", referenced from: ___divxc3 in divxc3.o ld: symbol(s) not found for inferred architecture x86_64 $ ld -e _scalar_base_mult -dead_strip target/release/libed25519_dalek_rustgo/*.o 

, , macOS _ , .


, Makefile, :]


 edwards25519/edwards25519.a: edwards25519/rustgo.go edwards25519/rustgo.o edwards25519/libed25519_dalek_rustgo.o go tool compile -N -l -o $@ -p main -pack edwards25519/rustgo.go go tool pack r $@ edwards25519/rustgo.o edwards25519/libed25519_dalek_rustgo.o edwards25519/libed25519_dalek_rustgo.o: target/$(TARGET)/release/libed25519_dalek_rustgo.a ifeq ($(shell go env GOOS),darwin) $(LD) -r -o $@ -arch x86_64 -u "_$(SYMBOL)" $^ else $(LD) -r -o $@ --gc-sections -u "$(SYMBOL)" $^ endif 

Linux. , , . CALL Rust .


, - , rustgo, , , . cmd/link ( !), , Go, , //cgo:cgo_import_static , //cgo:cgo_import_dynamic .


 //go:cgo_import_static scalar_base_mult //go:cgo_import_dynamic scalar_base_mult 

, , - rustgo , macOS, Linux, .



, .a , //go:binary-only-package tar- .a linux_amd64/darwin_amd64 , :


 $ tar tf ed25519-dalek-rustgo_go1.8.3.tar.gz src/github.com/FiloSottile/ed25519-dalek-rustgo/ src/github.com/FiloSottile/ed25519-dalek-rustgo/.gitignore src/github.com/FiloSottile/ed25519-dalek-rustgo/Cargo.lock src/github.com/FiloSottile/ed25519-dalek-rustgo/Cargo.toml src/github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519/ src/github.com/FiloSottile/ed25519-dalek-rustgo/main.go src/github.com/FiloSottile/ed25519-dalek-rustgo/Makefile src/github.com/FiloSottile/ed25519-dalek-rustgo/release.sh src/github.com/FiloSottile/ed25519-dalek-rustgo/src/ src/github.com/FiloSottile/ed25519-dalek-rustgo/target.go src/github.com/FiloSottile/ed25519-dalek-rustgo/src/lib.rs src/github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519/rustgo.go src/github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519/rustgo.s pkg/linux_amd64/github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519.a pkg/darwin_amd64/github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519.a 

, , - ( .a ).


, , , Rust -Ctarget-cpu=native , . ( curve25519-dalek authors ) - Haswell , Haswell:


 $ benchstat bench-none.txt bench-haswell.txt name old time/op new time/op delta ScalarBaseMult/rustgo 22.0µs ± 3% 20.2µs ± 2% -8.41% (p=0.001 n=7+6) $ benchstat bench-haswell.txt bench-native.txt name old time/op new time/op delta ScalarBaseMult/rustgo 20.2µs ± 2% 20.1µs ± 2% ~ (p=0.945 n=6+7) 

, , Makefile GOOS/GOARCH, Rust, Rust -, - .a .


: github.com/FiloSottile/ed25519-dalek-rustgo/edwards25519
godoc .



, .


, , rustgo , . , , g , , , , . Rust .


, morestack NOSPLIT , , ( rsp ) , , Rust ( ).


, - "rustgo" , Makefile . Cgo , . go:generate , -, cargo (-, Go Rust!). FFI- Rust, GoSlice .


 #[repr(C)] struct GoSlice { array: *mut u8, len: i32, cap: i32, } 

たたは、GoたたはRustの誰かが来お、怪我をする前に立ち止たるように蚀うかもしれたせん。


PS。誰かがこれをcgoセキュリティのためのたくさんの機胜を持っおいるたたは玔粋なGoず比范し始める前に、rustgoはどちらの代わりにもなりたせん。これは、手䜜業で蚘述されたアセンブラヌ関数を、同等のパフォヌマンスでより安党で読みやすいものに眮き換えるこずを意味しおいたした。さらに良いこずに、それは楜しい実隓ずしお意図されおいたした。



Source: https://habr.com/ru/post/J337348/


All Articles