1. ホーム
  2. assembly

[解決済み] アセンブリで文字列の長さを表示する方法

2022-02-17 11:05:12

質問内容

以下のHello Worldプログラムを使用して、アセンブリを学習しています。

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
   mov  edx,len     ;message length
   mov  ecx,msg     ;message to write
   mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel

   mov  eax,1       ;system call number (sys_exit)
   int  0x80        ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;our string
    len equ $ - msg              ;length of our string

私が最初に抱いた疑問は、文字列の長さとは何を意味するのか、ということでした。文字数なのか、メモリ上の長さ(バイト数)なのか。 これを確認するために、私は変数lenを表示したいと思いました。どうすればいいのだろう?私は素朴に、新しい変数を定義しようとしました

    len2 equ $ - len

を実行し、その代わりに

   mov  edx,len2     ;message length
   mov  ecx,len     ;message to write
   mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel

を実行して len を表示しようとしましたが、これでは何も表示されません。どうすれば、lenで表される数値を表示できるのでしょうか?

どのように解決するのですか?

   ...
   mov  edx,len     ;message length

これは edx を何らかの数値で指定します。この例では14です。 len は定数記号で、次のようなものです。 #define C言語の場合

   mov  ecx,msg     ;message to write

これは ecx のアドレスで、最初の文字 ( msg はラベルで、メモリを指している)。

   mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel
   ...

    msg db 'Hello, world!', 0xa  ;our string

これは14バイトのメモリを定義しており、値は72('H')、101('e')、......です。最初のバイトは msg ラベル(それのメモリアドレス)。

    len equ $ - msg              ;length of our string

これは、定数 len コンパイル時に表示されます。これはメモリの内容を定義しないので、実行ファイルや実行中に見つけることはできません。 mov edx,len もちろん、その特定の命令にコンパイルされます)。

定義は $ - msg は、その $ は、次に定義されたマシンコードのバイトがコンパイルされる場所であるカレントアドレスとして機能します。 msg + 14 (文字数を正しく数えられたでしょうか :) ) 。そして ((msg+14) - msg) = 14 = の定義までの間にメモリ上に定義されたバイト数。 len とラベル msg .

ASMはより低レベルなので、メモリやバイトにラベルを付ける方がより正確な表現になりますし、微妙な違いを認識するのに役立つと思うので、変数や文字という言葉を避けていることに注目してください。

あなたの len2 equ $ - len の後に len は、このように値を定義しました。 len2 として (msg+14) (によって新たに追加されたバイトはありません。 len 定義) マイナス len というのは 14 を定義しているので、実質的には len2 と同じです。 msg .

では

   mov  edx,len2     ;message length
   mov  ecx,len     ;message to write
   ...

を呼び出します。 sys_write と等しい文字列へのポインタを持つ 14 (無効なメモリ参照で、そのメモリ領域は通常のユーザーコードではアクセスできない)、長さは、アドレス msg これは、32bのLinuxでは、非常に高い確率で、次のような値になります。 0x80004000 つまり、2Gを超える文字数が出力されることになります。

は、その sys_write は当然それを好まず、失敗し、エラーコードを eax .

でコンソールに何かを出力する場合 sys_write の場合、まずASCII(UbuntuのシェルではデフォルトでUTF8がサポートされていると思いますが、確認するのが面倒なので)エンコードされた文字列としてメモリに書き込んで、それを sys_write のアドレスと長さ(bytesとcharsの違いは重要です)を指定します。 sys_write は文字を認識せず、バイナリファイルとバイトを扱うので、長さはバイト数です)。

数字を出力するコードは数行になってしまうので書きません(簡略化した printf の実装)、SOではこの件に関して何度かQ+Aを行っていますが、私の説明で何が起きたのか、どのように動作するのかを理解していただければ幸いです。

ASMを学び始めたばかりの方は、以下のどちらかを検討してみてください。 clib を持つようにする。 printf 文字列の出力は、最初の算数や基本的なフロー制御、スタックの操作よりも少し高度なトピックです。基本的な命令の動作やデバッグの仕方に慣れてきたら、数値の出力に挑戦するのも楽になるでしょう。