zoukankan      html  css  js  c++  java
  • 【CSAPP】第三章 程序的机器级表示

    第三章 程序的机器级表示

    1. 程序编码

    1.1 gcc编译C程序

    编译过程:

    • 预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删掉,这里并不会检查语法;
    • 编译:检查语法,将预处理后的文件编译成汇编文件;
    • 汇编: 将汇编文件生成目标文件(二进制文件);
    • 链接: C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到可执行程序中去。

    命令:

    步骤 命令
    预处理 gcc -E hello.c -o hello.i
    编译 gcc -S hello.i -o hello.s
    汇编 gcc -c hello.s -o hello.o
    链接 gcc hello.o -o hello_elf
    • 如果要一步到位直接生成可执行文件,命令为gcc hello.c -o hello

    • 可以在gcc后指定 -Og/-O1/-O2设定编译优化级别

    以一下代码为例:

    //main.c
    #include<stdio.h>
    #include<stdlib.h>
    void multstore(double, double, double*);
    int main(){
    	double d;
    	multstore(2, 3, &d);
    	printf("2 * 3-->%ld
    ", d);
    	system("pause");
    	return 0;
    }
    double mult2(double a, double b){
    	double s = a * b;
    	return s;
    }
    
    //mstore.c
    double mult2(double, double);
    void multstore(double x, double y, double *dest){
    	double t = mult2(x,y);
    	*dest = t;
    }
    

    预处理:

    gcc main.c -o main.i
    gcc mstore.c -o mstore.i
    

    编译:

    gcc -S main.i -o main.s
    gcc -S mstore.i -o mstore.s
    

    汇编:

    gcc -c main.s -o main.o
    gcc -c mstore.s -o mstore.o
    

    链接:

    gcc main.o mstore.o //不指定名称,默认为a.exe
    

    一步:

    gcc main.c mstore.c -o test//指定名称为test.exe
    

    1.2 Cmake使用

    makefile的格式

    target ... : prerequisites ...
            command
            ...
            ...
    

    target:输出文件的名称

    prerequisites:输入的文件名称

    command:一系列的gcc命令

    test : main.o mstore.o
    	gcc main.o mstore.o -o test
    	
    main.o : main.s
    	gcc main.s -o main.o
    mstore.o : mstore.s
    	gcc mstore.s - mstore.o
    	
    main.s : main.c
    	gcc main.c -o main.s
    mstore.s : mstore.c
    	gcc mstore.c -o mstore.s
    

    make的高级特性

    1. 自动推导:make可以识别一个.o文件,自动将对应的.c文件加在依赖关系中。并且也会自动推导出相关的编译命令。因此上述的makefile文件可以只包含前两行

    2. 使用变量

      objs = main.o mstore.o
      test : $(objs)
      	gcc $(objs) -o test
      

    1.3 反汇编

    机器代码反汇编到汇编:

    objdump -d target.o
    

    如:

    汇编到C?

    2. 数据表示

    • 针对不同大小,数据传送指令分为movb(字节), movw(字), movl(双字), movq(四字)
    • 后缀 l 同时表示4字节整数和8字节双精度浮点数,根据指令和寄存器进行区分

    3. 数据访问

    3.1 整数寄存器

    x86-64处理器包含了16个64bit的通用目的寄存器,存储整数和指针

    明明规则:

    • 前8组以16bit寄存器为主,64 32和8bit分别带r e 和l的前缀或者后缀;
    • 后8组以64bit寄存器为主,命名为r8~r15,32 16 8bit分别带d w b后缀

    功能:

    • 函数返回值:ax
    • 函数参数:di si dx cx r8 r9共9个,分别保存1到6个函数参数,超出6个的参数被放在被调函数的栈帧上
    • 被调用者保存寄存器:剩余的都是,不管哪个程序都可以使用,但是在使用前需要保存寄存器原始数据,使用完要恢复(在函数调用时,保存现场和恢复现场由被调函数完成)

    3.2 寻址

    共三类:

    • 立即数:只能是整数,浮点数没有立即数
    • 寄存器:16个寄存器的低位1 2 4 8字节
    • 内存引用:M[Addr]

    寻址方式:有效地址=Imm + R[rb] + R[ri]*s,表示为Imm(rb, ri, s)

    3.3 数据传送指令

    简单传送指令

    • movabsq的目的地之只能是64位寄存器
    • movl会将寄存器高32位置0

    扩充传送(将少位数的扩充后传送到多位数的)

    3.4 出栈入栈

    • 对rsp寄存器操作
    • 向下生长
    • 按字节编址

    3.5 算逻运算

    • 结果都存储在第二个寄存器处

    • 除了leaq外,其余指令都有 q l w b四个变种

    • 特殊算数运算:针对8字(128位)的运算

    rax存乘数、乘积低位、商,rdx存乘积高位、余数

    3.6 控制

    控制码

    进位:CF

    零标志:ZF

    负数标志:SF

    溢出标志:OF

    比较和测试

    cmp S1, S2:基于S2-S1进行

    test S1, S2:基于S1&S2

    • 都有b l w q四种变种
    • 只设置条件码,不改变寄存器

    访问条件码

    set指令

    总结:需要区分有符号数和无符号数的指令有移位、特殊除法、set指令

    跳转指令

    • 直接跳转:jump Label
    • 间接跳转:jump *Adrr

    if语句实现

    C语言模板:

    if(test-expr){
        then-statement
    }else{
        else-statement
    }
    

    汇编控制流:

    t = test-expr
    if(!t)
    	goto false;
    then-statement
    goto done
    false:
    	else-statement
    done:
    

    用条件传送实现分支

    • 实现:计算出两种操作的结果,然后根据条件选择传送一种

      例如对于条件赋值表达式:v = test-expr ? then-expr : else-expr

      条件传送的控制流为:

      v = then-expr
      ve = else-expr
      t = test-expr
      if(!t)v = ve
      
    • 副作用:需要对then-expr和else-expr都求值,一方面增加了计算量,另一方面可能导致非法行为(非法地址访问)

    循环实现

    do-while循环

    loop:
    body-staement
    t = test-expr
    if(t) goto loop
    

    while循环

    1. 中间跳转法
    	goto test
    loop:
    	body-statement
    test:
    	t = test-expr
    	if(t) goto loop
    
    1. guarded-do
    t = test-expr
    if(!t) goto done
    loop:
    	body-statement
    	t = test-expr
    	if(t) goto loop
    done:
    

    for循环,类似于while循环的实现

    switch实现

    跳转表+间接跳转

    3.7 函数调用

    函数调用的实现机制(P调用Q):

    • 控制转移:将Q的入口地址读到rsp中,将P的断点存到栈中
    • 传递数据:P将Q需要的参数传入,优先传到rdi rsi rdx rcx r8 r9
    • 分配和释放内存:Q的局部变量
  • 相关阅读:
    极光推送的的栗子
    老师oracle讲义第五天
    oracle学习第五天
    ajax使用
    jstl标签的使用
    json使用
    jsp的el表达式使用
    老师oracle讲义第三天
    oracle学习第一天
    oracle学习第二天
  • 原文地址:https://www.cnblogs.com/vinnson/p/13466078.html
Copyright © 2011-2022 走看看