@perkyoung
2015-07-12T14:36:09.000000Z
字数 5118
阅读 3472
汇编语言
都是正数,所有的位都表示数字,例如0XFFFF,表示一个正数,但在gdb的时候会输出其补码,也就是负数。
最高为如果是1,表示负数,-16的补码表示方法:取16的二进制值,取反,加1
如果我们只用长度较小的无符号整数赋值给长度较大的。注意要将后者的高位清零。
movl $0, %ebxmovl %cl, %ebx
很麻烦,所以intel提供了一个指令,movzx %cl, %ebx后者的高位自动清零
如果扩展的是有符号整数,则不能将长度较大的寄存器的高位清零,因为最高为表示符号。intel提供了
movw $-79, %cxmovl $0, %ebx //下面两行显然是错了,因为%ebx显然失去了负数的属性movw %cx, %bxmovsx %cx, %eax //这个是ok的,据算cx是整数,也会正确
如果想显示8字节整数x/5gd
以后再看
这个需要看,包括溢出,进位等标志的操作
暂时略过
看完后面的函数之后在回来看,不着急
输入
定义函数处理
.type func1, @functionfuncl:<instruction....>ret
定义输出
.section .dataprecision:.byte 0x7f, 0x00.section .bss.lcomm radius, 4.lcomm result, 4.lcomm trach, 4.section .text.globl mainmain:nopfinitfldcw precisionmovl $10, radiuscall areamovl $2, radiuscall areamovl $120, radiuscall areamovl $1, %eaxmovl $0, %ebxint $0x80.type area, @functionarea:fldpifilds radiusfmul %st(0), %st(0)fmulp %st(0), %st(1)fstps resultret
但是,寄存器,就那么几个,全局变量搞得太多头疼,函数调用却很多很多,显然不够用。如果真这么写的话,绝对每天晚上睡不着觉。
经典程序堆栈
虚拟内存地址空间,0X80480000~0XBFFFFFFF
想当然的,程序一启动,esp就指向了0XBFFFFFFF,其实不是,在加载程序之前,linux就把一些内容存放到堆栈中,命令行参数就是如此
#include <stdio.h>int main(int argc, char * argv[]) {int c = argc;char * p = argv[1];return 0;}
反汇编的结果是
08048354 <main>:8048354: 55 push %ebp8048355: 89 e5 mov %esp,%ebp8048357: 83 ec 10 sub $0x10,%esp804835a: 8b 45 08 mov 0x8(%ebp),%eax //0x4(%ebp)是返回地址,这个就是第一个参数804835d: 89 45 fc mov %eax,-0x4(%ebp)//赋值给函数内的临时变量8048360: 8b 45 0c mov 0xc(%ebp),%eax //第二个参数是包含多个指针的数组8048363: 83 c0 04 add $0x4,%eax //指向了该数组的第2个(如果下标为0的是第一个的话),8048366: 8b 00 mov (%eax),%eax //取出该内存地址的值,该值是指向一个字符串的指针8048368: 89 45 f8 mov %eax,-0x8(%ebp) //赋值给第二个临时变量804836b: b8 00 00 00 00 mov $0x0,%eax8048370: c9 leave
先跳过
#include <stdio.h>int main(){int i = 100;char * output = "the result is %d";asm("nop\n\t""movl $0, %eax\n\t""movl $2, %ebx\n\t""int $0x80");}
编译出的汇编代码是
main:pushl %ebpmovl %esp, %ebpsubl $16, %espmovl $100, -4(%ebp)movl $.LC0, -8(%ebp)#APP //从这里到后面的NO_APP# 6 "./test.c" 1nopmovl $0, %eaxmovl $2, %ebxint $0x80# 0 "" 2#NO_APPpushl $100pushl $.LC0call printfaddl $8, %espleave
使用全局变量
#include <stdio.h>int a = 10;int b = 20;int result;int main() {asm("pusha\n\t""movl a, %eax\n\t""movl b, %ebx\n\t""imull %ebx, %eax\n\t""movl %eax, result\n\t");printf("the answer is %d\n", result);return 0;}
对应点汇编代码是
27 #APP28 # 7 "./globaltest.c" 129 pusha //内部代码使用了这些寄存器,最好压栈30 movl a, %eax //如果objdump -D ./a.out ,a表示的是内存地址,而不是一个立即数31 movl b, %ebx32 imull %ebx, %eax33 movl %eax, result3435 # 0 "" 236 #NO_APP
使用volatile修饰符,避免编译器优化
asm volatile ("nop");__asm__ __volatile__("");
到目前,所有的输入值和输出值都必须使用都必须使用C的全局变量,下面要解决这个问题
asm ("assembly code" : output location : input operands : chagned registers)
int main() {int data1 = 10;int data2 = 20;int result;__asm__("imull %%edx, %%ecx\n\t" //寄存器要多一个%"movl %%ecx, %%eax\n\t": "=a"(result) //=表示只能写入result: "d"(data1), "c"(data2));printf("the result is %d\n", result);return 0;}
不一定总要在内联汇编中指定输出值
#include <stdio.h>int main() {char input[30] = "this is a test message.\n";char output[30];int length = 25;asm volatile ("cld\n\t""rep movsb":: "S"(input), "D"(output), "c"(length)); //output同时也是输出,movsb指令负责复制字符串,没有定义专门的输出值,所以,volatile很重要,编译器不会误以为它没必要,而删除它。printf("%s", output);return 0;}
前面的例子中,我们将变量赋值给寄存器,但是如果变量特别多的话,这样编程会很麻烦,所以利用占位符。使用占位符引用输入输出。占位符就是前面加%的数字,引用的是输入输出值在asm中出现的位置,从0开始
int main() {int data1 = 10;int data2 = 20;int result;__asm__ ("imull %1, %2\n\t""movl %2, %0": "=r"(result): "r"(data1), "r"(data2));printf("the result is %d\n", result);return 0;}有兴趣可以看下对应的汇编代码#APP# 7 "./reg.c" 1imull %edx, %eaxmovl %eax, %eax //其实已经算好了,结果我们的asm代码又一次赋值# 0 "" 2#NO_APP
从上面的例子中可以看到,有时候使用相同的变量作为输入和输出值是很不错的。
int main() {int data1 = 10;int data2 = 20;int result;__asm__ ("imull %1, %0\n\t": "=r"(data2) //data2输出为%0寄存器: "r"(data1), "0"(data2)); //输入data2,指定到%0,printf("the result is %d\n", data2);return 0;}
如果输入输出变量多,用0,1,2表示就会很混乱,可以用替换的占位符
int main() {int data1 = 10;int data2 = 20;int result;__asm__ ("imull %[value1], %[value2]\n\t": [value2]"=r"(data2): [value1]"r"(data1), "0"(data2));printf("the result is %d\n", data2);return 0;}
还记得扩展asm格式吗?对,最后一个是改动的寄存器列表,下面谈谈
int main(){int data1 = 10;int result = 20;asm ("addl %1, %0\n\t": "=d"(result): "c"(data1) ,"0"(result): "edx"); //这里告诉编译器,edx改动了,其实这个明显是废话,因为赋值的时候就用到了edx作为输出的。所以这里是编译不过去的printf("result is %d\n", result);return 0;}
正确用法
int main(){int data1 = 10;int result = 20;asm ("movl %1, %%eax\n\t""addl %%eax, %0": "=r"(result): "r"(data1) ,"0"(result): "eax"); //eax不是作为输入输出的,是在汇编代码中改动的,所以要写出来,一定要写出来。printf("result is %d\n", result);return 0;}
不用寄存器,直接引用内存位置
int main() {int dividend = 20;int divisor = 5;int result;asm("divb %2\n\t""movl %%eax, %0": "=m"(result): "a"(dividend), "m"(divisor));printf("result is %d\n", result);return 0;}
格式:#define NAME(input value, output value) (function)#include <stdio.h>#define SUM(a,b,result) \((result) = (a) + (b))int main() {int data1 = 5, data2 = 10;int result ;SUM(data1, data2, result);printf("%d\n", result);float fdata1 = 5.0, fdata2 = 10.0;float fresult;SUM(fdata1, fdata2,fresult);printf("%f\n", fresult);return 0;}
#define GREATER(a, b, result)({ \asm("cmp %1, %2\n\t"\"jge 0f\n\t"\"movl %1, %0\n\t" \"jmp 1f\n\t"\"0:\n\t" \"movl %2, %0\n\t"\"1:"\:"=r"(result)\:"r"(a), "r"(b));})int main() {int data1 = 20, data2 = 10;int result;GREATER(data1, data2, result);printf("a = %d, b = %d result: %d\n", data1, data2, result);data1 = 30;GREATER(data1, data2, result);printf("a = %d, b = %d result: %d\n", data1, data2, result);return 0;}