程序的机器级表示

来源:互联网 发布:网络机柜 编辑:程序博客网 时间:2024/05/16 17:54

硬着头皮来看一遍深入理解计算机系统,对其中章节进行做个笔记,其中有些一知半解的希望这次是真的懂来

1、程序的过程:从源文件到目标文件的过程

1.1 hello.c

1.2 hello.i 预处理阶段,将#开头的头文件进行展开,宏定义替换

1.3 hello.s 编译阶段,将hello.i文件翻译成汇编程序

1.4 hello.o 通过汇编器as将程序生成为可重定位的二进制的目标程序

1.5 hello 通过链接器生成可执行的目标程序


2. 存储设备

更小更快更贵,更大更慢更便宜

寄存器--》高速缓存l1--》高速缓存l2--》高速缓存ln--》主存--》本地磁盘--》远地磁盘


3. 类型大小

32位下

short int是2字节,long是4字节,long long才是8字节,char*是4字节

64位下

long是8字节,long long也是8字节,char* 是8字节


4.小端法,大端法

比如int类型下,内存值从小到大为

01 23 45 67

如果是小端法,则这个值是0x67452301,大端法这个值则是0x01234567。linux和windows目前都是采用小端法


5. 移位

移位分为算术右移和逻辑右移,就是符号位的问题,如果是算术右移,符号位需要继续保持不变,而逻辑右移则是在最右边补0即可


6.程序的机器级表示,是本次的重点内容来,主要是看懂了一些汇编的基础知识,寄存器的使用

%eip 表示要执行的下一条指令

%esp 栈顶指针

$ebp the temp data,always get data from esp, because esp is always change, we want to use esp data,so we set the esp to ebp for compute

还有寄存器的取值说明,比如直接寻址,间接寻址,立即数寻址,不再列出,下面以实际代码来进行说明

int decode1(int* xp, int* yp, int* zp)
{
    int y = *yp;
    int z = *zp;
    int x = *xp;
    *yp = x;
    *zp = y;
    *xp = z;
    int ttt = 0;
    return ttt;
}
int main()
{
    int xp = 0;
    int yp = 1;
    int zp = 2;
    decode1(&xp, &yp, &zp);
    return 0;
}

展开对应的汇编,未经过优化的,使用gcc -o1即可,下面详细解释下各个行代码

08048456 <main>:
 8048456:    55                       push   %ebp                 //保存ebp,即将ebp入栈,入桟后esp会往下减4,因为ebp占4个字节,桟底的值要小一点
 8048457:    89 e5                    mov    %esp,%ebp   // 将esp的值保存到ebp中,因为ebp已经入桟保存好了,所以覆盖ebp寄存器的值也是可以的
 8048459:    83 ec 1c                 sub    $0x1c,%esp // 将esp减去28,此处减去的值是为来在esp以下放临时变量,想要看减去来多少值,可以查看esp和ebp的差即可
 804845c:    c7 45 f4 00 00 00 00     movl   $0x0,-0xc(%ebp) 将0保存到r(ebp-12)这个位置, $0x0是立即寻址,即直接取值,如果是0x0这样就是直接寻址,需要取地址对应的内容了

