gcc汇编

转自http://blog.chinaunix.net/u1/48280/showart_1423294.html

1.gcc嵌入汇编
(1). 在gcc嵌入汇编中输入输出使用相同的寄存器?
  1. static void * __memcpy(void * to, const void * from, size_t n) 
  2. int d0,d1,d2; 
  3. __asm__ __volatile__( 
  4.    "rep;movsl/n/t" 
  5.    "testb $2,%b4/n/t" 
  6.    "je 1f/n/t" 
  7.    "movsw/n" 
  8.    "1:/ttestb $1,%b4/n/t" 
  9.    "je 2f/n/t" 
  10.    "movsb/n" 
  11.    "2:" 
  12.    :"=&c" (d0), "=&D" (d1), "=&S" (d2) 
  13.    :"0" (n/4), "q" (n), "1" ((long) to), "2" ((long) from) 
  14.    :"memory"); 
  15. return (to); 

操作数0,1,2和3,5,6使用相同的寄存器的理解:
a. 3,5,6在输入过程中将值n/4,to和from的地址读入ecx,edi,esi寄存器中;
b. 0,1,2在输出过程中将ecx,edi,esi寄存器中的值存入d0,d1,d2内存变量中。
c. 注意在上面的语句中也有"&"限定符,但输入和输出仍使用相同的寄存器,如操作数0和3都使用寄存器ecx,这是因为"0"限定的结果,如果
把"0" (n/4)换成"c" (n/4),则因为"=&c"的限定而使得编译时报错。
(2). 关于gcc嵌入式汇编中"&"限定符的作用?
"&"限定符用于输出操作数,使其唯一的使用某寄存器
  1. int bar,foo; 
  2. __asm__ __volatile__( 
  3. "call func /n/t" 
  4. "mov ebx,%1" 
  5. :"=a" (foo) 
  6. :"r" (bar)); 

在gcc编译时默认会让bar也使用eax寄存器,如果把"=a"改为"=&a",那么foo将唯一使用eax,而让bar使用其它的寄存器。
(3). _start和main的关系?
main是gcc看到的程序入口点,而ld和as所看到的程序入口点其实是_start。libc库中的_start会调用main函数。
a. 编译带_start的汇编程序时的步骤:as -o a.o a.s,ld -o a a.o; gcc -g -c a.s,ld -o a a.o。(使用gcc编译可以增加调试选项-g,这
时不能直接用gcc -o a a.s编译的原因是gcc会默认在程序中查找main函数,并且会将libc中的_start加入进来。如果直接用gcc -o a a.s编译
,则会报两个错误"重复的_start"和"没找到main")
b. 编译带main的汇编程序或C程序时的步骤:gcc -o a a.s;gcc -o a a.c。
(4).section和.previous
将这两个.section和.previous中间的代码汇编到各自定义的段中,然后跳回去,将这之后的的代码汇编到上一个section中(一般是.text段),
也就是自定义段之前的段。.section和.previous必须配套使用。
2. AT&T汇编
(1).data,.section等都是伪操作,不能直接翻译成机器码,只有相应的assembler才能识别
(2).section将程序分成几个片断,如.data,.text,.bss
(3).globl 函数名表示该函数被export,并可以被其它文件中的函数调用
(4).bss可以用来申请一块空间,但不需要对其进行初始化
(5)使用内核定义函数如open,read等可以通过int $80中断来完成
(6)MOVL $FOO,%EAX是把FOO在内存中的地址放到EAX中,而MOVL FOO,%EAX是把FOO这个变量的内容放入EAX
(7).include "文件名",将其它文件包含进来
(8)如何在汇编中表示结构?如c语言中的如下结构:
  1. struct para 
  2. char Firstname[40]; 
  3. char Lastname[40]; 
  4. char Address[240]; 
  5. long Age;//4 bytes 
  6. 在汇编中可以表示成: 
  7. .section data 
  8. record1: 
  9. .ascii "Fredrick/0" 
  10. .rept 31 #Padding to 40 bytes 
  11. .byte 0 
  12. .endr 
  13. .ascii "Bartlett/0" 
  14. .rept 31 #Padding to 40 bytes 
  15. .byte 0 
  16. .endr 
  17. .ascii "4242 S Prairie/nTulsa, OK 55555/0" 
  18. .rept 209 #Padding to 240 bytes 
  19. .byte 0 
  20. .endr 
  21. .long 45 

