1
static int func_name (const uint8_t * address)
{
    int result;
    asm ("movl $1f, %0; movzbl %1, %0; 1:"
   : "=&a" (result) : "m" (*address));

    return result;
}

私はインターネットを介してインラインアセンブリの参照を調べました。しかし、私はこのコードが何をしているのか理解できません。$ 1fとは何ですか?そして、「m」はどういう意味ですか?「=r」と「r」を使用する通常のインライン規則ではありませんか?

4

3 に答える 3

4

$1f1ラベルのアドレスです。は、順方向にf指定された最初のラベルを検索するように指定します。メモリ内にある入力オペランドです。レジスタを使用する出力オペランドです。使用するレジスタを指定し、それを出力オペランドにし、他のオペランドが同じレジスタを共有しないことを保証します。1"m""=&a"eaxa=&

ここでは、第1オペランド(レジスタ)と第2オペランド(が指すアドレス)%0に置き換えられます。eax%1address

これらすべておよびそれ以上のことは、インラインアセンブリおよびasm制約に関するGCCドキュメントで説明されています。

于 2013-02-17T14:40:54.203 に答える
2

このコードは(2つのタイプミスのためにコンパイルできないことを除けば)ほとんど役に立ちません。

これはそれが変わるものです(-Sスイッチを使用してください):

_func_name:
        movl 4(%esp), %edx ; edx = the "address" parameter
        movl $1f, %eax ; eax = the address of the "1" label
        movzbl (%edx), %eax; eax = byte from address in edx, IOW, "*address"
     1:
        ret

したがって、関数の本体全体を次のように置き換えることができます

return *address;
于 2013-02-17T14:51:09.340 に答える
2

コードは機能的には同じですがreturn *address、このwrtと完全に同等ではありません。生成されたバイナリ/オブジェクトファイルに。

ELFでは、前方参照を使用すると(つまりmov $1f, ...、アセンブリローカルラベルのアドレスを取得するために)、いわゆる再配置が作成されます。再配置とは、リンク/ロード時にのみ認識される値を挿入するための、リンカーへの(実行可能ファイルの作成時または後で実行可能ファイル/ライブラリのロード時の動的リンカーへの)命令です。オブジェクトコードでは、これは次のようになります。

セクション.textの逆アセンブル:

0000000000000000:
   0:b8 00 00 00 00 mov $ 0x0、%eax
   5:0f b6 07 movzbl(%rdi)、%eax
   8:c3 retq

.text実際には正しくありませんが、ここでは値(セクションのオフセット1)がゼロであることに注意してください。これは、実行中のコードのどこで関数が終了するかによって異なります。最終的にこれを知ることができるのは(ダイナミック)リンカだけであり、このメモリがロードされるときに更新する必要があるという情報は、実際にはオブジェクトファイルに配置されます。

$ readelf -a xqf.o
ELFヘッダー:
[...]
セクションヘッダー:
  [Nr]名前タイプアドレスオフセット
       サイズEntSizeフラグリンク情報整列
  [0] NULL 0000000000000000 00000000
       0000000000000000 0000000000000000 0 0 0
  [1] .text PROGBITS 0000000000000000 00000040
       0000000000000009 0000000000000000 AX 0 0 16
  [2] .rela.text RELA 0000000000000000 000004e0
       0000000000000018 0000000000000018 10 1 8
[...]
オフセット0x4e0の再配置セクション'.rela.text'には、次の1つのエントリが含まれています。
  オフセット情報タイプSym。値記号 名前+加数
000000000001 00020000000a R_X86_64_32 0000000000000000 .text + 8
[...]

このELFセクションのエントリには次のように書かれています。

  • セクションへのオフセット1を見てください.text
  • 64ビットに符号拡張される32ビット値があります(R_X86_64_32
  • (リンカーとして)配置する必要のある値は、.text + 8(リンク/ロード時に計算する必要があります)の値です。

このエントリは、指示のおかげで作成されmov $1f, %0ます。それを省く(または単に書くreturn *address)と、そこにはありません。

static修飾子を削除して、上記のコード生成を強制しました。そうしないと、単純なコンパイルでは実際にはコードがまったく作成されません(static使用しない場合はコードが削除され、使用する場合は多くの場合インライン化されます)。

前述のように、関数はstatic、通常、コンパイラによって呼び出しサイトでインライン化されるためです。したがって、それが使用される情報は、それをインストルメント化するデバッガーの機能と同様に、通常は失われます。ただし、ここに示すトリックは、関数の使用ごとに1つの再配置エントリが作成されるため、これを(間接的に)回復できます。それに加えて、このようなメソッドを使用して、バイナリ内にインストルメンテーションポイントを確立できます。よく知られている/厳密に定義されているが機能的に意味のない小さなアセンブリステートメントをオブジェクトファイル形式で回復可能な場所に挿入し、必要に応じてデバッガ/トレースユーティリティでそれらを「より便利な」ものに置き換えます。

于 2013-02-19T10:29:01.083 に答える