20

より深いリンクプロセスとリンカースクリプトを理解しようとしています... binutils doc を見ると、いくつかのコマンドを追加することで改善された単純なリンカースクリプトの実装が見つかりました:

OUTPUT_FORMAT("elf32-i386", "elf32-i386",
          "elf32-i386")
OUTPUT_ARCH(i386)

ENTRY(mymain)

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

私のプログラムは非常に単純なプログラムです:

void mymain(void)
{
  int a;
  a++;
}

今、私は実行可能ファイルを構築しようとしました:

gcc -c main.c
ld -o prog -T my_script.lds main.o

しかし、実行しようとするprogと、SIGKILL起動中にエラーが発生します。プログラムがコンパイルされ、コマンドでリンクされると、次のことがわかります。

gcc prog.c -o prog

最終的な実行可能ファイルは、 のような他のオブジェクト ファイルの生成物でもありますが、crt1.o私の場合はどうでしょうか。このリンカー スクリプトを使用する正しい方法はどれですか?crti.ocrtn.o

4

2 に答える 2

23

あなたのコードは問題なく動作していて、最後に問題が発生したと思われますa++

mymain()は、呼び出し元に戻ろうとする通常の C 関数です。

しかし、それを ELF エントリ ポイントとして設定したため、ELF ローダーは、プログラム セグメントを適切な場所にロードしたらそこにジャンプするように指示されます。

crt1.oこれらの「 、crti.oおよび」のような他のオブジェクトファイルは、crtn.o通常、Cプログラムのこのようなものを処理します。C プログラムの ELF エントリ ポイントはそうではありませんmain()。代わりに、適切な環境を設定するラッパーですmain()(たとえば、プラットフォームに応じて、スタックまたはレジスタに引数argcと引数を設定します)、呼び出し(それが戻る可能性があります)、次にシステム コールを呼び出します (からのリターン コードで)。argvmain()exitmain()


[次のコメントを更新:]

であなたの例を試してみるとgdb、 から戻るときに実際に失敗することがわかりmymain()ます: にブレークポイントを設定しmymain、命令をステップ実行した後、インクリメントを実行し、関数のエピローグで問題が発生することがわかります:

$ gcc -g -c main.c
$ ld -o prog -T my_script.lds main.o
$ gdb ./prog
...
(gdb) b mymain
Breakpoint 1 at 0x10006: file main.c, line 4.
(gdb) r
Starting program: /tmp/prog 

Breakpoint 1, mymain () at main.c:4
4         a++;
(gdb) display/i $pc
1: x/i $pc
0x10006 <mymain+6>:     addl   $0x1,-0x4(%ebp)
(gdb) si
5       }
1: x/i $pc
0x1000a <mymain+10>:    leave  
(gdb) si
Cannot access memory at address 0x4
(gdb) si
0x00000001 in ?? ()
1: x/i $pc
Disabling display 1 to avoid infinite recursion.
0x1:    Cannot access memory at address 0x1
(gdb) q

少なくとも i386 の場合、ELF ローダーはロードされたコードを入力する前に適切なスタックをセットアップするため、ELF エントリ ポイントを C 関数に設定して適切な動作を得ることができます。ただし、上で述べたように、クリーン プロセスの終了は自分で処理する必要があります。また、C ランタイムを使用していない場合は、C ランタイムに依存するライブラリも使用しない方がよいでしょう。

元のリンカー スクリプトを使用した例を次に示しますがa、既知の値に初期化するように C コードを変更し、 exit(インライン アセンブリを使用して) の最終値をa終了コードとしてシステム コールを呼び出します。(注: 使用しているプラ​​ットフォームを正確に述べていないことに気付きました。ここでは Linux を想定しています。)

$ cat main2.c
void mymain(void)
{
  int a = 42;
  a++;
  asm volatile("mov $1,%%eax; mov %0,%%ebx; int $0x80" : : "r"(a) : "%eax" );
}
$ gcc -c main2.c
$ ld -o prog2 -T my_script.lds main2.o
$ ./prog2 ; echo $?
43
$ 
于 2011-08-24T23:59:30.560 に答える