Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1754244
  • 博文数量: 413
  • 博客积分: 8399
  • 博客等级: 中将
  • 技术积分: 4325
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-09 10:44
文章分类

全部博文(413)

文章存档

2015年(1)

2014年(18)

2013年(39)

2012年(163)

2011年(192)

分类: LINUX

2011-09-22 11:16:28

接着 没有翻译完的继续进行翻译。

命令行参数在Linux中也是安排在堆栈上。argc在最前面,接着是保存参数字符串的指针数组,该指针数组的最后是一个NULL指针,第三个是一个保存执行环境的指针数组。这些参数都可以在汇编中很容易的获得,参加汇编代码(args.s)。

  1. # args.s 
  2. .include "defines.h"
  3. .text
  4. .globl _start
  5. _start:

  6. popl %ecx         # argc,每次用write输出一个字符串参数。循环%ecx次(lewp: ... jmp lewp 构成大循环)

  7. lewp:             # 大循环的开始处
  8. popl %ecx         # argv,每一个字符串的开始地址保存在%ecx中,作为write函数的第2个参数
  9. test %ecx, %ecx   # argv的最后一个字符串指针是NULL根据这个来判断是否要结束大循环
  10. jz exit           # argv为NULL,跳到执行推出的代码处。
  11. movl %ecx, %ebx   # 将字符串的地址赋值给%ebx,来用于计算字符串的长度
  12. xorl %edx, %edx   # 将%edx清零,来计算字符串的长度

  13. strlen:           # 小循环(strlen: ... jnz  strlen 用于计算每一个字符串参数的长度)
  14. movb (%ebx), %al  # 每次一个字节,每一个字节%edx增加1
  15. inc  %edx         # %edx是保存字符串的长度,是write函数的第3个参数
  16. inc  %ebx         # %edx自增,移到下一个字符的位置处
  17. test %al, %al     # 当遇到字符结尾的'\0'时,结束小循环,从而得到字符串的长度
  18. jnz  strlen
  19. movb $10, -1(%ebx) # ???
  20. # write(1,argv[i],strlen(argv[i]));  # 每次循环利用write输出一个字符串参数
  21. movl $SYS_write, %eax  # write函数的系统调用号
  22. movl $STDOUT, %ebx     # write函数的第1个参数
  23. int  $0x80
  24. jmp lewp         # 继续大循环

  25. exit:
  26. movl $SYS_exit, %eax  # exit的系统调用号
  27. xorl %ebx, %ebx       # exit的参数0
  28. int  $0x80            # 利用中断调用exit(0)函数
  29. ret

其中头文件defines.h的内容(都是一些系统调用号的宏定义)如下:

  1. SYS_exit = 1
  2. SYS_fork = 2
  3. SYS_write = 4
  4. SYS_open = 5
  5. SYS_close = 6
  6. SYS_execve = 11
  7. SYS_lseek = 19
  8. SYS_dup2 = 63
  9. SYS_mmap = 90
  10. SYS_munmap = 91
  11. SYS_socketcall = 102
  12. SYS_socketcall_socket = 1
  13. SYS_socketcall_bind = 2
  14. SYS_socketcall_listen = 4
  15. SYS_socketcall_accept = 5

  16. SEEK_END = 2
  17. PROT_READ = 1
  18. MAP_SHARED = 1

  19. AF_INET = 2
  20. SOCK_STREAM = 1
  21. IPPROTO_TCP = 6

  22. STDOUT = 1

编译、运行及结果:

  1. digdeep@ubuntu:~/assembly$ as -o args.o args.s
  2. digdeep@ubuntu:~/assembly$ ld -o args args.o
  3. digdeep@ubuntu:~/assembly$ ./args
  4. ./args
  5. digdeep@ubuntu:~/assembly$ ./args arg1 arg2 arg3
  6. ./args
  7. arg1
  8. arg2
  9. arg3

本小节讲的GCC内联汇编仅仅是指x86平台下。其它的处理器会存在一些差异。

简单的内联汇编是十分直接的。它的简单形式如下:

__asm__("movl %esp,%eax");    // look familiar ? 

or

 __asm__(" movl $1,%eax         // SYS_exit

                 xor %ebx,%ebx

                 int $0x80 "

);

我们能够通过指定“输入数据”、“输出数据”以及“要修改的寄存器”,来更有效的使用内联汇编编程。这三个部分 input/output/modify都是可选的。它们的格式如下:

__asm__("" : output : input : modify);

指定的输入和输出部分必须是由:严格的字符串后面跟着一个被括号包含的C表达式。另外在输出部分的前面必须有一个“=”等号,暗示他是输出部分。允许有多个input/output/modify,各个部分用','隔开,但是总数不能超过10个。字符串即可以包含寄存器的全面,也可以是简写的形式。

Abbrev Table
AbbrevRegister
a%eax/%ax/%al
b%ebx/%bx/%bl
c%ecx/%cx/%cl
d%edx/%dx/%dl
S%esi/%si
D%edi/%di
mmemory
Example:
__asm__("test %%eax,%%eax", : /* no output */ : "a"(foo)); OR
__asm__("test %%eax,%%eax", : /* no output */ : "eax"(foo)); 你也可以在__asm__的后面使用关键字__volatile__ :来阻止一个汇编指令被优化掉。

You can also use the keyword __volatile__ after __asm__: "You can prevent an `asm' instruction from being deleted, moved significantly, or combined, by writing the keyword `volatile' after the `asm'."