其中.rept n和.endr表示重复两者之间的序列n次,可用于填充数据
(9)当汇编程序由多个文件构成时,可以采用以下方式编译与连接:
as write-records.s -o write-records.o (gcc -g -c write-records.s)
as write-record.s -o write-record.o (gcc -g -c write-record.s)
ld write-record.o write-records.o -o write-records
(10)如何在汇编语言中使用动态库中的函数?
#helloworld-lib.s
.section .data
helloworld:
.ascii "hello world/n/0"
.section .text
.globl _start
_start:
pushl $helloworld
call printf
pushl $0
call exit
as helloworld-lib.s -o helloworld-lib.o
ld -dynamic-linker /lib/ld-linux.so.2 -o helloworld-lib helloworld-lib.o -lc
产生动态库:ld -shared write-record.o read-record.o -o librecord.so
(11)使用汇编文件生成动态库
as write-record.s -o write-record.o
as read-record.s -o read-record.o
ld -shared write-record.o read-record.o -o librecord.so
as write-records.s -o write-records.o
ld -L . -dynamic-linker /lib/ld-linux.so.2 -o write-records -lrecord write-records.o
记得运行write-records时还需要将动态库路径加到/etc/ld.so.conf中,并运行ldconfig
(12)编译汇编文件时如何产生调试符号
as --gstabs a.s -0 a.o 或者gcc -g -c a.s
附录:
(1)函数调用时栈的情况
#Parameter #N <--- N*4+4(%ebp)
#...
#Parameter 2 <--- 12(%ebp)
#Parameter 1 <--- 8(%ebp)
#Return Address <--- 4(%ebp)
#Old %ebp <--- (%ebp)
#Local Variable 1 <--- -4(%ebp)
#Local Variable 2 <--- -8(%ebp) and (%esp)
(2)例子
  1. .include "external_func.s" 
  2. .section .data 
  3. data_array:      #定义long型数组 
  4. .long 3,67,34,0          
  5. data_strings:     #定义字符串 
  6. .ascii "Hello there/0" 
  7. data_long: #定义long型变量 
  8. .long 5 
  9. .section .bss 
  10. .lcomm my_buffer, 500    #申请一块500字节的内存 
  11. .section .text 
  12. .equ LINUX_SYSCALL, 0x80 #定义符号LINUX_SYSCALL的值为0x80 
  13. .globl _start 
  14. _start: 
  15. pushl %edx 
  16. movl data_long,%edx      #将data_long变量的值放入edx寄存器 
  17. movl $data_long,%edx     #将data_long的地址放入edx寄存器 
  18. popl %edx 
  19. pushl $3       #push second argument 
  20. pushl $2       #push first argument 
  21. call power     #call the function 
  22. addl $8, %esp #move the stack pointer back 
  23. pushl %eax     #save the first answer before,calling the next function 
  24. movl $1, %eax #exit (%ebx is returned) 
  25. int $LINUX_SYSCALL      
  26. .type power, @function #定义函数power 
  27. power: 
  28. pushl %ebp          #save old base pointer 
  29. movl %esp, %ebp     #make stack pointer the base pointer 
  30. subl $4, %esp       #get room for our local storage 
  31. movl 8(%ebp), %eax #put first argument in %eax 
  32. movl 12(%ebp), %ebx #put second argument in %ebx 
  33. imull %ebx,%eax 
  34. movl %ebp, %esp     #restore the stack pointer 
  35. popl %ebp           #restore the base pointer 
  36. ret 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
setjmp是一个C语言库函数,用于实现非局部跳转(longjmp)。在GCC汇编中,可以通过使用汇编嵌入(inline assembly)的方式来调用setjmp。 下面是一个示例代码: ```c #include <setjmp.h> jmp_buf jump_buffer; int main() { int r = setjmp(jump_buffer); if (r == 0) { // 跳转到这里 } else { // 在这里处理跳转返回 } } ``` 以上的代码通过setjmp将程序的控制流程保存在了jump_buffer中,并返回了一个值r。通过在不同的条件下调用longjmp,可以在程序的不同位置跳转到jump_buffer所指向的位置。 在汇编中,可以通过使用.x86_64特殊标志来指示应该使用64位模式来编译代码。以下是一个使用汇编嵌入的setjmp示例代码: ```c #include <setjmp.h> jmp_buf jump_buffer; int my_setjmp() { int r; asm volatile ("movq %%rsp, %0\n\t" // 保存rsp "movq %1, %%rsp\n\t" // 跳转到jump_buffer "xorl %%eax, %%eax\n\t" "movl $1, %%eax\n\t" "jmp save_regs\n" "save_regs:" "pushq %%rbp\n\t" // 保存rbp "movq %%rsp, %%rbp\n\t" // 保存rsp到rbp "movq %%rax, (%1)\n\t" // 保存返回值 "lea 8(%%rbp), %%rsp\n\t" // 跳过返回地址恢复栈指针 "popq %%rax\n\t" // 恢复rax "popq %%rbp\n\t" // 恢复rbp "movl $0, %%eax\n\t" "ret\n\t" : "=m" (jump_buffer), "=m" (r) : : "memory"); return r; } void my_longjmp(int r) { asm volatile ("movq %0, %%rax\n\t" "jmp restore_regs\n" "restore_regs:" "movq %%rax, %%rsp\n\t" "popq %%rbp\n\t" "xorl %%eax, %%eax\n\t" "movl %1, %%eax\n\t" "jmp *%2\n\t" : : "m" (jump_buffer), "m" (r), "m" (jump_buffer[0]) : "memory"); } ``` 上述代码中实现了自定义的setjmp和longjmp函数。在my_setjmp中,使用汇编指令将当前rsp指针保存到jump_buffer中,并返回值1。在my_longjmp中,将jmp_buf指针中保存的rsp值恢复,并设置返回值。具体实现中,需要注意图示的栈帧结构,并使用volatile标记来强制GCC不要将某些寄存器的值保存在寄存器中而是强制保存到内存。同时,在GCC优化开启的情况下,需要使用"memory"约束来告知编译器此区域存储内容被内联汇编代码更改了。 这样,就可以在GCC汇编中使用setjmp和longjmp实现非局部跳转。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值