- ベストアンサー
インラインアセンブラの関数について質問です
- C言語で書かれたプログラムの中に、アセンブラで書かれた関数を使うため、インラインアセンブラで関数を作っていたのですが、成功するのかふと疑問に思ったのです。
- 元のアセンブラの関数は、OS○作○門という本に載っていたもので、アセンブラの種類はnasmを基にしたnaskです。
- 質問のサポートページが凍結状態で回答が返ってこないため、こちらで質問させていただきました。書いたプログラムでコンパイルエラーが発生しています。
- みんなの回答 (13)
- 専門家の回答
質問者が選んだベストアンサー
gcc/gasの流儀に従って書くならこうじゃないでしょうか。 void write_mem82(int addr, int data) { char data8 = (char)data; __asm__( "movb %1, (%0)\n\t" : : "r"(addr), "r"(data8) ); } 「GCC インラインアセンブラ」でググれば情報は見つかります。
その他の回答 (12)
- wormhole
- ベストアンサー率28% (1626/5665)
なぜインラインアセンブラ中にretを書いたらいけないかですがサンプルのソースを見た方がわかりやすいかと思います。 FreeBSDのgccでのコンパイルなので本当に参考程度ですが。 % cat halt.c void io_hlt2(void) { __asm__ __volatile__( "hlt\n\t" "ret" ); } % cc -m32 -O2 -S halt.c % cat halt.s .file "halt.c" .text .p2align 4,,15 .globl io_hlt2 .type io_hlt2, @function io_hlt2: pushl %ebp movl %esp, %ebp #APP hlt ret #NO_APP popl %ebp ret .size io_hlt2, .-io_hlt2 .ident "GCC: (GNU) 4.2.1 20070831 patched [FreeBSD]" .section .note.GNU-stack,"",@progbits % ラベルのio_hlt2:からpopl %ebpの次のretまでがio_hlt2関数で、#APP~#NO_APPの間がインラインアセンブラの出力です。 上記の例だとpopl %ebpしてからretすべきなのに、インラインアセンブラ中でそれをせずにretしてしまってます。 またコンパイラが用意しているretも無視することになります。 popl %ebpに関してはコンパイルオプションによりする必要のないケースもありますが、それを期待したコードにすべきでもありません。
- wormhole
- ベストアンサー率28% (1626/5665)
#7の補足で解決はしてる?んですね。 >static __inline__ void io_hlt2(void){ >__asm __volatile ( >"HLT\n\t" >"RET" > >); > >} はHLTがスキップされずに >static __inline__ void io_hlt2(void){ >__asm__ ( >"HLT" >); > >} はHLTがスキップされるというのはわかりませんが。 __asm__ __volatile__("HLT"); の方がいいですけど、HLTがスキップされるのとは関係ないでしょうし・・・ retは書いてはいけないのは確かなんだけどなぁ・・・(コンパイルできても実行としてはおかしいはず)
- wormhole
- ベストアンサー率28% (1626/5665)
もしかしたら"HLT"は"hlt"と書かないといけないのかも。
- wormhole
- ベストアンサー率28% (1626/5665)
>でも教えてくださったコードで、なぜかエラーが出ます。インラインアセンブラを使わない場合のものも、載せておきます。 エラーメッセージちゃんと読んでますか? 「エラーを訳したりせずに、そのまま書きましょう。」とは書きましたが、あなた自身が読む必要はないなどとはいってませんけど。 どう見ても >./z_tools/gas2nask.exe -a bootpack.gas bootpack.nas >skip:HLT スキップされてるのはio_hlt2()のHLTです(私はwrite_mem82()のコードしか書いてない) >static __inline__ void io_hlt2(void){ >__asm__ ( >"HLT" >); > >} もしかしてですが、gas2nask.exeがhltには対応してないんじゃないでしょうか。 またはhltではなく別の表記とか。 >個人的にはインラインアセンブラを使わない方法に、興味があるのですが、一体どんな勉強をすればよいの>か、教えていただけるとありがたいです。 ふつうにC言語の勉強をすればよいだけかと。 >回答くださりありがとうございます。正直なところ、耳が痛いです。苦言ごもっともだと思います。しかし、恵まれた環境におられる方ならば、わざわざここまで質問をしないと思います。すぐ近くに (以下略) ご自分が恵まれた環境にないとおっしゃられてるようですけど、とっても恵まれてる方だと思いますよ。 インターネットで検索して調べるなんてことができるわけですから。 近くに聞く人がいないとかは些細なこと。
- wormhole
- ベストアンサー率28% (1626/5665)
>よろしくおねがいいたします。私もがんばります。 「も」じゃないです。 一番がんばらないといけないのは、あなたです。 回答者ががんばってくれるかどうかは回答者次第。
補足
回答くださりありがとうございます。正直なところ、耳が痛いです。苦言ごもっともだと思います。しかし、恵まれた環境におられる方ならば、わざわざここまで質問をしないと思います。すぐ近くにいる誰かに聞けばよいわけですから。でも、そうじゃない人間もこうしてここにいるわけですから、私の表現が悪かったのだと思いますが、がんばる材料を、皆さま方いただいているという意味で、感謝いたします。
- wormhole
- ベストアンサー率28% (1626/5665)
#7の方が既に書いていらっしゃいますが、ログ中の >../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas は、gas形式のアセンブラソースからnasm形式のアセンブラソースへコンバートしてるようなので gccのインラインアセンブラで書くのはgas形式だと思います。 で、書くならたぶん static __inline__ void write_mem82(int addr, int data){ __asm__("movb %1,%0": "=m"(*(char *)addr): "r"((char)data): "memory"); } だと思います。 で、実はこれインラインアセンブラ使う必要がなくて static __inline__ void write_mem82(int addr, int data){ *(char *)addr = (char)data; } で済むはずです。 #1でも既に書いてることもありますが ・[ESP+4]がaddr,[ESP+8]がdataのようなスタックレジスタ相対などの決めうちは× ・インラインアセンブラでretを書くのは× (例えば例で書かれているmain関数でwrite_mem82()がインライン展開されたらいっしょにretも展開されます) ・インラインアセンブラ内では好き勝手にレジスタは使用できず使えるものは決まってます(この辺はCコンパイラのマニュアルなど読んでください) ・C言語は高級アセンブラともいわれるくらいなのでアセンブラでできるほとんどの事はC言語でもインラインアセンブラを使わずに書けます。インラインアセンブラはC言語で書けないような事を書くときだけにしておいた方がいいです。 ・C言語がどのようなコードを出力するのか想像できないのであればインラインアセンブラは使わない方がいいです。最適化のつもりがコンパイラのする最適化の足をひっぱったりします。
補足
回答くださりありがとうございます。解説がわかりやすくて助かります。でも教えてくださったコードで、なぜかエラーが出ます。インラインアセンブラを使わない場合のものも、載せておきます。せっかくいろんなことを教えてくださったのに、もったいないです。ありがとうございました。個人的にはインラインアセンブラを使わない方法に、興味があるのですが、一体どんな勉強をすればよいのか、教えていただけるとありがたいです。 bootpac.c アセンブラありーーーーーーーーーーーーーーーーーー static __inline__ void io_hlt2(void){ __asm__ ( "HLT" ); } static __inline__ void write_mem82(int addr, int data){ __asm__("movb %1,%0": "=m"(*(char *)addr): "r"((char)data): "memory"); } void HariMain(void) { int i; for (i = 0xa0000; i <= 0xaffff; i++) { write_mem82(i, i & 0x0f); } for (;;) { io_hlt2(); } } エラーログーーーーーーーーーーーーーーーーーーーーーーーーーーーー C:\TEST\test>make run C:\TEST\test>..\z_tools\make.exe run ../z_tools/make.exe -r img make.exe[1]: Entering directory `C:/TEST/test' ../z_tools/make.exe -r haribote.img make.exe[2]: Entering directory `C:/TEST/test' ../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp ack.c ../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas skip:HLT make.exe[2]: *** [bootpack.nas] Error 1 make.exe[2]: Leaving directory `C:/TEST/test' make.exe[1]: *** [img] Error 2 make.exe[1]: Leaving directory `C:/TEST/test' ..\z_tools\make.exe: *** [run] Error 2 C:\TEST\test> ここまでーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー bootbac.cアセンブラなしーーーーーーーーーーーーーーーーーーーーーーー static __inline__ void io_hlt2(void){ __asm__ ( "HLT" ); } static __inline__ void write_mem82(int addr, int data){ *(char *)addr = (char)data; } void HariMain(void) { int i; for (i = 0xa0000; i <= 0xaffff; i++) { write_mem82(i, i & 0x0f); } for (;;) { io_hlt2(); } } エラーログーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー C:\TEST\test>make run C:\TEST\test>..\z_tools\make.exe run ../z_tools/make.exe -r img make.exe[1]: Entering directory `C:/TEST/test' ../z_tools/make.exe -r haribote.img make.exe[2]: Entering directory `C:/TEST/test' ../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp ack.c ../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas skip:HLT make.exe[2]: *** [bootpack.nas] Error 1 make.exe[2]: Leaving directory `C:/TEST/test' make.exe[1]: *** [img] Error 2 make.exe[1]: Leaving directory `C:/TEST/test' ..\z_tools\make.exe: *** [run] Error 2 C:\TEST\test> ここまでーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
- m-take0220
- ベストアンサー率60% (477/782)
"MOV EAX,[ESP+4]\n\t" "MOV AL,[ESP+8]\n\t" "MOV [EAX],AL\n\t" "RET" だとどうですか? 前回の回答に書いた通り、検索で出てきた例の記述方法をまねているだけなので、うまくいくかはわかりません。 #ちなみに、「的を得ている」ではなく「的を射ている」です。
補足
回答くださりありがとうございます。 さっそく、試してみたところ。次のようになりました。 C:\TEST\test>make run C:\TEST\test>..\z_tools\make.exe run ../z_tools/make.exe -r img make.exe[1]: Entering directory `C:/TEST/test' ../z_tools/make.exe -r haribote.img make.exe[2]: Entering directory `C:/TEST/test' ../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp ack.c ../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas skip:MOV ECX, [ESP+4] skip:MOV AL,[ESP+8] skip:MOV [ECX], AL skip:RET skip:HLTRET make.exe[2]: *** [bootpack.nas] Error 1 make.exe[2]: Leaving directory `C:/TEST/test' make.exe[1]: *** [img] Error 2 make.exe[1]: Leaving directory `C:/TEST/test' ..\z_tools\make.exe: *** [run] Error 2 C:\TEST\test> です。 先ほどと違うのは、命令を、1つずつ認識している点です。 しかしスキップされています。 このスキップさえ止めればうまくいきそうです。 よろしくおねがいいたします。私もがんばります。
- Tacosan
- ベストアンサー率23% (3656/15482)
なんでいろいろと伏せる必要があるのか, さっぱりわからん. そのくらいやましいことをしているという自覚がある? あるいは, あえて情報量を減らすことによって回答者に不要な労力を強いるという策略? さておき, #2 にもあるように「インラインアセンブラの構文はコンパイラごとに異なる」んだから, 自分の使っているコンパイラに合わせて書き直してください. なお, インラインアセンブラに手を出すくらいなら「どう書き直せばいいのか」などという質問はしないように.
補足
回答くださりありがとうございます。伏せているのは、ただ単に私が臆病ものだからです。だったらこんなところに出てくるべきではないと、突っ込まれそうですが、どうしてもわからなかったから、ほかに良い方法がなかったのです。 >なお, インラインアセンブラに手を出すくらいなら「どう書き直せばいいのか」などという質問はしないように. と、くぎを刺されましたが、最悪の場合、「どう書き直せばいいのか」を質問せねばなりません。そのためにこういうサイトがあるのではないでしょうか。 #5の方には、申し訳ないのですが、私に釘をさすことだけが目的に感じられ、私の質問の答えになっておりません。すでに#2の方の転記だけです。今後、こういった投稿をなさるのならば、私が疲れるので、無視しますので、ご了承ください。
- m-take0220
- ベストアンサー率60% (477/782)
検索すると、コロンのあとはレジスタの条件等を指定することとなっています。 だとすると、コロンの後に"MOV AL,[ESP+8]"といった命令があると、エラーになるのでは? asmへの命令は、改行やセミコロンで区切ることになっていると思います。 C言語では、並べて描かれた文字列リテラルは連結されるので、 "MOV EAX,[ESP+4]" "MOV AL,[ESP+8]" "MOV [EAX],AL" "RET" は "MOV EAX,[ESP+4]MOV AL,[ESP+8]MOV [EAX],ALRET" と同じになるので、うまくいかないのでは? 検索してみると、命令の最後に\n\tを入れているコードを見かけます。 もしくは、コードがそのままコピーしてきたものなら、途中にマルチバイト文字(所謂全角文字)があるせいだとか。 使用するデータを、関数呼び出し時にスタックにある前提で取り出していますが、Cの変数をレジスタに設定する方法がある(前述のコロンを使用するもの)ので、そちらの方法にしたほうがいいように思いますが。 ちなみに、伏字で書いても、伏字部分が容易に推測できる場合には、法的責任を回避することはできません。 仮名で書かれた文章でも、内容から実在の人物が推測できるような場合に、プライバシーの侵害を認めた裁判例があります。 まあ、今回の内容なら、タイトルを堂々と書いても責任を問われるようなことはないでしょうが。
補足
回答ありがとうございます。私は、性分が臆病ものなので、書籍の名前を伏字で出すのも、ここに質問するのも、怖かったのですが、どうしても解決したくなり、質問するに至りました。 本論に入ります。 おっしゃっておられることが、的を得ていると思います。実際に、コロンなしでコンパイルするとエラーがおっしゃるとおりになりました。プログラムと、エラーメッセージを後で書いておきます。 コロンを使う方法で調べたのですが、グーグルにもあまりなくて、こういう場合どう書けばよいのか、具体例がないので、(あるのかもしれませんが私には見つけることができませんでした。)できれば、コロンありの場合を、どう書けばよかったのかを、ご教授願えないでしょうか。よろしくおねがいいたします。答えを聞くようで、こういうことを願い出るのはためらわれたのですが、もはや、私の頭の中がパニックに近い状態で、一体どの情報が正しいのかわからなくなっているので、何卒よろしくお願いいたします。 bootpack.c---------------------- static __inline__ void io_hlt2(void){ __asm __volatile ( "HLT" "RET" ); } static __inline__ void write_mem82(int addr, int data){ __asm __volatile ( "MOV ECX, [ESP+4]" "MOV AL,[ESP+8]" "MOV [ECX], AL" "RET" ); } void main(void) { int i; for (i = 0xa0000; i <= 0xaffff; i++) { write_mem82(i, i & 0x0f); } for (;;) { io_hlt2(); } } エラーメッセージーーーーーーーーーーーーーーーーーー C:\TEST\test>make run C:\TEST\test>..\z_tools\make.exe run ../z_tools/make.exe -r img make.exe[1]: Entering directory `C:/TEST/test' ../z_tools/make.exe -r haribote.img make.exe[2]: Entering directory `C:/TEST/test' ../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp ack.c ../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas skip:MOV ECX, [ESP+4]MOV AL,[ESP+8]MOV [ECX], A LRET skip:HLTRET make.exe[2]: *** [bootpack.nas] Error 1 make.exe[2]: Leaving directory `C:/TEST/test' make.exe[1]: *** [img] Error 2 make.exe[1]: Leaving directory `C:/TEST/test' ..\z_tools\make.exe: *** [run] Error 2 C:\TEST\test> 以上です。 よろしくお願いします。
- m-take0220
- ベストアンサー率60% (477/782)
> "MOV EBX,[ESP+4]": 最後のコロンは必要ですか?
補足
回答くださりありがとうございます。コロンを外すと、私にはよくわかっていないのですが、アセンブラの部分を、すべてスキップしてしまいます。なぜ、そこにコロンをううとうまくいくのかの理由は、わかりません。よろしくおねがいいたします。
- 1
- 2
補足
回答くださりありがとうございます。結果から申しますと。無事動きました。!!エラーもありません。というか、私もこんなコードをかけるように勉強したいです。検索のキーワードをあげてくださっていますが、私が気付いていない可能性が高いので、お勧めのサイトがあれば、(書籍でもいいです。)紹介していただきたいのですが、お願いできますでしょうか。(大げさかもしれませんが、本心では弟子にしてほしいぐらいです。感動しました。でもさっぱり分かっていないように思います。)実際にコンパイルしたソースと実行結果のログを載せておきます。ありがとうございました。 bootpack.c -------------------------- static __inline__ void io_hlt2(void){ __asm __volatile ( "HLT\n\t" "RET" ); } void write_mem82(int addr, int data) { char data8 = (char)data; __asm__( "movb%1, (%0)\n\t" : : "r"(addr), "r"(data8) ); } void HariMain(void) { int i; for (i = 0xa0000; i <= 0xaffff; i++) { write_mem82(i, i & 0x0f); } for (;;) { io_hlt2(); } } 結果のログーーーーーーーーーーーーーーーーーーーーーーーー C:\TEST\test>make run C:\TEST\test>..\z_tools\make.exe run ../z_tools/make.exe -r img make.exe[1]: Entering directory `C:/TEST/test' ../z_tools/make.exe -r haribote.img make.exe[2]: Entering directory `C:/TEST/test' make.exe[2]: `haribote.img' is up to date. make.exe[2]: Leaving directory `C:/TEST/test' make.exe[1]: Leaving directory `C:/TEST/test' copy haribote.img ..\z_tools\qemu\fdimage0.bin 1 個のファイルをコピーしました。 ../z_tools/make.exe -r -C ../z_tools/qemu make.exe[1]: Entering directory `C:/TEST/z_tools/qemu' qemu.exe -L . -m 32 -localtime -std-vga -fda fdimage0.bin make.exe[1]: Leaving directory `C:/TEST/z_tools/qemu' C:\TEST\test> ここまでーーーーーーーーーーーーー 回答いただくまで作っていたプログラムがあるのですが、エラーが1つだけありHLTをスキップしてしまうものでした。ついでに載せておきます。 bootpack.c------------------------ここから asm("io_hlt2:"); asm(" haltl"); asm(" ret"); extern void io_hlt2(void); asm("write_mem82:"); asm(" movl %ecx, [%esp+4]"); asm(" movl %al, [%esp+8]"); asm(" movl [%ecx], %al"); asm(" ret"); extern void write_mem82(int addr, int data); void HariMain(void) { int i; for (i = 0xa0000; i <= 0xaffff; i++) { write_mem82(i, i & 0x0f); } for (;;) { io_hlt2(); } } ここまでーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー よろしくおねがいいたします。