(引自:the "Assembler Instructions with C Expression Operands" section in the gcc info files.)
  1. #include <stdio.h>

  2. int main(void) {
  3.     int foo=10,bar=15;
  4.     
  5.     __asm__ __volatile__ (
  6.         "addl %%ebxx, %%eax"
  7.         : "=eax"(foo)              // ouput
  8.         : "eax"(foo), "ebx"(bar)   // input
  9.         : "eax"                    // modify
  10.     );
  11.     printf("foo+bar=%d\n", foo);
  12.     return 0;
  13. }
也许你已经注意到寄存器的前缀变成了"%%",而不是"%"。在内联汇编中不强制使用"eax"、"ax"、"al",你可以简单的写成"a"。其它的寄存器也可以用它的简写形式。参见上面的简写表格。
  1. int main(void) {
  2.     long eax;
  3.     short bx;
  4.     char cl;
  5.     __asm__("nop;nop;nop"); // to separate inline asm from the rest of the code
  6.     __volatile__ __asm__("
  7.         test    %0,%0
  8.         test    %1,%1
  9.         test    %2,%2"
  10.         : /* no outputs */
  11.         : "a"((long)eax), "b"((short)bx), "c"((char)cl)
  12.     );
  13.     __asm__("nop;nop;nop");
  14.     return 0;
  15. }
注意:如果需要同时执行多条汇编语句,则应该用"\n\t"将各个语句分隔开!
编译、运行及结果:
  1. digdeep@ubuntu:~/assembly$ gcc -o inline2 inline2.c
  2. digdeep@ubuntu:~/assembly$ ./inline2
  3. digdeep@ubuntu:~/assembly$ echo #?

  4. digdeep@ubuntu:~/assembly$

利用gdb调试:


  1. digdeep@ubuntu:~/assembly$ gdb ./inline2
  2. GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
  3. Copyright (C) 2010 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "i686-linux-gnu".
  9. For bug reporting instructions, please see:
  10. <http://www.gnu.org/software/gdb/bugs/>...
  11. Reading symbols from /home/digdeep/assembly/inline2...(no debugging symbols found)...done.
  12. (gdb) disassemble main
  13. Dump of assembler code for function main:
  14.    0x08048394 <+0>:    push %ebp
  15.    0x08048395 <+1>:    mov %esp,%ebp
  16.    0x08048397 <+3>:    push %ebx
  17.    0x08048398 <+4>:    sub $0x10,%esp
  18.    0x0804839b <+7>:    nop
  19.    0x0804839c <+8>:    nop
  20.    0x0804839d <+9>:    nop
  21.    0x0804839e <+10>:    mov -0xc(%ebp),%eax
  22.    0x080483a1 <+13>:    movzwl -0x8(%ebp),%edx
  23.    0x080483a5 <+17>:    movzbl -0x5(%ebp),%ecx
  24.    0x080483a9 <+21>:    mov %edx,%ebx
  25.    0x080483ab <+23>:    test %eax,%eax
  26.    0x080483ad <+25>:    test %bx,%bx
  27.    0x080483b0 <+28>:    test %cl,%cl
  28.    0x080483b2 <+30>:    nop
  29.    0x080483b3 <+31>:    nop
  30.    0x080483b4 <+32>:    nop
  31.    0x080483b5 <+33>:    mov $0x0,%eax
  32.    0x080483ba <+38>:    add $0x10,%esp
  33.    0x080483bd <+41>:    pop %ebx
  34.    0x080483be <+42>:    pop %ebp
  35.    0x080483bf <+43>:    ret 
  36. End of assembler dump.
  37. (gdb)

就像你看到的,从内联汇编生成的代码,将变量的值加载到寄存器中,因为这些寄存器放在了内联汇编的“输入部分”。编译器会自动检测到操作数的大小,所以编译器可以用它们的别名%0, %1, %2来代表。

寄存器的别名也可以被用到操作数的地方,但我们在input/output中不能指定超过10个的寄存器。

另外,我们用"q"来让编译器在a,b,c,d中自己来选择哪个寄存器。但是,当寄存器被修改之后,因为我们不知到哪个寄存器被选中,所以我们也不能在modify修改部分指定哪个寄存器。但是我们可以用寄存器别名的数字来指定。(注:其实新版的gcc已经不需要指定修改部分!!)

  1. #include <stdio.h>

  2. int main(void) {
  3.         long eax=1,ebx=2;

  4.         __asm__ __volatile__ ("add %0,%2"
  5.                 : "=b"((long)ebx)           // %ebx作为输出
  6.                 : "a"((long)eax), "q"(ebx)  // %eax作为变量eax的输入,变量ebx任选一个寄存器作为输入 
  7.                 //: "1"
  8.         );
  9.         printf("ebx=%lx\n", ebx);
  10.         return 0;
  11. }

编译、运行结果:

  1. digdeep@ubuntu:~/assembly$ gcc -o inline3 inline3.c
  2. digdeep@ubuntu:~/assembly$ ./inline3
  3. ebx=f19ff4
  4. digdeep@ubuntu:~/assembly$ ./inline3
  5. ebx=9cbff4
  6. digdeep@ubuntu:~/assembly$ ./inline3
  7. ebx=fb9ff4
  8. digdeep@ubuntu:~/assembly$

这里很奇怪:打印的结果不是我们所希望的!?每次都在变化,但是最后3位总是ff4.

Example Code: linux-asm.rar   


阅读(1800) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~