程序的机器级表示

寻址方式的演变

DOS → 8086 → IA32
Inter处理器系列俗称x86,其演变过程(根据其所需要的晶体管数量来说明):
8086 → 80286 → i386 → i486 → Pentium → PentiumPro → Pentium II → Pentium III → Pentium 4 → Pentium 4E
→ Core 2 → Core i7

机器级代码

两种抽象极为重要:

1.机器级程序的格式和行为
指令集体系结构(ISA),它定义了处理器状态、指令的格式以及每条指令对状态的影响。
大多数ISA,包括ISA32和x86-64,将程序的行为描述成每条指令是按顺序执行的。
2.机器级程序使用的存储器地址是虚拟抵制,提供的存储器模型看上去是一个非常打的字节数组。
实际实现是将多个硬件存储器和操作系统软件组合起来。

汇编代码非常接近于机器代码

汇编代码有一个主要特点

它用可读性更好的文本格式来表示。

IA32机器代码的处理器状态

1.**程序计数器**(在IA32中,通常成为“PC”,用%eip表示)指示将要执行的下一条指令在存储器中的地址。
2.**整数寄存器文件**包含8个命名的为止,分别存储32位的值。用于存储地址或整数数据。
3.**条件码寄存器**保存着最近执行的算术或者逻辑指令的状态信息。用于实现控制和数据流中的条件变化。

汇编代码不区分有符号或无符号整数,不区分各种类型的指针,不区分指针和整数。

程序存储器

包含程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的存储器块。
用虚拟地址来寻址。操作系统负责管理虚拟地址空间,将虚拟抵制翻译成实际处理器存储器中的物理地址。

一条机器指令只执行一个非常基本的操作。

代码示例

利用gcc -O1 -S xxx.c -o xxx.s将C语言代码编译成汇编代码

如下图,将code.c编译成code.s

由于本人使用的是Ubuntu 64位系统所以使用gcc -m32 -S xxx.c -o xxx.s所得到的汇编代码更接近教材所示。

也可以用objdump -d xxx进行反汇编

查看-S生成的汇编文件时,出现很多"."开头的语句,将其删除后便于阅读。

二进制文件的查看

可以用od 命令查看,也可以用gdb的x命令查看。

ATT与intel汇编代码格式

Intel格式代码的生成:

gcc -S -masm=intel xxx.c

区别

  • Intel代码省略了指示大小的后缀。movl改为mov。
  • intel代码省略了寄存器名字前面的'%'符号。%esp改为esp
  • intel代码用不同的方式来描述存储器中的位置。8(%ebp)改为DWORD PTR [ebp+8]
  • 在带有多个操作数的指令情况下,列出操作数的顺序相反。

数据格式

数据传送指令变种

  • movb(传送字节)
  • movw(传送字)
  • movl(传送双字)

访问信息

IA32CPU包含一组8个存储32位值的寄存器,用于存储整数数据和指针,名称都以%e开头。

  • 4个数据寄存器(eax、ebx、ecx和edx)

  • 2个变址和指针寄存器(esi和edi)

  • 2个指针寄存器(esp和ebp)

  • 4个数据寄存器(EAX、EBX、ECX和EDX)

      32位CPU有4个32位的通用寄存器EAX、EBX、ECX和EDX。对低16位数据的存取,不会影响高16位的数据。这些低16位寄存器分别命名
    为:AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。 4个16位寄存器又可分割成8个独立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每个寄存器都有自己的名称,可
    独立存取。程序员可利用数据寄存器的这种“可分可合”的特性,灵活地处理字/字节的信息。
  • 2个变址和指针寄存器(ESI和EDI)

      32位CPU有2个32位通用寄存器ESI和EDI。其低16位对应先前CPU中的SI和DI,对低16位数据的存取,不影响高16位的数据。
    
      寄存器ESI、EDI、SI和DI称为变址寄存器(Index Register),它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储
    器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。 变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。它们可作一般的存储器指针使用。
    在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能。
  • 2个重要的指针寄存器(ESP和EBP)

      ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
    EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部
  • 注意%ebp和%esp保存着指向程序栈中重要位置的指针。

  • 字节操作可以对%eax/%ecx/%edx/%ebx的2个低位字节进行读写操作。

