質問: nasmで1~100まで数える - Qiita 上記の記事を参考に次のコードを実行しますとエラーが返されました。調べたところオプションを設定する必要があるとかないとかのようですがよくわかりません、よろしくお願いします ``` extern printf ; printf関数を持ってくる section .data fmt: db "iteration: %d", 10, 0 ; printfで用いるフォーマット section .text global _main _main: mov rdx, 1 ; 初期値 mov rcx, 10 _loop: push rcx ; rcxの避難 push rdx ; rdxの避難 mov rdi, fmt ; フォーマット mov rsi, rdx ; カウントしている値 call printf ; printfをコール ; printfのコール時にレジスタの値が消えてしまう pop rdx ; rdxを持ってくる pop rcx ; rcxを持ってくる add rdx, 1 ; rdxに1加算 cmp rcx, 0 loop _loop ; ループバック fin: mov rax, 0x2000001 mov rdi, 0 syscall ``` エラー ``` $ ld -macosx_version_min 10.14 -lSystem -o p100 p100.o Undefined symbols for architecture x86_64: "printf", referenced from: _loop in p100.o ld: symbol(s) not found for inferred architecture x86_64 ``` また、 ``` mov rsi, rdx ``` としている理由がわかりません
回答: `Undefined symbols` の原因は `ld` が `libSystem.dynlib` を見つけることに失敗しているんだと思います(憶測:間違っている可能性大)。要するに `ld` に対するオプション `-L` が足らないんでしょう。オイラんとこには Mac 無いので具体的にどうすればよいかはわかりません。 `mov rsi, rdx` が必要なのは `x86-64` Application Binary Interface (ABI) 仕様がそのように求めているからです。詳しく説明すると本1冊になっちゃうので の System V AMD64 ABI あたりから参照のこと。 あなたのプログラムは `rcx` と `rdx` をレジスタ変数として使っているのですが ABI 仕様により、関数 (この例では `printf`) を呼び出す際の引数に `rcx` や `rdx` を使うことが定められているので、提示のように `push` / `pop` が必要となります。 と、言いたいところなんですが 提示プログラムは `x86-64 ABI` のレジスタ保存規則ならびにスタック仕様を満たしていないので正しく動きません。今動いているとしたら偶然です(障害が顕在化してないだけ)。 なぜrcxをpushする必要があるのか? でもレジスタ保存規則、スタック仕様を満たしていないので、動いているのは偶然です(指摘しようかなと思ったけど本筋でないので略したっす)。 現代 `ABI` はコンパイラを使う前提で設計されていて、アセンブラを手書きすることはほとんど全く想定されていません。関数は(この例では `main` が)その入口と出口で決められた仕様を満たすコードを必ず実装しなければならないことになっています。コンパイラなら忘れずに実装できますが、アセンブラ手書きだととても面倒。この辺の規約ならびに CPU の仕様を熟知した人間でないとアセンブラ手書きは実装コストに合いません。 本職でないのなら `x86-64` アセンブラの手書きを覚えるなどまったくもって遠回りで、学習コストのみ高くついて、でも得られるものが少ないです。オイラの後輩君がアセンブラ手書きを覚えたいと言ったなら一度は止めさせます(既にそれなりの知識があってかつ興味もあるのであれば GO させるでしょうが)本当にその必要があるのかどうかゆっくり検討してみてください。