違いは本当にエミュレーションがちょうどOUTPUT_ARCH
とOUTPUT_FORMAT
より道以上を意味ということです。いくつかの詳細はほぼ明らかですが、--verbose
オプションで見ることができるデフォルトのリンカスクリプトの違いのように、this documentで記述されているものもありますが、ほとんどの回答は、比較エミュレーションscript for elf_i386およびemulation script for elf_x86_64などのソースでしか見つかりませんでした。違いはそれほど高いとは思われませんが、唯一の違いではなく、実際にあなたの特定のケースであなたに刺さるものは、diff
(ld
ビルド)ld/eelf_i386.c
とld/eelf_x86_64.c
ファイルの間には見えません。 bfdライブラリから得られる定数にまで下がり、でもはエミュレーションによって決まります。
だから、少しドリルダウンして何が起こるか見てみましょう。下のすべての場所でscript.lds
OUTPUT_ARCH
とOUTPUT_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
が本当にあなたには、いくつかのために必要な程度の出力形式(奇妙で正確場合:あなたが注意していると、たとえば、のような、する必要がある場合しかし、あなたは行うことができます)理由。
しかし、家で試してはいけません。適切なエミュレーションを使用して、この悪夢を忘れてください。
代わりに 'asm(" int $ 0x80 "::" a "(1)、" b "(0));あなたの問題を解決することはできませんが、a)あなたのasmを最小限に抑えます。b)gccが(少なくとも少しは)最適化を実行できるようにします。通常はアウトプットやクローバーもセットアップすることをお勧めしますが、終了コールではほとんど問題になりません。 –