10

フォローアップELF 実行エントリ ポイントの仮想アドレスが、0x0 ではなく 0x80xxxxx の形式になっているのはなぜですか? Linux バイナリの仮想メモリ アドレスが 0x8048000 から始まるのはなぜですか? ld、デフォルトとは異なるエントリ ポイントを使用できないのはなぜld -eですか?

その場合segmentation fault、デフォルトのエントリ ポイントに近いアドレスであっても、リターン コード 139 が返されます。なんで?

編集:

質問をより具体的にします。

        .text
        .globl _start    
_start:
        movl   $0x4,%eax        # eax = code for 'write' system call   
        movl   $1,%ebx          # ebx = file descriptor to standard output
        movl   $message,%ecx    # ecx = pointer to the message
        movl   $13,%edx         # edx = length of the message
        int    $0x80            # make the system call
        movl   $0x0,%ebx        # the status returned by 'exit'
        movl   $0x1,%eax        # eax = code for 'exit' system call
        int    $0x80            # make the system call
        .data
        .globl message
message:        
        .string "Hello world\n" # The message as data

これを でコンパイルしas program.s -o program.oて静的にリンクするとld -N program.o -o program、テキスト セグメントの およびエントリ ポイントとしてreadelf -l program表示0x0000000000400078されます。実行すると、「Hello world」が出力されます。VirtAddr0x400078

しかし、ld -N -e0x400082 -Ttext=0x400082 program.o -o program(テキストセグメントとエントリポイントを4バイト移動して)リンクしようとすると、プログラムはkilled. で検査するとreadelf -l、タイプ の 2 つの異なるヘッダーが表示されます。1LOADつは at で0x0000000000400082、もう 1 つは at0x00000000004000b0です。

試してみると、すべて機能し、セクション0x400086は 1 つしかありません。LOAD

  1. 何が起きてる?
  2. 選択できるメモリアドレス、選択できないメモリアドレス、およびその理由は?

ありがとうございます。

4

1 に答える 1

25

ld -e でデフォルトとは異なるエントリ ポイントを ld に使用させることができないのはなぜですか

きっとできます。これ:

int foo(int argc, char *argv[]) { return 0; }

gcc main.c -Wl,-e,foo

実行がメインで開始されないため、機能しません。_start(glibc の一部)からリンクされている で始まり、crt0.o動的リンクなどを適切に起動するように手配します。にリダイレクト_startすることfooで、必要な glibc の初期化をすべてバイパスしたため、動作しません。

しかし、動的リンクを必要とせず、glibc が通常行うことを喜んで行う場合は、エントリ ポイントに好きな名前を付けることができます。例:

#include <syscall.h>

int foo()
{
  syscall(SYS_write, 1, "Hello, world\n", 13);
  syscall(SYS_exit, 0);
}

gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out
Hello, world

ああ、この質問のタイトルは実際の質問と一致しません (bad idea(TM))。

タイトルの質問に答えるために、実行可能ファイルがリンクされているアドレスを変更できます。デフォルトでは、0x8048000ロード アドレスを取得します (32 ビットのみ。64 ビットのデフォルトは です0x400000)。

たとえば、リンク行に0x80000追加することで、簡単に変更できます。-Wl,-Ttext-segment=0x80000

アップデート:

しかし、ld -N -e0x400082 -Ttext=0x400082 program.o -o program とリンクしようとすると (テキスト セグメントとエントリ ポイントを 4 バイト移動)、プログラムが強制終了されます。

セクションのアラインメント制約 (4)に違反せずに に割り当てることは不可能Ttextです。.text アドレスを少なくとも 4 バイト境界に揃える (または の必要な配置を変更する) 必要があります。0x400082.text.text

開始アドレスを 0x400078、0x40007c、0x400080、0x400084、...、0x400098 に設定し、GNU-ld 2.20.1 を使用すると、プログラムは動作します。

ただし、binutils の現在の CVS スナップショットを使用すると、プログラムは 0x400078、0x40007c、0x400088、0x40008c で動作し、0x400080、0x400084、0x400090、0x400094、0x400098 で強制終了されます。これはリンカーのバグである可能性があります。または、他の制約に違反しています (どちらかわかりません)。

この時点で、本当に興味がある場合は、binutils ソースをダウンロードして をビルドし、 1 つではなく 2 つのセグメントldが作成される正確な原因を突き止めることをお勧めします。PT_LOAD

更新 2:

LMA が重複しているセクションの新しいセグメントを強制します。

ああ!それ.dataは、邪魔にならないように移動する必要があることを意味します。これにより、実行可能な実行可能ファイルが作成されます。

ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180
于 2011-11-14T05:41:34.070 に答える