计算机系统基础实验 - 高级语言的机器级表示

实验4高级语言的机器级表示

实验序号:4 实验名称:高级语言的机器级表示
适用专业:软件工程 学 时 数:2学时

一、实验目的

1、理解函数调用的机器级表示方法。
2、了解复杂数据结构的机器级表示方法。
3、理解分支和循环语句的机器级表示方法。

二、实验要求

按照实验题目的要求,编写程序并上机调试

三、实验设备、环境

计算机、Windows 7 、Visual C++ 6.0

四、实验步骤及内容

写一段C语言程序包含循环分支和过程调用,查阅并分析汇编代码,下面是参考可写的程序,可以不按照以下程序来写,特别注意可以没有数组。
定义一个数组int sum[5];
通过循环从屏幕输入5个数进数组sum
调用函数int Sum(int sum[])计算数组中所有元素的和并返回
如果返回值大于50则输出平均值大于10,否则输出平均值小于10.
编译通过后查阅汇编代码并结合你写的C语言程序书写报告描述以下问题:

  1. 函数调用的汇编代码是如何描述的,参数放在什么地址(可以假设%ebp和%esp地址已知)
  2. 数组的汇编描述方法
  3. 分支语句的汇编描述方法
  4. 循环的汇编描述方法

提示:
查看汇编代码方法:
编译
设置断点 F11或在Build -> Start Debug ->Step Into
右键点击断点箭头,Go to disassembly

C语言代码

#include <stdio.h>

int g(int x)
{
    
    
  return x + 99;
}
int f(int x)
{
    
    
  return g(x);
}

void printStar(){
    
    
    for(int i = 0;i < 10; i++)
    {
    
    
        printf("*");
    }
}

int main() {
    
    
    int a = 0;
    int b = 0;
    int op1 = 1, op2 = 2;
    if (op1 = op2) {
    
    
        a = 1;
        b = 2;
    }
    return f(22) + 36;
}

汇编代码

g:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %eax
        addl    $99, %eax
        popl    %ebp
        ret
f:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        call    g
        leave
        ret
printStar:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $40, %esp
        movl    $0, -12(%ebp)
        jmp     .L6
.L7:
        movl    $42, (%esp)
        call    putchar
        addl    $1, -12(%ebp)
.L6:
        cmpl    $9, -12(%ebp)
        jle     .L7
        leave
        ret
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $528, %esp
        movl    $0, 524(%esp)
        movl    $0, 520(%esp)
        movl    $1, 516(%esp)
        movl    $2, 512(%esp)
        movl    516(%esp), %eax
        cmpl    512(%esp), %eax
        jne     .L9
        movl    $1, 524(%esp)
        movl    $2, 520(%esp)
.L9:
movl    $66, 24(%esp)
        movl    $666, 28(%esp)
        call    printStar
        movl    $22, (%esp)
        call    f
        addl    $36, %eax
        leave
        ret

代码解释:

push %ebp, movl %esp, %ebp两行代码,将ebp压入栈
然后esp的值赋给ebp,ebp指向与esp相同的位置
andl $-16,%esp esp 与0xfffffff0 与运算,使得它对齐寻址空间,加快cpu处理速度
subl $528, %esp指令,在栈中开辟528字节的空间。编译器会事先扫描函数中局部变量个数和大小,预分配给一个空间,供存储局部变量和参数调用。因为下面定义了一个122长的int数组,加上其他int变量,所以共需要开辟528字节空间存储局部变量。

movl    $0, 524(%esp)
movl    $0, 520(%esp)
movl    $1, 516(%esp)
movl    $2, 512(%esp)

这四句话是分别定义a,b,op1,op2四个变量,并分别将0,0,1,2赋值给这四个变量

1.分支语句的汇编描述方法

movl    516(%esp), %eax
cmpl    512(%esp), %eax
jne     .L9

将op1赋给eax,然后cmpl让op1和eax中的op2进行比较
Jne指令为不等于跳转,若op1不等于op2则跳转到.L9
若等于则执行下面的指令

movl    $1, 524(%esp)
movl    $2, 520(%esp)

给a赋值为1,给b赋值为2

2.数组的汇编描述方法
接上述,如果不等于则跳转至.L9

movl    $66, 24(%esp)
movl    $666, 28(%esp)

给数组进行赋值内存中会分配一个sizeof(T)*N字节的连续的区域。
这里数组的基地址放在了esp中,先给arr[0]赋值66,接着基地址加上int的4,就是arr[1],给他赋值为666.

3.循环的汇编描述方法
上述过程执行完,就会执行函数调用

call    printStar

将调用printStar函数
在printStar函数中

printStar:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $40, %esp
        movl    $0, -12(%ebp)
        jmp     .L6
.L7:
        movl    $42, (%esp)
        call    putchar
        addl    $1, -12(%ebp)
.L6:
        cmpl    $9, -12(%ebp)
        jle     .L7
        leave
        ret

前两句是保存旧的帧,创建新的栈帧。
第三局开辟40字节的空间,因为接下来会循环十次,调用十个int i
接着给i赋值为0,jmp是无条件跳转,所以直接跳转到.L6
Cmpl 将i与9比较,如果比9小,将跳转.L7

movl    $42, (%esp)
call    putchar
addl    $1, -12(%ebp)

接着将42给esp,42在ascill中对应的就是’
接下来调用putchar
输出
addl $1, -12(%ebp)这句指令是对i进行加一

4.函数调用的汇编代码是如何描述的,参数放在什么地址(可以假设%ebp和%esp地址已知)
上述过程执行完后

movl    $22, (%esp)
call    f

将22压入栈(这里其实是定义接下来调用的f的形参int x,并赋值)
调用f,这里先保存eip(PC计数器)的值入栈,esp的值-4,然后将f的地址赋给eip
进入f函数
执行创建新的栈帧的两条指令,进入新的函数调用栈
将ebp向前8个字节位置的值(main中压栈的22)压栈,esp-4
这里是定义g的形参并赋值
调用g,eip先压栈,然后eip变为g的地址
进入g函数:
首先执行创建新的栈帧的两条指令,进入新的调用栈(同上)
movl 8(%ebp), %eax 首先寻址,将f中压栈的数(也就是g的形参x),赋给eax
addl $99, %eax eax中得值+99,对应C语句 x + 99
popl %ebp 这里因为g中没有开辟新的栈空间(esp的值没有变化),所以这句和leave语句等价
执行完,这一句,ebp恢复到之前的值(f中得状态)
执行ret,其实是执行popl eip,将栈中保存的eip的值,赋给eip
此时ebp、esp和eip均恢复到调用f中得状态
退出g,返回值保存在eax中
回到f函数:
执行leave 和 ret,恢复esp、ebp、eip的值,同上
f仅仅返回g的返回值,这里不改动eax,里面保存着g的返回值
回到main函数:
addl $36, %eax f(22)的返回值+36,相当于C中得 f(22) + 36
main函数leave ret 返回上一层
gcc中应该为__libc_start_main,它可以接收到main的返回值,在eax中

猜你喜欢

转载自blog.csdn.net/L6666688888/article/details/128461077