アセンブラで呼び出したCの関数から戻り値を取得する方法


nasmとgccを用いてアセンブラを勉強中です  
動作環境はWindows11です

Cとアセンブラを連携するため、以下のコードを書きました

[code]

    ;Asm.asm
    
    bits 64
    
    extern print
    
    section .data
    message: db "Success", 10
    
    section .text
    global Test
    
    Test:
        mov rcx,message
        call print
        ret
    
[/code]

[code]

    //Win.c
    
    #include <stdio.h>
    
    extern char* Test();
    
    int main()
    {
            printf("%s",Test());
    }
    
    char* print(char* c)
    {
            printf("%s",c);
            return c;
    };
    
[/code]

`Win.c`の`print関数`を`Asm.asm`で呼び出し、その引数に渡した文字列`Success`を戻り値として`Asm.asm`側の`Test関数`で取得後、Cの`main関数`へ渡します  
`print関数`と`main関数`ではそれぞれ`printf関数`を宣言しています

以上のプログラムから、以下のコマンドを使用して、実行ファイル`a.exe`を生成します

[code]

    nasm -f elf64 Asm.asm
    gcc Win.c -c
    gcc Win.o Asm.o
    
[/code]

`a.exe`を起動すると、以下の結果が返ります

[code]

    Success
    
    
[/code]

`printf関数`はコード中で２度宣言しているので、もしもファイルが正しく実行される場合は、以下の結果が期待されます

[code]

    Success
    Success
    
[/code]

しかし、`Success`は一つしか表示されず、直下が空白行となります  
`print関数`の引数に与えた文字列は、そのスコープ内の`printf関数`に正しく渡っているようですが、その後の処理のどこかの段階でファイルが強制終了されている様子です

戻り値はx64の場合`raxレジスタ`に保存されるという解説を見たので、`print関数`からの戻り値は直接そのレジスタに渡っていると考え、直後にretを置きました

しかし、結果だけを見ると`Test関数`の戻り値は`main関数`へ返されていないように見えます  
どうすれば`print関数`からの戻り値`Success`を正しく`main関数`へ渡せるでしょうか？




`amd x64 abi` っぽく見えますがそうじゃないかもしれませんので以下は憶測なのですが

`amd x64 abi` のごく一部を解説した記事
[アセンブリ言語で書かれた関数をC言語に書き直す](https://ja.stackoverflow.com/questions/38341/)
では触れなかったのですが `amd x64 abi` では `rsp` レジスタを 16byte align
する義務があります。今ここで真面目に解説すると長くなるので外部記事へリンク  
[x64 でのスタックの使用](https://learn.microsoft.com/ja-jp/cpp/build/stack-
usage?view=msvc-170)  
[x86-64
モードのプログラミングではスタックのアライメントに気を付けよう](https://uchan.hateblo.jp/entry/2018/02/16/232029)

提示コードは  
\- C コンパイラが生成したコードが `call Test` する直前の `rsp` は 16 の倍数です。  
\- `call Test` 命令によって `rsp` は 8 減算されます（整合されなくなりました）  
\- その状態であなたのコードが `call print` をしてしまうと `rsp` が整合されていないので abi 違反  
\- つまり暴走しても強制終了しても文句言えない  
\- 正しく動かすには `rsp` の再整合を行う prologue epilogue コードが必要

もし正しく `print()` 関数が呼び出せてかつ戻ってこれるなら、その返却値が `rax`
に格納される、というのは認識の通りです。が、現状不正な手続きで `print()` 関数を呼んでいるため `print()` 処理中にエラーが発生し OS
によって強制終了させられています。なので結果的には「取得できない」ように見えています。 abi 仕様を満足させるべく `rsp`
のアラインメントを正しく取るように修正するとうまく動くと思われます。

# 既に指摘のある文字列の終端 `\0` に関しては正しく修正が必要

x86 や x64 の abi は、アセンブラコードを人間が手書きするには難しすぎてお勧めできないです。この例においても当該 `Test()` を C
ソースで書いてアセンブラ出力させると予想しないほど面倒な prologue epilogue コードが生成されているのが観測されるはずです。

`mov rcx,message` があなたの期待した動作をするかについては触れないでおくっス。多分こっちも修正する必要がありそう（ `nasm`
は全然使っていないので保留）

オイラの後輩君がアセンブラ始めたいって言うなら Arm Cortex とかをお勧めします。 Raspberry pi が流行してるのは伊達ではないっス。




`amd x64 abi` っぽく見えますがそうじゃないかもしれませんので以下は憶測なのですが

`amd x64 abi` のごく一部を解説した記事
[アセンブリ言語で書かれた関数をC言語に書き直す](https://ja.stackoverflow.com/questions/38341/)
では触れなかったのですが `amd x64 abi` では `rsp` レジスタを 16byte align
する義務があります。今ここで真面目に解説すると長くなるので外部記事へリンク  
[x64 でのスタックの使用](https://learn.microsoft.com/ja-jp/cpp/build/stack-
usage?view=msvc-170)  
[x86-64
モードのプログラミングではスタックのアライメントに気を付けよう](https://uchan.hateblo.jp/entry/2018/02/16/232029)

提示コードは  
\- C コンパイラが生成したコードが `call Test` する直前の `rsp` は 16 の倍数です。  
\- `call Test` 命令によって `rsp` は 8 減算されます（整合されなくなりました）  
\- その状態であなたのコードが `call print` をしてしまうと `rsp` が整合されていないので abi 違反  
\- つまり暴走しても強制終了しても文句言えない  
\- 正しく動かすには `rsp` の再整合を行う prologue epilogue コードが必要

もし正しく `print()` 関数が呼び出せてかつ戻ってこれるなら、その返却値が `rax`
に格納される、というのは認識の通りです。が、現状不正な手続きで `print()` 関数を呼んでいるため `print()` 処理中にエラーが発生し OS
によって強制終了させられています。なので結果的には「取得できない」ように見えています。 abi 仕様を満足させるべく `rsp`
のアラインメントを正しく取るように修正するとうまく動くと思われます。

# 既に指摘のある文字列の終端 `\0` に関しては正しく修正が必要

x86 や x64 の abi は、アセンブラコードを人間が手書きするには難しすぎてお勧めできないです。この例においても当該 `Test()` を C
ソースで書いてアセンブラ出力させると予想しないほど面倒な prologue epilogue コードが生成されているのが観測されるはずです。

`mov rcx,message` があなたの期待した動作をするかについては触れないでおくっス。多分こっちも修正する必要がありそう（ `nasm`
は全然使っていないので保留）

オイラの後輩君がアセンブラ始めたいって言うなら Arm Cortex とかをお勧めします。 Raspberry pi が流行してるのは伊達ではないっス。

