2016-08-15 34 views
1

出力形式をi386に指定すると、私の実行はSIGSEGVになります。しかし、私は-m elf_i386オプションを使用すると、それは働いた。マニュアルページを確認すると、OUTPUT_FORMAT-oformatオプションに相当するので、これら2つは異なります。リンカのエミュレーション対OUTPUT_FORMAT

どのような場合に2つの違いがありますか?

例コード:

ファイルhello.c

int a = 1; 
int b; 
void _start() { 
    /* exit system call */ 
    asm("movl $1,%eax;" 
     "xorl %ebx,%ebx;" 
     "int $0x80" 
    ); 
} 

script.lds:OUTPUT_FORMATとOUTPUT_ARCHは私のプログラムの実行を助けるために何もしないように見えます。

/* OUTPUT_FORMAT("elf32-i386"); */ 
/* OUTPUT_ARCH(i386); */ 
OUTPUT(hello); 
ENTRY(_start); 

SECTIONS 
{ 
    .text 0x10000: 
    { 
    *(.text) 
    } 
    .data 0x8000000: 
    { 
    *(.data) 
    } 
    .bss : 
    { 
    *(.bss) 
    } 
} 

コマンドが実行されます。

gcc -m32 -nostdlib -g -c hello.c -o hello.o 
ld -m elf_i386 -T script.lds hello.o 
+2

代わりに 'asm(" int $ 0x80 "::" a "(1)、" b "(0));あなたの問題を解決することはできませんが、a)あなたのasmを最小限に抑えます。b)gccが(少なくとも少しは)最適化を実行できるようにします。通常はアウトプットやクローバーもセットアップすることをお勧めしますが、終了コールではほとんど問題になりません。 –

答えて

2

違いは本当にエミュレーションがちょうどOUTPUT_ARCHOUTPUT_FORMATより道以上を意味ということです。いくつかの詳細はほぼ明らかですが、--verboseオプションで見ることができるデフォルトのリンカスクリプトの違いのように、this documentで記述されているものもありますが、ほとんどの回答は、比較エミュレーションscript for elf_i386およびemulation script for elf_x86_64などのソースでしか見つかりませんでした。違いはそれほど高いとは思われませんが、唯一の違いではなく、実際にあなたの特定のケースであなたに刺さるものは、diffldビルド)ld/eelf_i386.cld/eelf_x86_64.cファイルの間には見えません。 bfdライブラリから得られる定数にまで下がり、でもはエミュレーションによって決まります。

だから、少しドリルダウンして何が起こるか見てみましょう。下のすべての場所でscript.ldsOUTPUT_ARCHOUTPUT_FORMATのコメントはです。

それでは、最初の結果の違いを見てみましょう:「悪い」のバイナリは、仮想ゼロのアドレスと0x00200000のアライメントとPT_LOADセグメントを持っていることを

$ ld -T script.lds hello.o 
$ LC_ALL=C objdump -p hello 

hello:  file format elf32-i386 

Program Header: 
    LOAD off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**21 
     filesz 0x00010048 memsz 0x00010048 flags r-x 
    LOAD off 0x00200000 vaddr 0x08000000 paddr 0x08000000 align 2**21 
     filesz 0x00000004 memsz 0x00000008 flags rw- 
    STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4 
     filesz 0x00000000 memsz 0x00000000 flags rw- 
$ ld -m elf_i386 -T script.lds hello.o 
$ LC_ALL=C objdump -p hello 

hello:  file format elf32-i386 

Program Header: 
    LOAD off 0x00001000 vaddr 0x00010000 paddr 0x00010000 align 2**12 
     filesz 0x00000048 memsz 0x00000048 flags r-x 
    LOAD off 0x00002000 vaddr 0x08000000 paddr 0x08000000 align 2**12 
     filesz 0x00000004 memsz 0x00000008 flags rw- 
    STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4 
     filesz 0x00000000 memsz 0x00000000 flags rw- 

注意してください。仮想アドレス0は正しいとは言えませんが、本当に失敗する理由を見てみましょう。デバッグは本当に面白いです。 1は、GDBを使用しようとすると、彼はこれを取得します。

(gdb) run 
Starting program: /somewhere/hello 
During startup program terminated with signal SIGSEGV, Segmentation fault. 
(gdb) bt 
No stack. 
(gdb) info registers 
The program has no registers now. 

ので、プログラムでも実行している実際に起動しません。さんはその後、straceを見てみましょう:

$ strace ./hello 
execve("./hello", ["./hello"], [/* 108 vars */]) = -1 EPERM (Operation not permitted) 
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- 
+++ killed by SIGSEGV +++ 

は、我々はそのexecve()戻りEPERMを参照してください。どのような種類の許可が失敗する可能性がありますか?まあ、仮想アドレス0のため、カーネルはELFを読み込もうとしますが、仮想アドレス0のファイルをマップしようとします.Linux 2.6.23回の間にそこにsecurity feature introducedがあったため、実行できません。しかし、それは構成することができるので、単純な後に

$ echo 0 > /proc/sys/vm/mmap_min_addr 

"悪い"バイナリが突然動作を開始します。

ここでは何かを作っているわけではありません(よろしくお願いします。)、私たちはldの動作の違いについてです。私たちの "悪い"バイナリと "良い"バイナリの違いは、読み込み可能なセグメントの配置です。そして、それについてしばらく考えてみると、ldの振る舞いが実際には絶対に正しいことがわかります。アラインメント制約が0x1000の場合、このアライメントに適したセグメント開始に0x10000の仮想アドレスを使用しますが、 .textをアドレス0x10000に配置するように指示していることを前提に、0x200000の配置制約があります。これは、他に選択肢がありませんが、ゼロのベース仮想アドレスを使用します。

この調整の必要条件はどこから得られますか? elf_i386とelf_x86_64のデフォルトのアライメントは最大ページサイズ(bfdからbfd_emul_get_maxpagesize()を取得)ですが、これらのアーキテクチャではページサイズが異なるため、エミュレーションの項目に戻ります。

あなたが実際にelf_i386エミュレーションなしであなたのバイナリをビルドしていますが、同じように、パラメータを経由して最大ページサイズを指定する必要があることを行うためにすることができます

$ ld -T script.lds -z max-page-size=0x1000 hello.o 

この結果のバイナリますmmap_min_addrの調整なしでだけでなく、仕事しかし、適切なelf_i386エミュレーションで構築されたものとビット単位で同じになります。

元の質問に戻って—違いは巨大で微妙なものです。ソフトウェアをビルドするときは、正しいエミュレーションを使用したいと思っています。あなたのOUTPUT_FORMATがあなたのエミュレーションパラメータに非常によく似たものになる時間の99.99%。

しかし、まあ。いくつかのケースがあります。あなたが通常はしないこと。

$ head -n 1 script.lds 
OUTPUT_FORMAT("srec"); 
$ ld -T script.lds hello.o 
$ file hello 
hello: Motorola S-Record; binary data in text format 

あなたのエミュレーションは一つのことで、OUTPUT_FORMATが本当にあなたには、いくつかのために必要な程度の出力形式(奇妙で正確場合:あなたが注意していると、たとえば、のような、する必要がある場合しかし、あなたは行うことができます)理由。

しかし、家で試してはいけません。適切なエミュレーションを使用して、この悪夢を忘れてください。

+0

そのような有益なポストをありがとう! – Amumu

関連する問題