操作数指示符

  • 立即数(immediate),常数值
  • 寄存器(regist):
    1. 双字操作-可以表示32位寄存器中的一个(%eax);
    2. 操作-可以表示16位寄存器中的一个(%ax);
    3. 字节操作-可以表示单字节寄存器元素中的一个(%al);

      -存储器(mempry),根据有效地址访问某个存储器为止。

寻址模式

格式表示:Imm (Eb,Ei,s) *其中Imm为立即数偏移,Eb位基址寄存器,Ei为变址寄存器,s为比例因子,s必须位1、2、4或8.

有效地址运算:Imm + R[Eb] + R[Ei]·s

数据传送指令

传送指令MOV(Move)把一个字节、字或双字的操作数从源位置传送到目的位置,源操作数的内容不变。

格式:MOV DST,SRC

  • MOV reg/mem, imm ;立即数→寄存器或存储器
  • MOV reg/mem/seg, reg ;寄存器的值→寄存器/内存/段寄存器
  • MOV reg/seg, mem ;内存单元的值→寄存器/段寄存器
  • MOV reg/mem, seg ;段寄存器的值→寄存器/内存单元

实例:

  • MOV EAX,#050aH ;将十六进制数050a 传送到通用寄存器eax中
  • MOV DI,BX(寄存器到寄存器之间传数)
  • MOV ES,AX(通用寄存器与段寄存器之间传数)
  • MOV AX,DS(段寄存器至通用寄存器)
  • MOV AL,23H(将立即数"复制"到寄存器)
  • MOV [2000H],02H(直接寻址)
  • MOV [2061H],BX

应该注意的是:

  • 目的操作数要与源操作数类型要一致,不能一个是字,一个是字节
  • 目的操作数要和源操作数类型之一必须要有明确的类型
  • 立即数不能作为目的操作数
  • 不能用立即寻址方式给段寄存器传数
  • 源操作数和目的操作数不能同时为存储器操作数,即存储单元之间不能用MOV指令直接传送
  • CS和IP不能作为目的操作数,但CS可以作为源操作数。
  • 段寄存器之间不能用MOV指令直接传送
  • 在传送字单元时,遵循“高字节存放在高地址,低字节存放在低地址”的原则。

  • b-8位 w-16位 l-32位
  • MOVS(符号位扩展),目的位置的所有高位用源值的最高位数值进行填充。
  • MOVZ(零扩展),目的为止的所有高位都用零填充。

栈 p115

  • 栈是一种数据结构,可以添加或者删除值,遵循后进先出的原则。
  • 通过pop删除数据,弹出的值永远是最近被压入而仍然在栈中的值。总是从数组的栈顶插入和删除元素。
  • 通过push把数据压入栈中
  • 栈顶元素的地址是所有栈中元素地址中最低的。
  • pushl用于把数据压入到栈上,仅仅只有一个操作数,相当于将栈指针减4,然后将值写到新的栈顶地址
  • popl用于弹出数据,仅仅只有一个操作数,相当于从栈顶位置读出数据,然后将栈指针加4
  • 程序可以用标准的存储器寻址方法访问栈内任意位置,例如movl 4(%esp),%edx

c语言中的指针

p117代码展示:

  • c语言中所谓的指针其实就是地址
  • 间接引用指针就是将该指针放在一个寄存器中,然后在存储器引用中使用这个寄存器。
  • 局部变量x通常保存在寄存器中,因为寄存器访问较存储器访问要快。

加载有效地址

  • leal是从存储器读数据到寄存器,但实际上并没有引用存储器。
  • 格式:leal S,D 相对应的效果 D ← &S
  • leal并没有大小操作数的变种

