同学问了个问题:问里面到底是怎样加的,俺们是学过编译原理的,只能看汇编了~。限于VS启动慢,用了mingw中的gdb调试:disassemble,反汇编出来看代码。

 
  
  1. #include <iostream>  
  2. using namespace std;  
  3. int main(int argc, char *argv[])  
  4. {  
  5.     int x = 7;  
  6.       
  7.     x = (9+x++) + (16+x++);  
  8.       
  9.     cout<<x<<endl;  
  10.       
  11.     return 0;  

反汇编:

 
  
  1. Dump of assembler code for function main:  
  2. 0x00401318 <main+0>:    push   %ebp  
  3. 0x00401319 <main+1>:    mov    %esp,%ebp  
  4. 0x0040131b <main+3>:    and    $0xfffffff0,%esp  
  5. 0x0040131e <main+6>:    sub    $0x20,%esp  
  6. 0x00401321 <main+9>:    call   0x413390 <__main>  
  7. 0x00401326 <main+14>:   movl   $0x0,0x1c(%esp)  
  8. 0x0040132e <main+22>:   mov    0x1c(%esp),%eax  
  9. 0x00401332 <main+26>:   lea    0x1(%eax),%edx  
  10. 0x00401335 <main+29>:   mov    0x1c(%esp),%eax  
  11. 0x00401339 <main+33>:   add    $0x2,%eax  
  12. 0x0040133c <main+36>:   lea    (%edx,%eax,1),%eax  
  13. 0x0040133f <main+39>:   mov    %eax,0x1c(%esp)  
  14. 0x00401343 <main+43>:   incl   0x1c(%esp)  
  15. 0x00401347 <main+47>:   incl   0x1c(%esp)  
  16. 0x0040134b <main+51>:   mov    0x1c(%esp),%eax  
  17. 0x0040134f <main+55>:   mov    %eax,0x4(%esp)  
  18. 0x00401353 <main+59>:   movl   $0x4740c0,(%esp)  
  19. 0x0040135a <main+66>:   call   0x4485b0 <_ZNSolsEi>  
  20. 0x0040135f <main+71>:   mov    $0x0,%eax  
  21. 0x00401364 <main+76>:   leave    
  22. 0x00401365 <main+77>:   ret      
  23. End of assembler dump.  
  24.  

 可见,调用逻辑就是最后才做俩次自增操作....

想看下lea的,就搜了些资料~

资料:http://xue23.blog.163.com/blog/static/979344200662736300/

 
  
  1. linux汇编编程  2006-07-27 15:06:30|  分类: Asm |  标签: |字号大中小 订阅 .  
  2.  
  3. Author:philliph(phillip@u***back.com)  
  4. translator:horacexue(xue23@163.com)  
  5.  
  6. 介绍  
  7. 这篇文章描述在linux下使用汇编语言编程的方法。其中包含的内容有:  
  8.  
  9. Intel及AT&T汇编语言的比较  
  10.  
  11. 怎么使用系统调用  
  12.  
  13. 怎么在GCC中使用嵌入的汇编  
  14.  
  15. 写这篇文章的原因是因为在这方面编程(特别是内嵌汇编编程)缺乏比较好的材料,我们在这里不写shell编程,因为这方面的材料很容易找到。  
  16.  
  17. 这篇文章是都是我的经验所得,所以不免会有错误。如果你发现任何问题,一定要发邮件给我,或者给我指导。  
  18.  
  19. 读这篇文章具备的条件是有x86汇编和c语言方面的基础。  
  20.  
  21.  
  22. --------------------------------------------------------------------------------  
  23. Intel和AT&T的语法.  
  24. Intel和AT&T汇编语法在有很大的差别,所以一个懂Intel汇编语法的人开始去学习At&t时,很容易造成混乱,反过也是一样。下面我们从基础开始学起。  
  25.  
  26. 前缀  
  27.  
  28. Intel的汇编语法中,寄存器和立即数立即数没有前缀。而在AT&T汇编语法中在寄存器前面要加上前缘%,立即数前面要加上前缀$。Intel语法的16进制,2进制立即数必须分别加上后缀h,b,另外如果第一个16进制数是字母,那么该值必须加上前缀0.  
  29.  
  30. Example:  
  31. Intex Syntax  
  32.  
  33. mov eax,1  
  34. mov ebx,0ffh  
  35. int 80h  
  36.  AT&T Syntax  
  37.  
  38. movl    $1,%eax  
  39. movl    $0xff,%ebx  
  40. int     $0x80  
  41.    
  42. Direction of Operands.  
  43. The direction of the operands in Intel syntax is opposite from that of AT&T syntax. In Intel syntax the first operand is the destination, and the second operand is the source whereas in AT&T syntax the first operand is the source and the second operand is the destination. The advantage of AT&T syntax in this situation is obvious. We read from left to right, we write from left to right, so this way is only natural.  
  44.  
  45. Example:  
  46. Intex Syntax  
  47.  
  48. instr   dest,source  
  49. mov eax,[ecx]  
  50.  AT&T Syntax  
  51.  
  52. instr   source,dest  
  53. movl    (%ecx),%eax  
  54.    
  55. Memory Operands.  
  56. Memory operands as seen above are different also. In Intel syntax the base register is enclosed in '[' and ']' whereas in AT&T syntax it is enclosed in '(' and ')'.   
  57.  
  58. Example:  
  59. Intex Syntax  
  60.  
  61. mov eax,[ebx]  
  62. mov eax,[ebx+3]  
  63.  AT&T Syntax  
  64.  
  65. movl    (%ebx),%eax  
  66. movl    3(%ebx),%eax   
  67.    
  68.  
  69. The AT&T form for instructions involving complex operations is very obscure compared to Intel syntax. The Intel syntax form of these is segreg:[base+index*scale+disp]. The AT&T syntax form is %segreg:disp(base,index,scale).   
  70.  
  71. Index/scale/disp/segreg are all optional and can simply be left out. Scale, if not specified and index is specified, defaults to 1. Segreg depends on the instruction and whether the app is being run in real mode or pmode. In real mode it depends on the instruction whereas in pmode its unnecessary. Immediate data used should not '$' prefixed in AT&T when used for scale/disp.  
  72.  
  73. Example:  
  74. Intel Syntax  
  75.  
  76. instr   foo,segreg:[base+index*scale+disp]  
  77. mov eax,[ebx+20h]  
  78. add eax,[ebx+ecx*2h  
  79. lea eax,[ebx+ecx]  
  80. sub eax,[ebx+ecx*4h-20h]  
  81.  AT&T Syntax  
  82.  
  83. instr   %segreg:disp(base,index,scale),foo  
  84. movl    0x20(%ebx),%eax  
  85. addl    (%ebx,%ecx,0x2),%eax  
  86. leal    (%ebx,%ecx),%eax  
  87. subl    -0x20(%ebx,%ecx,0x4),%eax  
  88.    
  89.  
  90. As you can see, AT&T is very obscure. [base+index*scale+disp] makes more sense at a glance than disp(base,index,scale).  
  91.  
  92. Suffixes.  
  93. As you may have noticed, the AT&T syntax mnemonics have a suffix. The significance of this suffix is that of operand size. 'l' is for long, 'w' is for word, and 'b' is for byte. Intel syntax has similar directives for use with memory operands, i.e. byte ptr, word ptr, dword ptr. "dword" of course corresponding to "long". This is similar to type casting in C but it doesnt seem to be necessary since the size of registers used is the assumed datatype.  
  94.  
  95. Example:  
  96. Intel Syntax  
  97.  
  98. mov al,bl  
  99. mov ax,bx  
  100. mov eax,ebx  
  101. mov eax, dword ptr [ebx]  
  102.  AT&T Syntax  
  103.  
  104. movb    %bl,%al  
  105. movw    %bx,%ax  
  106. movl    %ebx,%eax  
  107. movl    (%ebx),%eax  
  108.    
  109. **NOTE: ALL EXAMPLES FROM HERE WILL BE IN AT&T SYNTAX**  
  110.  
  111. --------------------------------------------------------------------------------  
  112. Syscalls.  
  113. This section will outline the use of linux syscalls in assembly language. Syscalls consist of all the functions in the second section of the manual pages located in /usr/man/man2. They are also listed in: /usr/include/sys/syscall.h. A great list is at http://www.linuxassembly.org/syscall.html. These functions can be executed via the linux interrupt service: int $0x80.   
  114.  
  115. Syscalls with < 6 args.  
  116. For all syscalls, the syscall number goes in %eax. For syscalls that have less than six args, the args go in %ebx,%ecx,%edx,%esi,%edi in order. The return value of the syscall is stored in %eax.  
  117.  
  118. The syscall number can be found in /usr/include/sys/syscall.h. The macros are defined as SYS_<syscall name> i.e. SYS_exit, SYS_close, etc.   
  119.  
  120. Example:  
  121. (Hello world program - it had to be done)   
  122. According to the write(2) man page, write is declared as: ssize_t write(int fd, const void *buf, size_t count);   
  123.  
  124. Hence fd goes in %ebx, buf goes in %ecx, count goes in %edx and SYS_write goes in %eax. This is followed by an int $0x80 which executes the syscall. The return value of the syscall is stored in %eax.  
  125.  
  126. $ cat write.s  
  127. .include "defines.h"  
  128. .data  
  129. hello:  
  130.     .string "hello world\n"  
  131.  
  132. .globl  main  
  133. main:  
  134.     movl    $SYS_write,%eax  
  135.     movl    $STDOUT,%ebx  
  136.     movl    $hello,%ecx  
  137.     movl    $12,%edx  
  138.     int $0x80  
  139.  
  140.     ret  
  141. $   
  142. The same process applies to syscalls which have less than five args. Just leave the un-used registers unchanged. Syscalls such as open or fcntl which have an optional extra arg will know what to use.   
  143.  
  144. Syscalls with > 5 args.  
  145. Syscalls whos number of args is greater than five still expect the syscall number to be in %eax, but the args are arranged in memory and the pointer to the first arg is stored in %ebx.  
  146.  
  147. If you are using the stack, args must be pushed onto it backwards, i.e. from the last arg to the first arg. Then the stack pointer should be copied to %ebx. Otherwise copy args to an allocated area of memory and store the address of the first arg in %ebx.  
  148.  
  149. Example:   
  150. (mmap being the example syscall). Using mmap() in C:  
  151.  
  152. #include <sys/types.h> 
  153. #include <sys/stat.h> 
  154. #include <sys/mman.h> 
  155. #include <fcntl.h> 
  156. #include <unistd.h> 
  157.  
  158. #define STDOUT  1  
  159.  
  160. void main(void) {  
  161.     char file[]="mmap.s";  
  162.     char *mappedptr;  
  163.     int fd,filelen;  
  164.  
  165.     fd=fopen(file, O_RDONLY);  
  166.     filelen=lseek(fd,0,SEEK_END);  
  167.     mappedptr=mmap(NULL,filelen,PROT_READ,MAP_SHARED,fd,0);  
  168.     write(STDOUT, mappedptr, filelen);  
  169.     munmap(mappedptr, filelen);  
  170.     close(fd);  
  171. }  
  172. Arrangement of mmap() args in memory: %esp %esp+4 %esp+8 %esp+12 %esp+16 %esp+20   
  173. 00000000 filelen 00000001 00000001 fd 00000000   
  174. ASM Equivalent:  
  175.  
  176. $ cat mmap.s  
  177. .include "defines.h"  
  178.  
  179. .data  
  180. file:  
  181.     .string "mmap.s"  
  182. fd:  
  183.     .long   0  
  184. filelen:  
  185.     .long   0  
  186. mappedptr:  
  187.     .long   0  
  188.  
  189. .globl main  
  190. main:  
  191.     push    %ebp  
  192.     movl    %esp,%ebp  
  193.     subl    $24,%esp  
  194.  
  195. //  open($file, $O_RDONLY);  
  196.  
  197.     movl    $fd,%ebx    // save fd  
  198.     movl    %eax,(%ebx)  
  199.  
  200. //  lseek($fd,0,$SEEK_END);  
  201.  
  202.     movl    $filelen,%ebx   // save file length  
  203.     movl    %eax,(%ebx)  
  204.  
  205.     xorl    %edx,%edx  
  206.  
  207. //  mmap(NULL,$filelen,PROT_READ,MAP_SHARED,$fd,0);  
  208.     movl    %edx,(%esp)  
  209.     movl    %eax,4(%esp)    // file length still in %eax  
  210.     movl    $PROT_READ,8(%esp)  
  211.     movl    $MAP_SHARED,12(%esp)  
  212.     movl    $fd,%ebx    // load file descriptor  
  213.     movl    (%ebx),%eax  
  214.     movl    %eax,16(%esp)  
  215.     movl    %edx,20(%esp)  
  216.     movl    $SYS_mmap,%eax  
  217.     movl    %esp,%ebx  
  218.     int $0x80  
  219.  
  220.     movl    $mappedptr,%ebx // save ptr  
  221.     movl    %eax,(%ebx)  
  222.           
  223. //  write($stdout, $mappedptr, $filelen);  
  224. //  munmap($mappedptr, $filelen);  
  225. //  close($fd);  
  226.       
  227.     movl    %ebp,%esp  
  228.     popl    %ebp  
  229.  
  230.     ret  
  231. $  
  232. **NOTE: The above source listing differs from the example source code found at the end of the article. The code listed above does not show the other syscalls, as they are not the focus of this section. The source above also only opens mmap.s, whereas the example source reads the command line arguments. The mmap example also uses lseek to get the filesize.** Socket Syscalls.  
  233. Socket syscalls make use of only one syscall number: SYS_socketcall which goes in %eax. The socket functions are identified via a subfunction numbers located in /usr/include/linux/net.h and are stored in %ebx. A pointer to the syscall args is stored in %ecx. Socket syscalls are also executed with int $0x80.  
  234.  
  235. $ cat socket.s  
  236. .include "defines.h"  
  237.  
  238. .globl  _start  
  239. _start:  
  240.     pushl   %ebp  
  241.     movl    %esp,%ebp  
  242.     sub $12,%esp  
  243.  
  244. //  socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);  
  245.     movl    $AF_INET,(%esp)  
  246.     movl    $SOCK_STREAM,4(%esp)  
  247.     movl    $IPPROTO_TCP,8(%esp)  
  248.  
  249.     movl    $SYS_socketcall,%eax  
  250.     movl    $SYS_socketcall_socket,%ebx  
  251.     movl    %esp,%ecx  
  252.     int $0x80  
  253.  
  254.     movl    $SYS_exit,%eax  
  255.     xorl    %ebx,%ebx  
  256.     int     $0x80  
  257.  
  258.     movl    %ebp,%esp  
  259.     popl    %ebp  
  260.     ret  
  261. $  
  262.  
  263. --------------------------------------------------------------------------------  
  264. Command Line Arguments.  
  265. Command line arguments in linux executables are arranged on the stack. argc comes first, followed by an array of pointers (**argv) to the strings on the command line followed by a NULL pointer. Next comes an array of pointers to the environment (**envp). These are very simply obtained in asm, and this is demonstrated in the example code (args.s).  
  266.  
  267.  
  268. --------------------------------------------------------------------------------  
  269. GCC内嵌汇编  
  270. 这章我们只讨论x86应用程序,在别的CPU上操作指令可能有所不同,我们可以从文章结尾的列表去查找有关这方面的资料。  
  271.  
  272. gcc中的内嵌的汇编的基本结构是简单明僚的。如下例所示:  
  273.  
  274.     __asm__("movl   %esp,%eax");    // look familiar ?  
  275. or   
  276.     __asm__("  
  277.             movl    $1,%eax     // SYS_exit  
  278.             xor %ebx,%ebx  
  279.             int $0x80  
  280.     ");通过指定的其值可以调整的寄存器作为输入输出数据,可能更有效的使用内嵌汇编, It is possible to use it more effectively by specifying the data that will be used as input, output for the asm as well as which registers will be modified. No particular input/output/modify field is compulsory. It is of the format:  
  281.  
  282.     __asm__("<asm routine>" : output : input : modify);  
  283. The output and input fields must consist of an operand constraint string followed by a C expression enclosed in parentheses. The output operand constraints must be preceded by an '=' which indicates that it is an output. There may be multiple outputs, inputs, and modified registers. Each "entry" should be separated by commas (',') and there should be no more than 10 entries total. The operand constraint string may either contain the full register name, or an abbreviation.  
  284.  
  285. Abbrev Table   
  286. Abbrev Register   
  287. a %eax/%ax/%al   
  288. b %ebx/%bx/%bl   
  289. c %ecx/%cx/%cl   
  290. d %edx/%dx/%dl   
  291. S %esi/%si   
  292. D %edi/%di   
  293. m memory   
  294. Example:  
  295.  
  296.     __asm__("test   %%eax,%%eax", : /* no output */ : "a"(foo));  
  297. OR  
  298.  
  299.     __asm__("test   %%eax,%%eax", : /* no output */ : "eax"(foo));  
  300. 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'."  
  301.  
  302. (Quoted from the "Assembler Instructions with C Expression Operands" section in the gcc info files.)   
  303. $ cat inline1.c  
  304. #include <stdio.h> 
  305.  
  306. int main(void) {  
  307.     int foo=10,bar=15;  
  308.       
  309.     __asm__ __volatile__ ("addl     %%ebxx,%%eax"   
  310.         : "=eax"(foo)       // ouput  
  311.         : "eax"(foo), "ebx"(bar)// input  
  312.         : "eax"         // modify  
  313.     );  
  314.     printf("foo+bar=%d\n", foo);  
  315.     return 0;  
  316. }  
  317. $  
  318. You may have noticed that registers are now prefixed with "%%" rather than '%'. This is necessary when using the output/input/modify fields because register aliases based on the extra fields can also be used. I will discuss these shortly.  
  319.  
  320. Instead of writing "eax" and forcing the use of a particular register such as "eax" or "ax" or "al", you can simply specify "a". The same goes for the other general purpose registers (as shown in the Abbrev table). This seems useless when within the actual code you are using specific registers and hence gcc provides you with register aliases. There is a max of 10 (%0-%9) which is also the reason why only 10 inputs/outputs are allowed.  
  321.  
  322. $ cat inline2.c  
  323. int main(void) {  
  324.     long eax;  
  325.     short bx;  
  326.     char cl;  
  327.  
  328.     __asm__("nop;nop;nop"); // to separate inline asm from the rest of  
  329.                 // the code  
  330.     __volatile__ __asm__("  
  331.         test    %0,%0  
  332.         test    %1,%1  
  333.         test    %2,%2"  
  334.         : /* no outputs */  
  335.         : "a"((long)eax), "b"((short)bx), "c"((char)cl)  
  336.     );  
  337.     __asm__("nop;nop;nop");  
  338.     return 0;   
  339. }  
  340. $ gcc -o inline2 inline2.c   
  341. $ gdb ./inline2  
  342. GNU gdb 4.18  
  343. Copyright 1998 Free Software Foundation, Inc.  
  344. GDB is free software, covered by the GNU General Public License, and you are  
  345. welcome to change it and/or distribute copies of it under certain conditions.  
  346. Type "show copying" to see the conditions.  
  347. There is absolutely no warranty for GDB.  Type "show warranty" for details.  
  348. This GDB was configured as "i686-pc-linux-gnulibc1"...  
  349. (no debugging symbols found)...  
  350. (gdb) disassemble main  
  351. Dump of assembler code for function main:   
  352. ... start: inline asm ...   
  353. 0x8048427 : nop  
  354. 0x8048428 : nop   
  355. 0x8048429 : nop   
  356. 0x804842a : mov 0xfffffffc(%ebp),%eax   
  357. 0x804842d : mov 0xfffffffa(%ebp),%bx  
  358. 0x8048431 : mov 0xfffffff9(%ebp),%cl   
  359. 0x8048434 : test %eax,%eax   
  360. 0x8048436 : test %bx,%bx  
  361. 0x8048439 : test %cl,%cl   
  362. 0x804843b : nop   
  363. 0x804843c : nop   
  364. 0x804843d : nop   
  365. ... end: inline asm ...   
  366. End of assembler dump.   
  367. $   
  368. As you can see, the code that was generated from the inline asm loads the values of the variables into the registers they were assigned to in the input field and then proceeds to carry out the actual code. The compiler auto detects operand size from the size of the variables and so the corresponding registers are represented by the aliases %0, %1 and %2. (Specifying the operand size in the mnemonic when using the register aliases may cause errors while compiling).   
  369.  
  370. The aliases may also be used in the operand constraints. This does not allow you to specify more than 10 entries in the input/output fields. The only use for this i can think of is when you specify the operand constraint as "q" which allows the compiler to choose between a,b,c,d registers. When this register is modified we will not know which register has been chosen and consequently cannot specify it in the modify field. In which case you can simply specify "<number>".  
  371.  
  372. Example:  
  373.  
  374. $ cat inline3.c  
  375. #include <stdio.h> 
  376.  
  377. int main(void) {  
  378.     long eax=1,ebx=2;  
  379.  
  380.     __asm__ __volatile__ ("add %0,%2"  
  381.         : "=b"((long)ebx)  
  382.         : "a"((long)eax), "q"(ebx)  
  383.         : "2"  
  384.     );  
  385.     printf("ebx=%x\n", ebx);  
  386.     return 0;  
  387. }  
  388. $  
  389.  
  390. --------------------------------------------------------------------------------  
  391. Compiling  
  392. Compiling assembly language programs is much like compiling normal C programs. If your program looks like Listing 1, then you would compile it like you would a C app. If you use _start instead of main, like in Listing 2 you would compile the app slightly differently:  
  393.  
  394. •Listing 1  
  395.  
  396. $ cat write.s  
  397. .data  
  398. hw:  
  399.     .string "hello world\n"  
  400. .text  
  401. .globl main  
  402. main:  
  403.     movl    $SYS_write,%eax  
  404.     movl    $1,%ebx  
  405.     movl    $hw,%ecx  
  406.     movl    $12,%edx  
  407.     int $0x80  
  408.     movl    $SYS_exit,%eax  
  409.     xorl    %ebx,%ebx  
  410.     int $0x80  
  411.     ret  
  412. $ gcc -o write write.s  
  413. $ wc -c ./write  
  414.    4790 ./write  
  415. $ strip ./write  
  416. $ wc -c ./write  
  417.    2556 ./write  
  418.  •Listing 2  
  419.  
  420. $ cat write.s  
  421. .data  
  422. hw:  
  423.     .string "hello world\n"  
  424. .text  
  425. .globl _start  
  426. _start:  
  427.     movl    $SYS_write,%eax  
  428.     movl    $1,%ebx  
  429.     movl    $hw,%ecx  
  430.     movl    $12,%edx  
  431.     int $0x80  
  432.     movl    $SYS_exit,%eax  
  433.     xorl    %ebx,%ebx  
  434.     int $0x80  
  435.  
  436. $ gcc -c write.s  
  437. $ ld -s -o write write.o  
  438. $ wc -c ./write  
  439.     408 ./write  
  440.    
  441.  
  442. The -s switch is optional, it just creates a stripped ELF executable which is smaller than a non-stripped one. This method (Listing 2) also creates smaller executables, since the compiler isnt adding extra entry and exit routines as would normally be the case.   
  443.  
  444.  
  445. --------------------------------------------------------------------------------  
  446. Links.  
  447. Further reference.  
  448. http://www.linuxassembly.org  
  449. GNU Assembler Manual  
  450. GNU C Compiler Manual  
  451. GNU Debugger Manual  
  452. Operand Constraint Reference  
  453. AT&T Syntax Reference  
  454. Example Code  
  455. args.s Reads command line arguments passed to the prog   
  456. daemon.s Binds a shell to a port (backdoor style)   
  457. mmap.s Maps a file to memory, and dumps its contents   
  458. socket.s Creates a socket   
  459. write.s Hello world !   
  460. linasm-src.tgz Makefile defines.h args.s daemon.s socket.s write.s