程序的机器级表示(一)


计算机执行机器代码,用字节序列编码低级操作,而编译器基于编译语言的规则、目标机器的指令集和操作系统遵循的惯例,经过一系列阶段生成机器代码。GCC C语言编译器以汇编代码的形式产生输出,汇编代码是机器代码的文本表示。在本章中,我们会近距离的观察机器代码,以及人类可读的表示——汇编代码。

机器级代码

计算机系统使用了多种不同形式的抽象,利用更简单的抽象模型来隐藏实现的细节。对于机器变成来说,有两种抽象尤为重要。

  1. 指令集体系结构或指令集架构(ISA)来定义机器级程序的格式和行为,它定义了处理器状态、指令的格式,以及每条指令对状态的影响。·············
  2. 机器级程序使用的内存地址是虚拟地址,操作系统给程序提供了一个虚拟的内存模型。在更早以前,汇编程序员直接使用物理地址进行编程,我们不得不在编程时处处小心,仔细的分配每一块内存的使用,而现在我们不需要考虑这么多,整个内存都可以认为是我们当前一个程序在使用。

机器级代码与原始的C语言代码也有着巨大的差别。一些对C语言程序员隐藏的处理器状态都是可见的。

  • 程序计数器(PC),在x86-64中用%rip寄存器表示(在8086处理器中用%ip表示),给出将要执行的下一条执行在内存中的地址。
  • 整数寄存器文件,包含16个明明位置,分别存储64位的值。这些寄存器可以存储地址或者整数数据,有的寄存器可以被用来记录某些重要的程序状态,而其它的寄存器用来保存临时变量。
  • 条件码寄存器,保存这最近执行的算术或逻辑指令的状态信息,用来实现C语言中的条件判断。
  • 一组向量寄存器,可以存储一个或多个整数或者浮点数值。

代码示例

mstore.c

long mul2(long,long);

void mulstore(long x, long y, long *dest)
{
    long t = mul2(x, y);
    *dest = t;
}

用如下方式编译并汇编mstore.c并生成mstore.o

gcc -c mstore.c

接下来我们使用反汇编工具来检查汇编代码

objdump -d mstore.o

结果如下:

0000000000000000 <mulstore>:
   #offset  # Bytes
   0:   f3 0f 1e fa             endbr64 
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
   8:   48 83 ec 30             sub    $0x30,%rsp
   c:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  10:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
  14:   48 89 55 d8             mov    %rdx,-0x28(%rbp)
  18:   48 8b 55 e0             mov    -0x20(%rbp),%rdx
  1c:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  20:   48 89 d6                mov    %rdx,%rsi
  23:   48 89 c7                mov    %rax,%rdi
  26:   e8 00 00 00 00          callq  2b <mulstore+0x2b>
  2b:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  2f:   48 8b 45 d8             mov    -0x28(%rbp),%rax
  33:   48 8b 55 f8             mov    -0x8(%rbp),%rdx
  37:   48 89 10                mov    %rdx,(%rax)
  3a:   90                      nop
  3b:   c9                      leaveq 
  3c:   c3                      retq

暂时你还不需要读懂这些代码,通过后面的学习我们会慢慢的了解这些指令。

除了编译并汇编一个文件,我们还需要学会如何生成可执行文件。

main.c

#include <stdio.h>

void mulstore(long, long, long *);

int main()
{
    long d;
    mulstore(2, 3, &d);
    printf("2 * 3 --> %ld", d);
    return 0;
}

long mult2(long a, long b)
{
    long s = a * b;
    return s;
}

指令如下

gcc -o main main.c mstore.c

该指令会把main.c和mstore.c一起编译,并通过-o选项制定了可执行文件的名称。

关于格式的注解

我们可以通过-S参数来直接查看c语言文件的汇编代码

gcc -S mstore.c

内容如下

        .file   "mstore.c"
        .text
        .globl  mulst`在这里插入代码片`ore
        .type   mulstore, @function
mulstore:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $48, %rsp
        movq    %rdi, -24(%rbp)
        movq    %rsi, -32(%rbp)
        movq    %rdx, -40(%rbp)
        movq    -32(%rbp), %rdx
        movq    -24(%rbp), %rax
        movq    %rdx, %rsi
        movq    %rax, %rdi
        call    mul2@PLT
        movq    %rax, -8(%rbp)
        movq    -40(%rbp), %rax
        movq    -8(%rbp), %rdx
        movq    %rdx, (%rax)
        nop
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   mulstore, .-mulstore
        .ident  "GCC: (Ubuntu 9.3.0-10ubuntu2) 9.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long    1f - 0f
        .long    4f - 1f
        .long    5
0:
        .string  "GNU"
1:
        .align 8
        .long    0xc0000002
        .long    3f - 2f
2:
        .long    0x3
3:
        .align 8
4:

可以看到这与反汇编的结果类似,但还是有很多不同,其中大量的以.开头的行我们并不需要关系,这是知道汇编器和链接器工作的伪指令。

此外,为了更加清晰地说明汇编代码,我们会用一种格式来表示汇编代码,它省略了大部分伪指令,但包括行号和解释性说明,对于上面的示例,带解释的汇编代码如下:

# void mulstore(long x,long y,long *dest)
# x in %rdi, y in %rsi, dest in %rdx
mulstore:
     1          pushq   %rbp				# Save %dbx
     2          movq    %rsp, %rbp			
     3          subq    $48, %rsp
     4          movq    %rdi, -24(%rbp)
     5          movq    %rsi, -32(%rbp)
     6          movq    %rdx, -40(%rbp)
     7          movq    -32(%rbp), %rdx
     8          movq    -24(%rbp), %rax
     9          movq    %rdx, %rsi			# mov y to %rsi
    10          movq    %rax, %rdi			# mov x to %rdi
    11          call    mul2@PLT         	# call mul2
    12          movq    %rax, -8(%rbp)		# save result(%rax) 
    13          movq    -40(%rbp), %rax
    14          movq    -8(%rbp), %rdx		# mov result to %rdx
    15          movq    %rdx, (%rax)		# mov %rdx to *dest
    16          leave
    17          ret

通常我们只会给出与讨论内容相关的代码行,每一行的左边都有编号供引用,右边是注释,简单地描述指令的效果以及它和原始C语言代码的关系。

本小节介绍了机器级代码和汇编语言,并为接下来的章节奠定基础。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值