一元操作

  • 仅有一个操作数,既是源也是目的,可以为寄存器也可以为存储器位置。
  • 类别:1.INC 2.DEC 3.NEG 4.NOT

二元操作

  • 有两个操作数,第二个操作数既是源又是目的。源操作数是第一个,目的操作数是第二个。
  • 第一个操作数可以是立即数、寄存器或者是存储器位置。
  • 第二个操作数可以是寄存器或是存储器位置。
  • 两者不能同时是存储器位置
  • 类别:1.ADD 2.SUB 3.IMUL 4.XOR 5.OR 6.AND

移位操作

  • 移位量只允许进行0~31位的移位。
  • 移位量可以是立即数,或者放在单字节寄存器元素%cl中。
  • 类别:1.SAL 2.SHL 3.SAR 4.SHR

特殊操作(p122)

  1. imull S 有符号全64位乘法(乘数除了S外,另一个操作数取自%eax)
  2. mull S 无符号全64位乘法(乘数除了S外,另一个操作数取自%eax)
  3. cltd 转为四字
  4. idivl S 有符号除法
  5. divl S 无符号除法

条件码

  • CF:进位标志。最近的操作使最高位产生了进位。可以用来检查无符号操作数据的溢出。
  • ZF:零标志。最近的操作得出的结果为0.
  • SF:符号标志。最近的操作得到的结果为负数。
  • OF: 溢出标志。最近的操作导致一个补码溢出--正溢出或负溢出。
  • leal指令不改变任何条件码
  • 一元操作、二元操作、移位操作均会影响条件码

CMP与SUB

  • CMP的指令与SUB的指令行为相同。Operate S2,S1 => S1 - S2
  • CMP S2,S1 改变标志位,但相减的结果并不会赋予S1
  • SUB S2,S1 改变标志位,但相减的结果会赋予S1

TEST与AND

  • 两者的指令行为相同,Operate S2,S1 => S1&S2
  • 典型用法:testl %eax,%eax用来检查%eax是负数、零,还是正数。

访问条件码

基于set的不同组合

跳转指令及其编码

  • jump(跳转)指令会导致执行切换到程序中一个全新的位置。
  • label(标号),跳转的目的地需要用一个标号来指明。
  • 分类:
    • 直接跳转:给出一个标号作为跳转目标。例如 jmp .L1
    • 间接跳转:跳转目标是从寄存器或存储器位置中读出的。写法为'*'+操作数指示符 例如 jmp *%eax
      • 区别jmp *%eax 与 jmp *(%eax)
      • jmp *%eax将%eax中的值作为跳转目标 | jmp *(%eax)将%eax的值作为读地址,从存储器中读出跳转目标。

翻译条件分支

  • 常用的方式:

    • 结合有条件跳转
    • 结合无条件跳转
  • C语言中的if-else语句通用模版:

      if(test-expr)
    then-statement
    else
    else-statement
  • 汇编相对应的通用形式

      t = test-expr;
    if(!t)
    goto false;
    then-statement
    goto done;
    false:
    else-statement
    done:
  • 汇编为then-statement 和 else-statement产生了各自的代码块。

循环

do-while

  • 通用形式:

      do
    body-statment
    while(test-expr);
  • 对应的goto语句:

      loop:
    body-statement
    t = test-expr;
    if(t)
    goto loop;
  • body-statemnt至少会执行一次

while循环

  • 通用形式:

      while(test-expr)
    body-statement
  • 对应的goto语句

      t = test-expr;
    if(!t)
    goto done;

    loop:

    body-statement

    t = test-expr;

    if(t)

    goto loop;

    done:

  • 通过条件才执行依次。

for循环

  • 通用形式:

      for(init-expr;test-expr;update-expr)
    body-statement
  • 对应的goto语句

      init-expr;
    t = test-expr;
    if(!t)
    goto done;
    loop:
    body-statement
    update-expr
    t = test-expr;
    if(t)
    goto loop;
    done:

