曲径通幽论坛

标题: 模仿高级条件分支与循环 [打印本页]

作者: beyes    时间: 2009-12-13 22:08
标题: 模仿高级条件分支与循环
高级条件分支,在高级语言里最常见的是 if 语句。下面是一段简单的代码( ifthen.c ):
#include <stdio.h>

int main()
{
    int a = 100;
    int b = 25;
    if (a > b) {
        printf ("The higher value is %d\n", a);
    } else {
        printf ("The higher value is %d\n", b);
    }

    return 0;
}
看一下这段代码相应的汇编形式,使用 gcc -S ifthen.c 命令可以生成 ifthen.s 文件,内容如下:
$ cat ifthen.s
    .file    "ifthen.c"
    .section    .rodata
.LC0:
    .string    "The higher value is %d\n"
    .text
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $100, 28(%esp)
    movl    $25, 24(%esp)
    movl    28(%esp), %eax
    cmpl    24(%esp), %eax
    jle    .L2
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    jmp    .L3
.L2:
    movl    $.LC0, %eax
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
.L3:
    movl    $0, %eax
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
    .section    .note.GNU-stack,"",@progbits
下面分析汇编代码的实现:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
上面一段,是在进入程序后,先对 EBP 寄存器入栈保护,然后使 EBP 指向当前堆栈的栈顶,这也是进入一个函数后的通常做法。第 3 行中,使 ESP 寄存器边界对齐,因为 -16 的十六进制为 0xfffffff0 ,这样就保证了堆栈指针从 "零" 开始,这么一来无论是对于 2 字节,还是 4 字节,都是容易对齐的。第 4 行,留出 8 个字节的堆栈空间,目的是放入程序中需要压栈的变量。
   movl    $100, 28(%esp)
    movl    $25, 24(%esp)
    movl    28(%esp), %eax
    cmpl    24(%esp), %eax
    jle    .L2
第1行,变量 a 入栈(a 的值为100)。
第2行,变量 b 入栈(b 的值为 25) 。
第3行,从堆栈中取出 a ,然后送到 EAX 中。
第4行,进行 a 与 b 的比较
第5行,如果 a 小于 b,则跳到 .L2 标签。
   movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    jmp    .L3
第 1 行,是 printf() 函数第一个字符串参数的起始地址,这里送入EAX 寄存器中。
第 2 行,从堆栈中取出 a 的值送入 EDX 寄存器中。
第 3 行,EDX 寄存器放入堆栈 ESP+4 处。
第 4 行,要打印的 a 值放在栈顶。
第 5 行,调用 printf 函数。( C 函数的参数入栈顺序是从右到左)
第 6 行,跳到 .L3,准备退出整个函数 (return 0;)

剩下的代码类似分析。
作者: beyes    时间: 2009-12-13 23:19
标题: 模仿循环
一段简单的 for 循环程序( for.c ):
#include <stdio.h>

int main()
{
    int i;
    int j;
    for (i = 0; i < 1000; i++) {
        j = i * 5;
        printf ("The answer is %d\\n", j);
    }
    return (0);
}
使用 gcc -S for.c 生成 for.s 汇编文件,其内容为:
.file    "for.c"
    .section    .rodata
.LC0:
    .string    "The answer is %d\\n"
    .text
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $0, 28(%esp)
    jmp    .L2
.L3:
    movl    28(%esp), %edx
    movl    %edx, %eax
    sall    $2, %eax
    addl    %edx, %eax
    movl    %eax, 24(%esp)
    movl    $.LC0, %eax
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    addl    $1, 28(%esp)
.L2:
    cmpl    $999, 28(%esp)
    jle    .L3
    movl    $0, %eax
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
    .section    .note.GNU-stack,"",@progbits
汇编程序简要说明:
    movl    28(%esp), %edx
    movl    %edx, %eax
    sall    $2, %eax
    addl    %edx, %eax
上面程序段是对 j = i * 5; 这个处理方法,EAX 寄存器(内容为 i 的值)先左移 2 位,相当于乘 4,然后再与 EDX 寄存器中的值相加,于是结果就是原始值乘 5 ,这样的处理方法比较巧妙。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2