より深いリンクプロセスとリンカースクリプトを理解しようとしています... 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.o
、crtn.o
などの他のオブジェクトファイルの製品でもありますが、私の場合はどうでしょうか。このリンカースクリプトを使用する正しい方法はどれですか?
コードは正常に実行されており、最後に問題が発生していると思われます。何が起こると思いますかafter _a++
_?
mymain()
は通常のC関数であり、呼び出し元に戻ろうとします。
ただし、これをELFエントリポイントとして設定しました。これは、プログラムセグメントが適切な場所にロードされると、ELFローダーにジャンプするように指示します。また、戻ることは期待されていません。
これらの「_crt1.o
_、_crti.o
_、_crtn.o
_などの他のオブジェクトファイル」は通常、Cプログラムでこのようなものを処理します。 CプログラムのELFエントリポイントはmain()
ではありません-代わりに、main()
の適切な環境を設定するラッパーです(たとえば、argc
とスタックまたはレジスタ内のargv
引数(プラットフォームに応じて)、main()
を呼び出し(戻る可能性があることを期待して)、次にexit
システムコールを呼び出します( main()
からの戻りコードを使用)。
[次のコメントを更新:]
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ローダーはロードされたコードを入力する前に適切なスタックをセットアップするので、can ELFエントリポイントをC関数に設定し、適切な動作を取得します。ただし、前述したように、クリーンなプロセス出口を自分で処理する必要があります。また、Cランタイムを使用していない場合は、Cランタイムに依存するライブラリも使用しない方がよいでしょう。
これがその例です。元のリンカースクリプトを使用していますが、Cコードを変更してa
を既知の値に初期化し、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
$
_
はい、Linuxで実行するには、.ldsファイルを変更する必要があります
SECTIONS
{
. = 0x8048000;
.text : { *(.text)
}