switch语句 p145

  • 根据一个整数索引值进行多重分支。多用于处理具有多种可能结果的测试时。
  • 运用到了跳转表(jump table),跳转表为一个数组,表项i是一个代码段的地址,这个代码段实现打那个开关索引值等于i时程序应采取的措施

栈帧结果

  • 栈用来传递参数、存储返回信息、保存寄存器以及本地存储
  • 寄存器%ebp为帧指针
  • 寄存器%esp为栈指针
  • 当程序执行时,栈指针可以移动,大多数信息的访问都是相对于帧指针的。

转移控制

  • call Label 直接调用
  • call *Operand 间接调用
  • call的效果是将返回地址入栈,并跳转道被调用过程的起始处。
  • 返回地址是在程序中紧跟在call后面的那条指令的抵制。
  • leave为返回准备栈
  • ret的效果是从栈中弹出地址,并跳转到这个位置。
  • 函数返回值保存在%eax中

寄存器使用惯例

  • %eax/%edx/%ecx - 调用者保存寄存器 当过程P调用Q时,Q可以覆盖这些寄存器
  • %ebx/%esi/%edi - 被调用者保存寄存器 当Q必须在覆盖这些寄存器的值之前,先把他们保存到栈中,并在返回前回复他们,因为P有可能在今后的计算中需要这些值。
  • 必须保持寄存器%ebp/%esp

GDB调试器

  • GDB命令示例:

对于栈的GDB命令
  • bt n

    • 若省略n,则为打印当前栈的信息
    • 若n为正整数,则为打印该栈顶上n层的栈信息
    • 若n为负整数,则为打印该栈地下n层的栈信息
  • frame n
    • frame命令允许你从一个栈框移动到另一个栈框,然后可以打印出你选择的栈框,args要么是栈框的地址,要么是栈框的标号,如果没有参数,frame 命令将会打印出当前的栈框。
    • select-frame 命令允许你从一个栈框移动到另外一个栈框而不打印栈框的信息,

      这个命令可以看作是frame命令的silent版本。
  • up n
    • 向当前栈上面移动n层
    • 若省略n,则单步进行
  • down n
    • 向当前栈下面移动n层
    • 若省略n,则单步进行

作业

源代码如下:

编译后去除.开头的语句得到以下语句:

add:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $15, %eax
popl %ebp
function:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call add
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $15, (%esp)
call function
addl $8, %eax
leave
ret

gdb小试

遇到的问题与解决方法

1.在加载有效地址 %edx=x leal 7(%edx,%edx,4),%eax对此并不理解为什么表示的为将设置寄存器%eax的值为5x+7

 解决:通过网络的查询,得知此为AT&T语法,base(offset, index, i),就是 base+offset+index*i
相对应的则是7+x+x*4=5*x+7。

2.在查阅CMP与SUB之间的区别时,发现百度百科上CMP对于操作数的执行顺序与教材中的不同。

解决:询问老师,得知在Linux系统中CMP S2,S1表示S1-S2,而在Windows系统中 CMP S2,S1 表示S2-S1。