8048463:    c7 45 f8 01 00 00 00     movl   $0x1,-0x8(%ebp) 将1保存到r(ebp-8)这个位置,就是说ebp-8里面存放的这个地址,这个地址对应的值是1
 804846a:    c7 45 fc 02 00 00 00     movl   $0x2,-0x4(%ebp) 将2保存到ebp-4这个位置
 8048471:    8d 45 fc                 lea    -0x4(%ebp),%eax   取ebp-4的内容保存到eax中,eax中存放的是ebp-4这个值,ebp-4这个地址对应的内容是2
 8048474:    89 44 24 08              mov    %eax,0x8(%esp)  eax赋值给esp+8,就是将zp的地址赋值给esp+8,即esp上面是参数,esp下面是本地临时变量
 8048478:    8d 45 f8                 lea    -0x8(%ebp),%eax
 804847b:    89 44 24 04              mov    %eax,0x4(%esp)
 804847f:    8d 45 f4                 lea    -0xc(%ebp),%eax
 8048482:    89 04 24                 mov    %eax,(%esp) 与上面一样来,将xp的地址赋值给esp
 8048485:    e8 8a ff ff ff           call   8048414 <decode1> 此处会将esp-4,留了4个地址来放什么内容?存放的是eip的值,所以第一个参数就变成来esp+4了
 804848a:    b8 00 00 00 00           mov    $0x0,%eax
 804848f:    c9                       leave  
 8048490:    c3                       ret    
 8048491:    66 90                    xchg   %ax,%ax
 8048493:    66 90                    xchg   %ax,%ax
 8048495:    66 90                    xchg   %ax,%ax
 8048497:    66 90                    xchg   %ax,%ax
 8048499:    66 90                    xchg   %ax,%ax
 804849b:    66 90                    xchg   %ax,%ax
 804849d:    66 90                    xchg   %ax,%ax
 804849f:    90                       nop

08048414 <decode1>:
 8048414:    55                       push   %ebp                        ebp入桟,esp又会减少4,因为esp总是指向桟的最顶端,所以第一个参数本来是+4的,需要+8了
 8048415:    89 e5                    mov    %esp,%ebp
 8048417:    83 ec 10                 sub    $0x10,%esp        esp-16,为了保存下面的临时变量
 804841a:    8b 45 0c                 mov    0xc(%ebp),%eax  // ebp+12,就是第二个参数
 804841d:    8b 00                    mov    (%eax),%eax
 804841f:    89 45 f0                 mov    %eax,-0x10(%ebp)  esp-16用来放临时变量
 8048422:    8b 45 10                 mov    0x10(%ebp),%eax // ebp+16就是第三个参数
 8048425:    8b 00                    mov    (%eax),%eax
 8048427:    89 45 f4                 mov    %eax,-0xc(%ebp)
 804842a:    8b 45 08                 mov    0x8(%ebp),%eax // ebp+8就是第一个参数
 804842d:    8b 00                    mov    (%eax),%eax
 804842f:    89 45 f8                 mov    %eax,-0x8(%ebp)
 8048432:    8b 45 0c                 mov    0xc(%ebp),%eax
 8048435:    8b 55 f8                 mov    -0x8(%ebp),%edx
 8048438:    89 10                    mov    %edx,(%eax)
 804843a:    8b 45 10                 mov    0x10(%ebp),%eax
 804843d:    8b 55 f0                 mov    -0x10(%ebp),%edx
 8048440:    89 10                    mov    %edx,(%eax)
 8048442:    8b 45 08                 mov    0x8(%ebp),%eax
 8048445:    8b 55 f4                 mov    -0xc(%ebp),%edx
 8048448:    89 10                    mov    %edx,(%eax)
 804844a:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp)
 8048451:    8b 45 fc                 mov    -0x4(%ebp),%eax    eax是用来存放返回值的
 8048454:    c9                       leave    leave所做的事情是还原esp,还原ebp:它等效与如下两句话:movl %ebp %esp,popl %ebp
 8048455:    c3                       ret         ret执行的是pop %eip,$eip会存放调用函数处的下一跳语句,每执行一条语句eip都会指向下一条要执行的语句,函数调用前,会先将eip入桟保存,ret再出桟


以上应该就算告一段落来,需要跟踪代码发现步步是怎么回事,另外介绍一下gdb下调试汇编

display /i $eip 需要有这句话,即每次都显示eip的值,ni才有效果,ni就是跟踪汇编一行

i r $esp查看对应寄存器的值

x  0x123,0x123这个地址下对应的值

p *0x123,与上面这个效果一样

disassemble /m main 显示main函数的汇编,并且也显示源代码,这样就能根据汇编地址打断点来

b main

b *0x123,在地址123上打断点


根据上面这些命令,每步执行后,再看各个寄存器的值,就知道上面解释的所以然了
















0 0
原创粉丝点击