50

Goコードにインラインアセンブリを含めることは可能ですか?

このブログ投稿では、Go を別の.sファイルにコンパイルして編集することを示していますが、多くの C コンパイラがサポートするように、Go 関数の一部としてインラインasm をサポートしていません。

4

4 に答える 4

48

インライン アセンブリはサポートされていませんが、cgo でコンパイルし、 gmp.goimport "C"のようにを使用して、C を介してアセンブリで記述されたコードとリンクできます。または、関数名が「・」で始まる必要があるasm_linux_amd64.sのように、Go と直接互換性のあるスタイルのアセンブリで記述することもできます。

または、これまでのところ私のお気に入りの方法である nasm と gccgo を使用することもできます。(Nasm は "·" で始まる関数をサポートしていないようです)。

これは、実際の「Hello World」の例です。

こんにちは.asm:

; Based on hello.asm from nasm

    SECTION .data       ; data section
msg:    db "Hello World",10 ; the string to print, 10=cr
len:    equ $-msg       ; "$" means "here"
                ; len is a value, not an address

    SECTION .text       ; code section

global go.main.hello        ; make label available to linker (Go)
go.main.hello:

    ; --- setup stack frame
    push rbp            ; save old base pointer
    mov rbp,rsp   ; use stack pointer as new base pointer

    ; --- print message
    mov edx,len     ; arg3, length of string to print
    mov ecx,msg     ; arg2, pointer to string
    mov ebx,1       ; arg1, where to write, screen
    mov eax,4       ; write sysout command to int 80 hex
    int 0x80        ; interrupt 80 hex, call kernel

    ; --- takedown stack frame
    mov rsp,rbp  ; use base pointer as new stack pointer
    pop rbp      ; get the old base pointer

    ; --- return
    mov rax,0       ; error code 0, normal, no error
    ret         ; return

main.go:

package main

func hello();

func main() {
    hello()
    hello()
}

そして便利な Makefile:

main: main.go hello.o
    gccgo hello.o main.go -o main

hello.o: hello.asm
    nasm -f elf64 -o hello.o hello.asm

clean:
    rm -rf _obj *.o *~ *.6 *.gch a.out main

hello()hello() が正しく返されることを再確認するためだけに、main.go で 2 回呼び出します。

割り込み 80h を直接呼び出すことは、Linux では適切なスタイルとは見なされず、C で記述された関数を呼び出すことは、より「将来性のある」ものであることに注意してください。また、これは 64 ビット Linux 専用のアセンブリであり、形状や形式にかかわらず、プラットフォームに依存しないことに注意してください。

これがあなたの質問に対する直接的な回答ではないことは承知していますが、インライン展開がない場合に Go でアセンブリを使用する方法として私が知っている最も簡単な方法です。インライン化が本当に必要な場合は、ソース ファイルからインライン アセンブリを抽出し、上記のパターンに従う方法で準備するスクリプトを作成することができます。十分近い?:)

Go、C、および Nasm の簡単な例: gonasm.tgz

更新: gccgo の新しいバージョンには -g フラグが必要であり、「go.main.hello」の代わりに「main.hello」のみが必要です。Go、C、および Yasm の更新された例を次に示します: goyasm.tgz

于 2011-06-30T13:36:31.697 に答える
25

Go プログラミング言語には、インライン アセンブラー言語コードをサポートする機能はなく、そうする予定もありません。Go は、アセンブラーCで書かれたルーチンへのリンクをサポートしています。GoにSWIG サポートを追加する実験的な機能があります。

于 2010-06-01T15:54:47.237 に答える
3

標準の Go コンパイラ (つまり、gccgo ではなく 8g+8l) の最適化パスは、基本的にバイナリ形式の生の命令を処理します。現在、コンパイラがコンパイラによって生成されたアセンブリをユーザー提供のインライン アセンブリ コードと区別する方法はありません (実装されていません) 。これが、Go コンパイラがインライン アセンブリを許可しない主な理由です。つまり、コンパイラ アーキテクチャが原因で、コンパイラはインライン アセンブリをサポートしていません。

もちろん、Go 言語自体には、他の Go 言語の実装 (つまり、他の Go コンパイラ) がインライン アセンブリをサポートすることを妨げるものは何もありません。インライン アセンブリはコンパイラ固有の決定であり、Go 言語自体とはほとんど関係ありません。

どちらの場合も、Go の型システムでは正確性をチェックできないため、インライン アセンブリは安全ではありません。インライン アセンブリを使用する必要がある関数は C などの言語で実装し、Go から C の関数を呼び出す方がよいようです。

于 2012-01-10T21:40:04.013 に答える