Linux第四次学习笔记的更多相关文章

  1. 《Linux内核设计与实现》第四章学习笔记

    <Linux内核设计与实现>第四章学习笔记           ——进程调度 姓名:王玮怡  学号:20135116 一.多任务 1.多任务操作系统的含义 多任务操作系统就是能同时并发地交 ...

  2. 《Linux内核设计与实现》第四章学习笔记——进程调度

                                                                        <Linux内核设计与实现>第四章学习笔记——进程调 ...

  3. Linux——帮助命令简单学习笔记

    Linux帮助命令简单学习笔记: 一: 命令名称:man 命令英文原意:manual 命令所在路径:/usr/bin/man 执行权限:所有用户 语法:man [命令或配置文件] 功能描述:获得帮助信 ...

  4. Spring实战第四章学习笔记————面向切面的Spring

    Spring实战第四章学习笔记----面向切面的Spring 什么是面向切面的编程 我们把影响应用多处的功能描述为横切关注点.比如安全就是一个横切关注点,应用中许多方法都会涉及安全规则.而切面可以帮我 ...

  5. Linux性能优化实战学习笔记:第四讲

    一.怎么查看系统上下文切换情况 通过前面学习我么你知道,过多的上下文切换,会把CPU时间消耗在寄存器.内核栈以及虚拟内存等数据的保存和回复上,缩短进程真正运行的时间,成了系统性能大幅下降的一个元凶 既 ...

  6. Linux性能优化实战学习笔记:第三十四讲

    一.上节回顾 上一节,我带你学习了 Linux 网络的基础原理.简单回顾一下,Linux 网络根据 TCP/IP模型,构建其网络协议栈.TCP/IP 模型由应用层.传输层.网络层.网络接口层等四层组成 ...

  7. Linux性能优化实战学习笔记:第四十讲

    一.上节回顾 上一节,我们学习了碰到分布式拒绝服务(DDoS)的缓解方法.简单回顾一下,DDoS利用大量的伪造请求,导致目标服务要耗费大量资源,来处理这些无效请求,进而无法正常响应正常用户的请求. 由 ...

  8. Linux性能优化实战学习笔记:第四十二讲

    一.上节回顾 上一节,我们学习了 NAT 的原理,明白了如何在 Linux 中管理 NAT 规则.先来简单复习一下. NAT 技术能够重写 IP 数据包的源 IP 或目的 IP,所以普遍用来解决公网 ...

  9. Linux性能优化实战学习笔记:第四十三讲

    一.上节回顾 上一节,我们了解了 NAT(网络地址转换)的原理,学会了如何排查 NAT 带来的性能问题,最后还总结了 NAT 性能优化的基本思路.我先带你简单回顾一下. NAT 基于 Linux 内核 ...

随机推荐

  1. 8张图理解Java

    一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有最多的票选.如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟. 1.字符串不变性 下面这张图展示了这段代码做 ...

  2. FLUSH TABLES WITH READ LOCK

    最近在mysql主从复制中用到锁,翻了资料回忆一下.一下内容参考于:http://blog.csdn.net/arkblue/article/details/27376991 1.FLUSH TABL ...

  3. 【转】Python开发指南:最佳实践精选

    总体原则 价值 “为别人开发你也想要使用的工具.” ——Kenneth Reitz "简洁总是胜过可用." ——Pieter Hintjens "满足90%的使用场景.忽 ...

  4. 回文数 第N个回文数

    判断回文数还是不难,如果能转为字符串就更简单了. 如果是求第N个回文数呢. 12321是一个回文数,这里先考虑一半的情况. 回文数的个数其实是有规律的.如: 1位回文数: 9个 2位回文数: 9个 3 ...

  5. Hibernate之Criteria的完整用法

    Criteria的完整用法 QBE (Query By Example) Criteria cri = session.createCriteria(Student.class); cri.add(E ...

  6. 在jsp中默认写上的一段java代码表示basePath 的路径的具体的意思是什么?

    <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" ...

  7. Linux搭建DNS服务器

    Linux系统信息: Version: Centos 6.6 Ip address:10.0.0.104 Hostname: extmail.com 配置系统 hostname Vim /etc/sy ...

  8. C# 遍历枚举类

    framework 4.0 环境下 方法 定义枚举类 判断枚举类中是否存在,若存在则输出 例子: Defined.QrCode.QrCodeType type;//枚举类 if (!Enum.TryP ...

  9. python中for和if else的使用

    In []: a = set('abcd') In []: b = set('ef') In []: def match(x,y): ....: for i in x: ....: for j in ...

  10. Spring AOP 注解和xml实现 --转载

    AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...