C指针原理(85)-helloworld的C程序汇编剖析(1)

来源:互联网 发布:python matplotlib教程 编辑:程序博客网 时间:2024/05/16 05:11

一、汇编基础

1、指令码与数据处理

当计算机处理应用程序运行指令码时,数据指针指示处理器如何在内存的数据区域寻找要处理的数据,这块区域也称为堆栈,指令码放在另外的指令区,此外,还有指令指针机制,当处理器完成一个指令码的处理后,指令指针指向下一条指令码。

IA-32指令码(INTELAMD公司的CPU使用)由一堆二进制码构成,其格式为:

指令前缀、操作码、可选修饰符、可选数据元素

指令前缀可包含14个修改操作码行为的1字节前缀,分为:

锁定前缀和重复前缀

段覆盖前缀和分支提示前缀

操作数长度覆盖前缀

地址长度覆盖前缀

操作码定义了处理器执行的功能

修饰符定义执行功能时涉及的寄存器和内存位置。

数据元素是完成功能需要使用的数据,这些数据可以是直接的数据值,也可以是数据在内存中的地址。

麦好的AI乐园博客所有内容是原创,如果转载请注明来源

http://blog.csdn.net/myhaspl/


2、汇编语言

   以LINUX/UNIX环境下的汇编语言AT&T汇编(WINDOWS下有一种常用的汇编格式Intel汇编)进行讲解,汇编语言允许程序员方便地创建指令码程序,但不是用那些二进制编码的格式,还是使用助记符,助记符使用不同的词表示不同的指令码,有了助记符,程序员可以用英语来书写在目标机器上执行的指令码,不用记忆那些无趣的二进制编码。

     通常, FreeBSD 的内核使用 语言的调用规范。 此外, 虽然我们使用 int  $0x80来访问内核, 但是我们常常通过调用一个函数来执行  int  $0x80, 而不是直接访问。这个规范是非常方便的, 比 Microsoft� 的 MS-DOS上使用的规范更加优越。 为什么呢? 因为 UNIX� 的规范允许任何语言所写的程序访0问内核。这意味着在freebsd下访问内核需要先将参数压入栈中,然后再执行 int  $0x80调用内核中断,执行内核函数。 

 

  下面这段代码是经典的helloworld汇编代码:

  dp@dp:~ % vim helloworld.s

   #hello.s 

  .data                    # 数据段声明

        msg : .string "Hello, world!\n" # 要输出的字符串

        len = . - msg                   # 字串长度

.text                    # 代码段声明

.global _start           # 指定入口函数

 

_start:                  # 在屏幕上显示一个字符串

        pushl $len  # 参数三:字符串长度

        pushl $msg  # 参数二:要显示的字符串

        pushl $1    # 参数一:文件描述符(stdout) 

        movl $4, %eax    # 系统调用号(sys_write) 

        pushl %eax

        int  $0x80       # 调用内核功能

 

                         # 退出程序

        movl $0,%ebx     # 参数一:退出代码

        movl $1,%eax     # 系统调用号(sys_exit) 

        int  $0x80       # 调用内核功能

LINUX/UNIX(以freebsd为例)下,可以使用gsld软件汇编和链接。

dp@dp:~ % as -o helloworld.o helloworld.s

dp@dp:~ % ld -o helloworld helloworld.o

dp@dp:~ % ./helloworld

Hello, world!

dp@dp:~ % 

也可以直接使用GCC命令编译,但用gcc编译时将入口函数名由_start改为main

dp@dp:~ % vim helloworld.s

#hello.s 

.data                    # 数据段声明

        msg : .string "Hello, world!\n" # 要输出的字符串

        len = . - msg                   # 字串长度

.text                    # 代码段声明

.global main           # 指定入口函数

 

main:                  # 在屏幕上显示一个字符串

        pushl $len  # 参数三:字符串长度

        pushl $msg  # 参数二:要显示的字符串

        pushl $1    # 参数一:文件描述符(stdout) 

        movl $4, %eax    # 系统调用号(sys_write) 

        pushl %eax

        int  $0x80       # 调用内核功能

 

                         # 退出程序

        movl $0,%ebx     # 参数一:退出代码

        movl $1,%eax     # 系统调用号(sys_exit) 

        int  $0x80       # 调用内核功能

~                                               

汇编后运行

dp@dp:~ % gcc -o helloworld helloworld.s

dp@dp:~ % ./helloworld

Hello, world!

dp@dp:~ %

麦好的AI乐园博客所有内容是原创,如果转载请注明来源

http://blog.csdn.net/myhaspl/


对于ubuntuLinux 是一个类 UNIX 操作系统。 但是, 它的内核在传递参数的时候, 使用和 MS-DOS 相同系统调用规范。 比如在 UNIX 的规范中, 代表内核函数的数字存放在 EAX 中。 但是在 Linux 中, 参数不进行压栈而是存放在 EBX, ECX, EDX, ESI, EDI, EBP。因此在ubuntu下这段代码需要这样编写(设使用GCC编译)

#hello.s 

.data                    # 数据段声明

        msg : .string "Hello, world!\\n" # 要输出的字符串

        len = . - msg                   # 字串长度

  ext                    # 代码段声明

global main           # 指定入口函数

 

main:                  # 在屏幕上显示一个字符串

        movl $len, %edx  # 参数三:字符串长度

        movl $msg, %ecx  # 参数二:要显示的字符串

        movl $1, %ebx    # 参数一:文件描述符(stdout) 

        movl $4, %eax    # 系统调用号(sys_write) 

        int  $0x80       # 调用内核功能

 

                         # 退出程序

        movl $0,%ebx     # 参数一:退出代码

        movl $1,%eax     # 系统调用号(sys_exit) 

        int  $0x80       # 调用内核功能

2、C语言编译

       C语言属于高级语言,对汇编语言程序员来说也许是一种解脱,完成同样的任务,程序编码量减少很多,这只是把很多编码生成转移除到了编译器上来而已,让机器承担这部分编码生成工作。

    

freebsd系统、intel Intel(R) Pentium(R)CPU为例,编写以下hello,world代码

dp@dp:~ % cat hello.c

#include <stdio.h>

int main(){

printf("hello,world\n");

return 0;

}

dp@dp:~ % 

编译后,运行

dp@dp:~ % gcc -o hello hello.c 

dp@dp:~ % ./hello

hello,world

使用GCC的 -S选项生成C语言对应的汇编代码

dp@dp:~ % gcc -S hello.c  

下面是刚才生成的汇编语言代码

dp@dp:~ % cat hello.s

.file "hello.c"

.section .rodata

.LC0:

.string "hello,world"

.text

.p2align 4,,15

.globl main

.type main, @function

main:

leal 4(%esp), %ecx

andl $-16, %esp

pushl -4(%ecx)

pushl %ebp

movl %esp, %ebp

pushl %ecx

subl $4, %esp

movl $.LC0, (%esp)

call puts

movl $0, %eax

addl $4, %esp

popl %ecx

popl %ebp

leal -4(%ecx), %esp

ret

.size main, .-main

.ident "GCC: (GNU) 4.2.1 20070831 patched [FreeBSD]"

.section .note.GNU-stack,"",@progbits

dp@dp:~ % 

0 0
原创粉丝点击