|
发表于 2004-10-8 12:38:00
|
显示全部楼层
下面是我的观点。
gcc 中调用一个普通函数时,首先将函数的参数压入堆栈 (按从右至左的顺序),然后压入程序记数器 (pc) 的值,就将新的 pc 值装入执行函数中的代码了。从上面的汇编代码来看,手工的与自动的代码在这一点上没有太大的区别。
如果是 main 函数,编译器会在编译时做一些其他的工作,将初始化的代码插入到 main() 之前或之中。如果只是一般的函数,编译器是不会做这些工作的。至于连接器,只是做将一个或几个二进制文件组合起来,重新定位它们的数据并将符号集中到一起这样简单的工作,是不会插入新的代码的。
下面是自动编译的代码产生的指令:
- (gdb) b 1
- Breakpoint 1 at 0x80482c0: file ../sysdeps/i386/elf/start.S, line 1.
- (gdb) run
- Starting program: /home/herbert/x2
- Breakpoint 1, _start () at ../sysdeps/i386/elf/start.S:47
- 47 ../sysdeps/i386/elf/start.S: 没有那个文件或目录.
- in ../sysdeps/i386/elf/start.S
- Current language: auto; currently asm
- (gdb) disassemble
- Dump of assembler code for function _start:
- 0x080482c0 <_start+0>: xor %ebp,%ebp
- 0x080482c2 <_start+2>: pop %esi
- 0x080482c3 <_start+3>: mov %esp,%ecx
- 0x080482c5 <_start+5>: and $0xfffffff0,%esp
- 0x080482c8 <_start+8>: push %eax
- 0x080482c9 <_start+9>: push %esp
- 0x080482ca <_start+10>: push %edx
- 0x080482cb <_start+11>: push $0x8048410
- 0x080482d0 <_start+16>: push $0x80483b0
- 0x080482d5 <_start+21>: push %ecx
- 0x080482d6 <_start+22>: push %esi
- 0x080482d7 <_start+23>: push $0x8048384
- 0x080482dc <_start+28>: call 0x80482a0 <_init+40>
- 0x080482e1 <_start+33>: hlt
- 0x080482e2 <_start+34>: nop
- 0x080482e3 <_start+35>: nop
- End of assembler dump.
复制代码
下面是手工指定的指令:
- (gdb) b 1
- No symbol table is loaded. Use the "file" command.
- (gdb) run
- Starting program: /home/herbert/x
- (no debugging symbols found)...(no debugging symbols found)...
- Program received signal SIGSEGV, Segmentation fault.
- 0x080481e9 in x ()
- (gdb) disassemble
- Dump of assembler code for function x:
- 0x080481e0 <x+0>: push %ebp
- 0x080481e1 <x+1>: mov %esp,%ebp
- 0x080481e3 <x+3>: sub $0x8,%esp
- 0x080481e6 <x+6>: mov 0xc(%ebp),%eax
- 0x080481e9 <x+9>: mov (%eax),%eax
- 0x080481eb <x+11>: mov %eax,0x4(%esp)
- 0x080481ef <x+15>: movl $0x8048207,(%esp)
- 0x080481f6 <x+22>: call 0x80481c0
- 0x080481fb <x+27>: movl $0x0,(%esp)
- 0x08048202 <x+34>: call 0x80481d0
- End of assembler dump.
复制代码
我认为段错误就是没有调用 <_init+40> 造成的。(<_init+40> 是什么呢?) 没有调用它,就没有初始化页表 (或段寄存器,我不知道在 Linux 中是哪个),这样系统就没有为这个进程分配实际的物理内存,应该就是产生段错误的原因吧。 |
|