あなたのコードは問題なく動作していて、最後に問題が発生したと思われます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 ローダーはロードされたコードを入力する前に適切なスタックをセットアップするため、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
$