Error[8]: Undefined offset: 148, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

long scale(long x,long y,long z){
    long t = x + 4 *y + 12 *z;
    return t;
}
// 汇编代码 x rdi ; y rsi; z rdx
scale:
	leaq	(%rdi,%rsi,4), %rax ; rax=x + 4*y
	leaq	(%rdx,%rdx,2), %rdx ; rdx=3*z
	leaq	(%rax,%rdx,4), %rax ; rax=x+4*y + 4*(3*z)
// mul.c
// cc mul.c -O2 -S
#include 
typedef unsigned __int128 uint128_t;
typedef  __int128 int128_t;
void store_uprod(uint128_t *dest,uint64_t x,uint64_t y){
        *dest = x * (uint128_t) y;
}
void store_uprod2(int128_t *dest,int64_t x,int64_t y){
        *dest = x *  (int128_t)y;
}

store_uprod:
	movq	%rsi, %rax
	mulq	%rdx
	movq	%rax, (%rdi)  ;存储低8字节在目的地 小端序机器
	movq	%rdx, 8(%rdi) ;存储高8字节在目的地+8
	ret
store_uprod2:
	movq	%rsi, %rax
	imulq	%rdx
	movq	%rax, (%rdi)
	movq	%rdx, 8(%rdi)
	ret
void remdiv(long x,long y,long *qp,long *rp){
	long q=x/y;
	long r=x%y;
	*qp = q;
	*rp = r;
}
void remdiv2(unsigned long x,unsigned long y,unsigned long *qp,unsigned long *rp){
	long q=x/y;
	long r=x%y;
	*qp = q;
	*rp = r;
}
// x rdi,y rsi,qp rdx,rp rcx

remdiv:
	movq	%rdi, %rax    ; x低8字节存储在%rax
	movq	%rdx, %r8     ; qp地址存储在r8
	cqto                  ; 符号扩展%rdx为全0或全1
	idivq	%rsi          ; 除法 y
	movq	%rax, (%r8)   ; 商存储在*qp
	movq	%rdx, (%rcx)  ; 余数存储在*rp
	ret
	
remdiv2:
	movq	%rdx, %r8
	movq	%rdi, %rax
	xorl	%edx, %edx ; 将%rdx置为0
	divq	%rsi
	movq	%rax, (%r8)
	movq	%rdx, (%rcx)
	ret
控制
int comp(int a,int b){
  return a=0;
}

// a in %rdi
comp:
	testq	%rdi, %rdi 
	setge	%al
	movzbl	%al, %eax
// jmp.c
long lt_cnt = 0;
long ge_cnt = 0;
long absdiff_se(long x,long y){
	long result;
	if (x=y 跳转 .L2
	addq	$1, lt_cnt(%rip)  ; lt_cnt++
	movq	%rsi, %rax        ; rax = y
	subq	%rdi, %rax        ; rax = y-x
	ret
.L2:
	addq	$1, ge_cnt(%rip)  ; ge_cnt++
	movq	%rdi, %rax        ; rax = x
	subq	%rsi, %rax        ; rax = x-y
	ret
	
// cc jmp.c -Og -c
// objdump -d jmp.o
0000000000000000 :
   0:	48 89 f8             	mov    %rdi,%rax
   3:	48 39 f7             	cmp    %rsi,%rdi
   6:	7d 0c                	jge    14  ; 0x14 = 0x08 + 0x0c
   8:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 10 
   f:	01
  10:	48 29 f0             	sub    %rsi,%rax
  13:	c3                   	retq
  14:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 1c 
  1b:	01
  1c:	48 29 f0             	sub    %rsi,%rax
  1f:	c3                   	retq
long absdiff(long a,long b){
  if (a>b)
    return a-b;
  else
    return b-a;
}

// cc absdiff.c -O1 -S
// a in %rdi, b in %rsi
absdiff:
	movq	%rdi, %rdx
	subq	%rsi, %rdx ; rdx = a-b
	movq	%rsi, %rax
	subq	%rdi, %rax ; rax = b-a
	cmpq	%rsi, %rdi ; compare a:b
	cmovg	%rdx, %rax ; if a>b, rax = rdx(a-b)
	ret
循环
long fact_do(long n){
	long result = 1;
	do {
		result *=n;
		n = n-1;
	}while(n>1);
	return result;
}
long fact_do_goto(long n){
	long result = 1;
	loop:
	    result *=n;
		n = n-1;
		if (n>1) goto loop;
	return result;
}
// n in %rdi
fact_do:
	movl	$1, %eax
.L2:
	imulq	%rdi, %rax
	subq	$1, %rdi
	cmpq	$1, %rdi ; compare x:1
	jg	.L2           ; if > , goto .L2
	ret
long fact_do_while(long n){
	long result = 1;
	while(n>1) {
		result *=n;
		n = n-1;
	};
	return result;
}
long fact_do_while_goto(long n){
	long result = 1;
	if(n<=1) goto done;
	loop:
	    result *=n;
		n = n-1;
		if (n>1) goto loop;
	done:
	  return result;
}
fact_do_while:
	movl	$1, %eax
	cmpq	$1, %rdi
	jle	.L4
.L3:
	imulq	%rdi, %rax
	subq	$1, %rdi
	cmpq	$1, %rdi
	jne	.L3
	ret
.L4:
	ret
// 返回0表示有偶数个1,返回1奇数个1
long func_a(unsigned long x){
	long val=0;
	while(x){
		val ^= x;
		x >>= 1;
    }
    return val&1;
}
long func_a_goto(unsigned long x){
	long val=0;
	goto test;
	loop:
		val ^= x;
		x >>= 1;
    test:
      if (x!=0) goto loop;
    return val&1;
}
func_a:
	movl	$0, %eax
	jmp	.L7
.L8:
	xorq	%rdi, %rax
	shrq	%rdi
.L7:
	testq	%rdi, %rdi
	jne	.L8
	andl	$1, %eax
	ret
long fact_for(long n){
	long i;
	long result = 1;
	for(i=2;i<=n;i++){
		result *= i;
	}
	return result;
}
long fact_for_goto(long n){
	long i = 2;
	long result = 1;
	goto test;
	loop:
	  result *= i;
	  i++;
	test:
	  if (i<=n) goto loop;
	return result;
}

// n in %rdi
fact_for:
	movl	, %edx
	movl	, %eax
	jmp	.L10
.L11:
	imulq	%rax, %rdx
	addq	, %rax
.L10:
	cmpq	%rdi, %rax
	jle	.L11
	movq	%rdx, %rax
	ret
过程
void proc(long a1,long *a1p,
		int a2,int *a2p,
		short a3,short *a3p,
		char a4, char *a4p
	 ){
	*a1p += a1;
	*a2p += a2;
	*a3p += a3;
	*a4p += a4;
}

long call_proc(){
	long x1 = 1;
	int x2 = 2;
	short x3 = 3;
	char x4 = 4;
	proc(x1,&x1,x2,&x2,x3,&x3,x4,&x4);
	return (x1+x2)*(x3-x4);
}

// rdi rsi rdx rcx r8 r9
proc:
	movq	16(%rsp), %rax    ; 获取 *a4p
	addq	%rdi, (%rsi)      ; *a1p += a1
	addl	%edx, (%rcx)      ; *a2p += a2
	addw	%r8w, (%r9)       ; *a3p += a3
	movl	8(%rsp), %edx     ; 获取 a4
	addb	%dl, (%rax)       ; *a4p += a4;
	ret
	
call_proc:
	subq	$16, %rsp           ; 分配参数栈空间
	movq	$1, 8(%rsp)         ; 局部变量x1
	movl	$2, 4(%rsp)         ; 局部变量x2
	movw	$3, 2(%rsp)         ; 局部变量x3
	movb	$4, 1(%rsp)         ; 局部变量x4
	leaq	1(%rsp), %rax       ; &x4
	pushq	%rax                ; 参数 &x4
	pushq	$4                  ; 参数 x4
	leaq	18(%rsp), %r9       ; 参数 &x3
	movl	$3, %r8d            ; 参数 x3
	leaq	20(%rsp), %rcx      ; 参数 &x2
	movl	$2, %edx            ; 参数 x2
	leaq	24(%rsp), %rsi      ; 参数 &x1
	movl	$1, %edi            ; 参数 x1
	call	proc
	movslq	20(%rsp), %rax      ; x2 int->long
	addq	24(%rsp), %rax      ; rax=x1+x2
	movswl	18(%rsp), %edx      ; x3 short->int
	movsbl	17(%rsp), %ecx      ; x4 char->int
	subl	%ecx, %edx          ; edx=x3-x4
	movslq	%edx, %rdx          ; edx int->long
	imulq	%rdx, %rax          ; rax = (x1+x2)*(x3-x4)
	addq	$32, %rsp           ; 释放栈空间
	ret
rfact:
	cmpq	, %rdi
	jg	.L8
	movl	, %eax
	ret
.L8:
	pushq	%rbx ; save rbx
	movq	%rdi, %rbx ; store n
	leaq	-1(%rdi), %rdi ; n=n-1
	call	rfact
	imulq	%rbx, %rax ; result = n*rfact(n-1)
	popq	%rbx ; restore rbx
	ret
数组
#define N 16
typedef int fix_matrix[N][N];
int fix_prod_ele(fix_matrix A,fix_matrix B,long i,long k){
	long j;
	int result =0 ;

	for(j=0;i变长数组:对于int A[m][n],A[i][j]地址为i * n+j,必须用乘法指令对i伸缩n倍(i * j),而不能使用一系列的移位和加法, %r8d            ; result = 0
	movl	结构struct的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址, %eax            ; j = 0
	jmp	.L3
.L4:
	movq	%rdx, %rdi          ; rdi = i
	salq	, %rdi            ; rdi = 64*i
	addq	%r9, %rdi           ; rdi = A+64*i
	movq	%rax, %rsi          ; rsi = j
	salq	, %rsi            ; rsi = j*64
	addq	%r10, %rsi          ; rsi = B+j*64
	movl	(%rsi,%rcx,4), %esi ; M[B+j*64+4*k]【B[j][k]】
	addl	(%rdi,%rax,4), %esi ; esi = M[A+64*i+4*j]+M[B+j*64+4*k]
	addl	%esi, %r8d          ; r8d += esi
	addq	, %rax            ; rax ++
.L3:
	cmpq	, %rdx           ; compare i:n
	jle	.L4                     ; if <= ,goto .L4
	movl	%r8d, %eax          ; return result
	ret
异质的数据结构 GDB调试器
[root@localhost csapp]# ./a.out
123456
123456
[root@localhost csapp]# ./a.out
123456789012345
123456789012345
[root@localhost csapp]# ./a.out
1234567890123456
1234567890123456
Segmentation fault (core dumped)

// echo.c
#include 

void echo(){
	char buf[8];
	gets(buf);
	puts(buf);
}
int main(){

	echo();
	return 0;
}

// cc echo.c
// objdump -d a.out
...
0000000000401136 :
  401136:	55                   	push   %rbp
  401137:	48 89 e5             	mov    %rsp,%rbp
  40113a:	48 83 ec 10          	sub    $0x10,%rsp
  40113e:	48 8d 45 f8          	lea    -0x8(%rbp),%rax ; buf
  401142:	48 89 c7             	mov    %rax,%rdi
  401145:	b8 00 00 00 00       	mov    $0x0,%eax
  40114a:	e8 f1 fe ff ff       	callq  401040 
  40114f:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
  401153:	48 89 c7             	mov    %rax,%rdi
  401156:	e8 d5 fe ff ff       	callq  401030 
  40115b:	90                   	nop
  40115c:	c9                   	leaveq
  40115d:	c3                   	retq
...
// 栈破坏检测
// gcc echo.c -fstack-protector
[root@localhost csapp]# ./a.out
123456789
123456789
*** stack smashing detected ***: terminated
Aborted (core dumped)

0000000000401146 :
  401146:	55                   	push   %rbp
  401147:	48 89 e5             	mov    %rsp,%rbp
  40114a:	48 83 ec 10          	sub    $0x10,%rsp
  40114e:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax ; 栈保护
  401155:	00 00
  401157:	48 89 45 f8          	mov    %rax,-0x8(%rbp) ; 金丝雀canary值
  40115b:	31 c0                	xor    %eax,%eax
  40115d:	48 8d 45 f0          	lea    -0x10(%rbp),%rax ; buf
  401161:	48 89 c7             	mov    %rax,%rdi
  401164:	b8 00 00 00 00       	mov    $0x0,%eax
  401169:	e8 e2 fe ff ff       	callq  401050 
  40116e:	48 8d 45 f0          	lea    -0x10(%rbp),%rax
  401172:	48 89 c7             	mov    %rax,%rdi
  401175:	e8 b6 fe ff ff       	callq  401030 
  40117a:	90                   	nop
  40117b:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  40117f:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax ; 查看是否栈溢出
  401186:	00 00
  401188:	74 05                	je     40118f 
  40118a:	e8 b1 fe ff ff       	callq  401040 <__stack_chk_fail@plt>
  40118f:	c9                   	leaveq
  401190:	c3                   	retq
  • SSE XMM寄存器(128位):%xmm0~%xmm15
  • 浮点代码
    • 媒体寄存器
      • 传送单精度:vmovss M32,X
      • 传送单精度:vmovss X,M32
    • 浮点传送指令:不做任何转换的传送指令。X:XMM寄存器
      • 传送双精度:vmovsd M64,X
      • 传送双精度:vmovsd X,M64
      • 传送对其的封装好的单精度:vmovaps X,X
      • 传送对其的封装好的双精度:vmovapd X,X
      • float float_mov(float v1,float *src,float *dst){ float v2 = *src; *dst = v1; return v2; } // v1 in %xmm0, src in %rdi, dst in %rsi float_mov: movaps %xmm0, %xmm1 ; copy v1 movss (%rdi), %xmm0 ; Read v2 from src movss %xmm1, (%rsi) ; Write v1 to dst ret ; return v2 in %xmm0
      • 对于三个 *** 作数的指令,第一个 *** 作数读自内存或一个通用寄存器,第二个 *** 作数数只会影响结果的高位字节,一般忽略,第三个 *** 作数为目的寄存器
  • XMM寄存器%xmm0~%xmm7最多可以传递8个浮点参数。多于的使用栈
    • 浮点数和整数转换即不同浮点格式之间进行转换
      • vcvttss2si X/M32,R32:单精度->整数
      • vcvttsd2si X/M64,R32:双精度->整数
      • vcvttss2siq X/M32,R64:单精度->四字整数
      • vcvttsd2siq X/M64,R64:双精度->四字整数
      • vcvtsi2ss M32/R32,X,X:整数->单精度
      • vcvtsi2sd M32/R32,X,X:四字整数->双精度
      • vcvtsi2ssq M64/R64,X,X:整数->单精度
      • vcvtsi2sdq M64/R64,X,X:四字整数->双精度
      • 函数使用%xmm0来返回浮点数
    • 过程中的浮点代码
        所有的XMM寄存器都是调用者保存。被调用者可以不用保存就覆盖这些寄存器中的任何一个。
      • 当函数包含指针、整数、浮点数混合的参数时,指针和整数通过通用寄存器传递
      • vaddss vaddsd:浮点加 单精度 双精度
      • vsubss vsubsd:浮点减
    • 浮点运算 *** 作
      • vmulss vmulsd:乘
      • vdivss vdivsd:除
      • vmaxss vmaxsd:最大
      • vminss vminsd:最小
      • sqrtss sqrtsd:平方根
      • 定义和使用浮点常数:AVX浮点数不能以立即数作为 *** 作数。编译器必须为所有的常量分配和初始化存储空间,然后代码再把这些值从内存读入。
      • double cel2fahr(double temp){ return 1.8*temp+32.0; } cel2fahr: mulsd .LC0(%rip), %xmm0 addsd .LC1(%rip), %xmm0 ret .LC0: .long -858993459 ; 1.8的低4位 .long 1073532108 ; 1.8的高四位 .LC1: .long 0 .long 1077936128
    double funct(double a,float x,double b,int i){
    	return a*x-b/i;
    }
    
    funct:
    	cvtss2sd	%xmm1, %xmm1  ; x -> double
    	mulsd	%xmm0, %xmm1      ; a*x
    	pxor	%xmm0, %xmm0      ; 0
    	cvtsi2sdl	%edi, %xmm0   ; int -> double
    	divsd	%xmm0, %xmm2      ; b/i
    	subsd	%xmm2, %xmm1      ; a*x - b/i
    	movapd	%xmm1, %xmm0 
    	ret
    
    • vxorps xorpd:异或 单精度 双精度
  • vandps andpd
    • 位级 *** 作:
      • vucomiss:单精度比较
      • vucomisd:双精度比较
    • 浮点比较
        [+++][+++]
    )
    File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
    File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
    File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
    Error[8]: Undefined offset: 149, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
    File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

    • 程序调试:gcc -Og -o p p1.c p2.c
      • 反汇编 objdump -d p
    • 寄存器
      • 函数参数第1到第6个参数:%rdi %rsi %rdx %rcx %r8 %r9
      • 返回值 %rax
      • 栈指针 %rsp
      • 被调用者保存 %rbx %rbp %r12 %r13 %r14 %r15
      • 调用者保存:所有其他寄存器,除了栈指针%rsp,都分类为调用者保存寄存器
    • *** 作数格式
      • $lmm 立即数寻址
      • %rax 寄存器寻址
      • lmm 内存绝对寻址 (lmm)
      • (%rax) 间接寻址
      • lmm(%rax) 基址+偏移量寻址 (%rax+lmm)
      • (%rax,%rsi) 变址寻址 (%rax+%rsi)
      • (,%rsi,s) 比例变址寻址 (s*%rsi)
      • (%rax,%rsi,s) 比例变址寻址 (%rax+s*%rsi)
    • 数据传输指令:mov,字节b,字w,双字l,四字q
      • 将一个值从一个内存位置复制到另一个内存位置需要两条指令——第一条指令将源值加载到寄存器中,第二条将该寄存器值写入目的地址。
    • 间接引用指针就是将指针放在一个寄存器中,然后在内存引用中使用这个寄存器。
      • 压栈 pushq:将栈指针减8,然后将值写到新的栈顶地址
      • 出栈 popq:从栈顶读出数据,然后将栈指针加8
    • 算法指令
      • inc 加1
      • dec 减1
      • neg 取负
      • not 取补
      • add sub imul div 加减乘除
      • xor 异或
      • or 或
      • and 与
    • 加载有效地址(leaq):从内存读取数据到寄存器,但是不引用内存。它的第一个 *** 作数看上去是内存引用,但是该指令并不是从指定的位置读入数据,而是将有效地址写入到目的 *** 作数
    long scale(long x,long y,long z){
        long t = x + 4 *y + 12 *z;
        return t;
    }
    // 汇编代码 x rdi ; y rsi; z rdx
    scale:
    	leaq	(%rdi,%rsi,4), %rax ; rax=x + 4*y
    	leaq	(%rdx,%rdx,2), %rdx ; rdx=3*z
    	leaq	(%rax,%rdx,4), %rax ; rax=x+4*y + 4*(3*z)
    
    • 移位 *** 作:sal(shl) 左移;sar 算术右移(填符号位),shr 逻辑右移(填0)
      • 移位量可以是一个立即数,也可以放在单字节寄存器%cl中(只允许这个%cl寄存器)
      • 移位 *** 作对w位长的数据值进行 *** 作,移位量由%cl寄存器的低m位决定,这里2m=w。当%cl=0xFF,对于salb,23=8,所以会左移7位,salw会移15位,sall会移31位,而salq会移63位
    • 乘法:
      • imulq %rdx, %rsi;有符号乘法生成64位,高位溢出丢掉
      • 全乘法:要求一个参数必须在寄存器%rax中,而另一个作为指令的源 *** 作数给出。然后乘积存放在寄存器%rdx(高64位)和%rax(低64位中)。
        • 无符号全乘法:mulq
        • 有符号全乘法:imulq
    // mul.c
    // cc mul.c -O2 -S
    #include 
    typedef unsigned __int128 uint128_t;
    typedef  __int128 int128_t;
    void store_uprod(uint128_t *dest,uint64_t x,uint64_t y){
            *dest = x * (uint128_t) y;
    }
    void store_uprod2(int128_t *dest,int64_t x,int64_t y){
            *dest = x *  (int128_t)y;
    }
    
    store_uprod:
    	movq	%rsi, %rax
    	mulq	%rdx
    	movq	%rax, (%rdi)  ;存储低8字节在目的地 小端序机器
    	movq	%rdx, 8(%rdi) ;存储高8字节在目的地+8
    	ret
    store_uprod2:
    	movq	%rsi, %rax
    	imulq	%rdx
    	movq	%rax, (%rdi)
    	movq	%rdx, 8(%rdi)
    	ret
    
    • 除法:
      • 有符号除法指令idivq将寄存器%rdx(高64位)和%rax(低64位)中的128位作为被除数,而除数作为指令的 *** 作数给出,指令将商存储在寄存器%rax,将余数存储在寄存器%rdx中。
      • 无符号除法指令idivq将寄存器%rdx(高64位,置为0)和%rax(低64位)中的128位作为被除数,而除数作为指令的 *** 作数给出,指令将商存储在寄存器%rax,将余数存储在寄存器%rdx中。
    void remdiv(long x,long y,long *qp,long *rp){
    	long q=x/y;
    	long r=x%y;
    	*qp = q;
    	*rp = r;
    }
    void remdiv2(unsigned long x,unsigned long y,unsigned long *qp,unsigned long *rp){
    	long q=x/y;
    	long r=x%y;
    	*qp = q;
    	*rp = r;
    }
    // x rdi,y rsi,qp rdx,rp rcx
    
    remdiv:
    	movq	%rdi, %rax    ; x低8字节存储在%rax
    	movq	%rdx, %r8     ; qp地址存储在r8
    	cqto                  ; 符号扩展%rdx为全0或全1
    	idivq	%rsi          ; 除法 y
    	movq	%rax, (%r8)   ; 商存储在*qp
    	movq	%rdx, (%rcx)  ; 余数存储在*rp
    	ret
    	
    remdiv2:
    	movq	%rdx, %r8
    	movq	%rdi, %rax
    	xorl	%edx, %edx ; 将%rdx置为0
    	divq	%rsi
    	movq	%rax, (%r8)
    	movq	%rdx, (%rcx)
    	ret
    
    控制
    • 条件码
      • CF:进位标志。最近的 *** 作使最高位产生了进位。用来检查无符号 *** 作的溢出
      • ZF:零标志。最近的 *** 作得出的结果为0
      • SF:符号标志。最近的 *** 作得到的结果为负数。
      • OF:溢出标志。最近的 *** 作导致一个补码溢出——正溢出或负溢出
    • CMP指令根据两个 *** 作数之差来设置条件码,不会修改目的 *** 作数。CMP S1,S2 ;S2-S1
    • TEST指令根据两个 *** 作数AND来设置条件码,不会修改目的 *** 作数。TEST S1,S2 ; S1&S2
      • testq %rax,%rax 用来检查%rax是负数、零,还是正数
    • 根据条件码的某种组合,将一个字节设置为0或者1.
      • sete 相等或零设置为1
      • setne 不相等或非零设置为1
      • sets 负数设置为0 setns
      • setg setge setl setle:有符号大于 大于等于 小于 小于等于
      • seta setae setb setbe:超过(无符号>) 超过或等于 低于 低于或等于
    int comp(int a,int b){
      return a=0;
    }
    
    // a in %rdi
    comp:
    	testq	%rdi, %rdi 
    	setge	%al
    	movzbl	%al, %eax
    
    • 跳转指令:jmp Label 使用控制的条件转移。但条件满足的时,程序沿着一条执行路径执行,而当条件不满足时,就走另一条路径。
      • 无条件跳转:jmp
        • 直接跳转:跳转目标是作为指令的一部分编码的。jmp Label
        • 间接跳转:跳转目标是从寄存器或内存位置中读出的。jmp *Operand
      • 有条件跳转:只能是直接跳转
        • 相等与不相等:je(jz) jne(jnz)
        • 负数与非负数:js jns
        • 有符号比较:jg(jnle) jge(jnl) jl(jnge) jle(jng)
        • 无符号比较:ja(jnbe) jae(jnb) jb(jnae) jbe(jna)
      • rep(repz):空 *** 作,可以忽略。因为在AMD中,当ret指令通过跳转指令到达时,处理器不能正确的处理它,所以加入rep空 *** 作。
      • jmp 相对寻址,以当前jmp指令的下一条指令地址+jmp后的数字,如下objdump -d中有详细展示
    // jmp.c
    long lt_cnt = 0;
    long ge_cnt = 0;
    long absdiff_se(long x,long y){
    	long result;
    	if (x=y 跳转 .L2
    	addq	$1, lt_cnt(%rip)  ; lt_cnt++
    	movq	%rsi, %rax        ; rax = y
    	subq	%rdi, %rax        ; rax = y-x
    	ret
    .L2:
    	addq	$1, ge_cnt(%rip)  ; ge_cnt++
    	movq	%rdi, %rax        ; rax = x
    	subq	%rsi, %rax        ; rax = x-y
    	ret
    	
    // cc jmp.c -Og -c
    // objdump -d jmp.o
    0000000000000000 :
       0:	48 89 f8             	mov    %rdi,%rax
       3:	48 39 f7             	cmp    %rsi,%rdi
       6:	7d 0c                	jge    14  ; 0x14 = 0x08 + 0x0c
       8:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 10 
       f:	01
      10:	48 29 f0             	sub    %rsi,%rax
      13:	c3                   	retq
      14:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 1c 
      1b:	01
      1c:	48 29 f0             	sub    %rsi,%rax
      1f:	c3                   	retq
    
    • 用条件传送来实现条件分支。 这种方法计算一个条件 *** 作的两种结果,然后再根据条件是否满足从中选取一个。
      • 指令 cmov S,D
        • 相等(或零)与不相等(或非零):comve(comvz) comvne(comvnz)
        • 负数与非负数:comvs comvns
        • 有符号比较:comvg(comvnle) comvge(comvnl) comvl(comvnge) comvle(comvng)
        • 无符号比较:comva(comvnbe) comvae(comvnb) comvb(comvnae) comvbe(comvna)
      • 由于当前的CPU通过使用流水线来获得高性能。当机器遇到条件跳转时,只有当分支条件求值完成之后,才能决定分支往哪边走。处理器采用非常精密的分支预测逻辑来猜测每条跳转指令是否会执行。只要它猜测的比较可靠,指令流水线中就会充满指令。另一方面,错误预测一个跳转,要求处理器丢掉它为该跳转指令后所有指令已做的工作,然后开始用从正确位置处起始的指令去填充流水线。这样一个错误预判会招致很严重的惩罚,浪费15~30个时钟周期,导致程序性能严重下降。
      • 而条件传送,先各分支求值,没有跳转,性能稳定。但是当分支计算复杂时,程序性能可能会下降。
    long absdiff(long a,long b){
      if (a>b)
        return a-b;
      else
        return b-a;
    }
    
    // cc absdiff.c -O1 -S
    // a in %rdi, b in %rsi
    absdiff:
    	movq	%rdi, %rdx
    	subq	%rsi, %rdx ; rdx = a-b
    	movq	%rsi, %rax
    	subq	%rdi, %rax ; rax = b-a
    	cmpq	%rsi, %rdi ; compare a:b
    	cmovg	%rdx, %rax ; if a>b, rax = rdx(a-b)
    	ret
    
    循环
    • do-while
    long fact_do(long n){
    	long result = 1;
    	do {
    		result *=n;
    		n = n-1;
    	}while(n>1);
    	return result;
    }
    long fact_do_goto(long n){
    	long result = 1;
    	loop:
    	    result *=n;
    		n = n-1;
    		if (n>1) goto loop;
    	return result;
    }
    // n in %rdi
    fact_do:
    	movl	$1, %eax
    .L2:
    	imulq	%rdi, %rax
    	subq	$1, %rdi
    	cmpq	$1, %rdi ; compare x:1
    	jg	.L2           ; if > , goto .L2
    	ret
    
    • while
    long fact_do_while(long n){
    	long result = 1;
    	while(n>1) {
    		result *=n;
    		n = n-1;
    	};
    	return result;
    }
    long fact_do_while_goto(long n){
    	long result = 1;
    	if(n<=1) goto done;
    	loop:
    	    result *=n;
    		n = n-1;
    		if (n>1) goto loop;
    	done:
    	  return result;
    }
    fact_do_while:
    	movl	$1, %eax
    	cmpq	$1, %rdi
    	jle	.L4
    .L3:
    	imulq	%rdi, %rax
    	subq	$1, %rdi
    	cmpq	$1, %rdi
    	jne	.L3
    	ret
    .L4:
    	ret
    
    • 判断二进制数字中1的奇偶个数
    // 返回0表示有偶数个1,返回1奇数个1
    long func_a(unsigned long x){
    	long val=0;
    	while(x){
    		val ^= x;
    		x >>= 1;
        }
        return val&1;
    }
    long func_a_goto(unsigned long x){
    	long val=0;
    	goto test;
    	loop:
    		val ^= x;
    		x >>= 1;
        test:
          if (x!=0) goto loop;
        return val&1;
    }
    func_a:
    	movl	$0, %eax
    	jmp	.L7
    .L8:
    	xorq	%rdi, %rax
    	shrq	%rdi
    .L7:
    	testq	%rdi, %rdi
    	jne	.L8
    	andl	$1, %eax
    	ret
    
    • for
    long fact_for(long n){
    	long i;
    	long result = 1;
    	for(i=2;i<=n;i++){
    		result *= i;
    	}
    	return result;
    }
    long fact_for_goto(long n){
    	long i = 2;
    	long result = 1;
    	goto test;
    	loop:
    	  result *= i;
    	  i++;
    	test:
    	  if (i<=n) goto loop;
    	return result;
    }
    
    // n in %rdi
    fact_for:
    	movl	, %edx
    	movl	, %eax
    	jmp	.L10
    .L11:
    	imulq	%rax, %rdx
    	addq	, %rax
    .L10:
    	cmpq	%rdi, %rax
    	jle	.L11
    	movq	%rdx, %rax
    	ret
    
    过程
    • P调用Q,共同机制
      • 传递控制:在进入过程Q的时候,程序计数器必须被设置为Q的代码的起始地址,然后在返回时,要把程序计数器设置为P中调用Q后面那条指令的地址
      • 传递数据:P必须能够向Q提供一个或多个参数,Q必须能够向P返回一个值
      • 分配和释放内存:在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些存储空间
    • 当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间。这个部分称为过程的栈帧(stack frame)
      • 参数大于6个的部分参数放在栈中
      • 栈上的局部存储
        • 寄存器不足够存放所有的本地数据
        • 对一个局部变量使用地址运算符’&',因此必须能够为它产生一个地址。(leaq 18(%rsp), %r9 ; 参数 &x3)
        • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用被访问到。
    void proc(long a1,long *a1p,
    		int a2,int *a2p,
    		short a3,short *a3p,
    		char a4, char *a4p
    	 ){
    	*a1p += a1;
    	*a2p += a2;
    	*a3p += a3;
    	*a4p += a4;
    }
    
    long call_proc(){
    	long x1 = 1;
    	int x2 = 2;
    	short x3 = 3;
    	char x4 = 4;
    	proc(x1,&x1,x2,&x2,x3,&x3,x4,&x4);
    	return (x1+x2)*(x3-x4);
    }
    
    // rdi rsi rdx rcx r8 r9
    proc:
    	movq	16(%rsp), %rax    ; 获取 *a4p
    	addq	%rdi, (%rsi)      ; *a1p += a1
    	addl	%edx, (%rcx)      ; *a2p += a2
    	addw	%r8w, (%r9)       ; *a3p += a3
    	movl	8(%rsp), %edx     ; 获取 a4
    	addb	%dl, (%rax)       ; *a4p += a4;
    	ret
    	
    call_proc:
    	subq	$16, %rsp           ; 分配参数栈空间
    	movq	$1, 8(%rsp)         ; 局部变量x1
    	movl	$2, 4(%rsp)         ; 局部变量x2
    	movw	$3, 2(%rsp)         ; 局部变量x3
    	movb	$4, 1(%rsp)         ; 局部变量x4
    	leaq	1(%rsp), %rax       ; &x4
    	pushq	%rax                ; 参数 &x4
    	pushq	$4                  ; 参数 x4
    	leaq	18(%rsp), %r9       ; 参数 &x3
    	movl	$3, %r8d            ; 参数 x3
    	leaq	20(%rsp), %rcx      ; 参数 &x2
    	movl	$2, %edx            ; 参数 x2
    	leaq	24(%rsp), %rsi      ; 参数 &x1
    	movl	$1, %edi            ; 参数 x1
    	call	proc
    	movslq	20(%rsp), %rax      ; x2 int->long
    	addq	24(%rsp), %rax      ; rax=x1+x2
    	movswl	18(%rsp), %edx      ; x3 short->int
    	movsbl	17(%rsp), %ecx      ; x4 char->int
    	subl	%ecx, %edx          ; edx=x3-x4
    	movslq	%edx, %rdx          ; edx int->long
    	imulq	%rdx, %rax          ; rax = (x1+x2)*(x3-x4)
    	addq	$32, %rsp           ; 释放栈空间
    	ret
    
    • 寄存器中的局部存储空间:当寄存器不足于保存临时变量时,使用栈保存。
      • 根据惯例,寄存器%rbx、%rbp和%r12~%r15被划分为被调用者保存寄存器。当过程P调用过程Q时,Q必须保存这些寄存器的值,保证它们的值在Q返回到P时与Q被调用时是一样的。
      • 所有其他寄存器,除了栈指针%rsp,都分类为调用者保存寄存器。这就意味着任何函数都可以修改它们。也就是说,过程P在某个此类寄存器中有局部数据,然后调用过程Q,因为Q可以随意修改这个寄存器,所以在调用之前首先保存好这个数据时P(调用者)的责任。
    • 递归
    rfact:
    	cmpq	, %rdi
    	jg	.L8
    	movl	, %eax
    	ret
    .L8:
    	pushq	%rbx ; save rbx
    	movq	%rdi, %rbx ; store n
    	leaq	-1(%rdi), %rdi ; n=n-1
    	call	rfact
    	imulq	%rbx, %rax ; result = n*rfact(n-1)
    	popq	%rbx ; restore rbx
    	ret
    
    数组
    • 嵌套数组:行主序,对于int P[M][N]类型的数组,P[i][j] 地址为 P+(i*N+j)*4
    #define N 16
    typedef int fix_matrix[N][N];
    int fix_prod_ele(fix_matrix A,fix_matrix B,long i,long k){
    	long j;
    	int result =0 ;
    
    	for(j=0;i变长数组:对于int A[m][n],A[i][j]地址为i * n+j,必须用乘法指令对i伸缩n倍(i * j),而不能使用一系列的移位和加法, %r8d            ; result = 0
    	movl	结构struct的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址, %eax            ; j = 0
    	jmp	.L3
    .L4:
    	movq	%rdx, %rdi          ; rdi = i
    	salq	, %rdi            ; rdi = 64*i
    	addq	%r9, %rdi           ; rdi = A+64*i
    	movq	%rax, %rsi          ; rsi = j
    	salq	, %rsi            ; rsi = j*64
    	addq	%r10, %rsi          ; rsi = B+j*64
    	movl	(%rsi,%rcx,4), %esi ; M[B+j*64+4*k]【B[j][k]】
    	addl	(%rdi,%rax,4), %esi ; esi = M[A+64*i+4*j]+M[B+j*64+4*k]
    	addl	%esi, %r8d          ; r8d += esi
    	addq	, %rax            ; rax ++
    .L3:
    	cmpq	, %rdx           ; compare i:n
    	jle	.L4                     ; if <= ,goto .L4
    	movl	%r8d, %eax          ; return result
    	ret
    
    • 联合
    异质的数据结构
    • 数据对齐,通常为2、4、8
    • quit 退出GDB
    • run 运行程序(在此给出命令行参数)
    GDB调试器
    • 使用
      • 开始和停止
        • kill 停止程序
        • break multscore:在函数multscore入口处设置断点
        • break * 0x400540:在地址0x400540处设置断点
      • 短点
        • delete 1:删除断点1
        • delete:删除所有断点
        • stepi:执行一条指令
        • stepi 4:执行4条指令
      • 执行
        • nexti:类似于stepi,但以函数调用为单位
        • continue:继续执行
        • finish:运行到当前函数返回
        • disas:反汇编当前函数
        • disas multscore:反汇编函数multscore
      • 检查代码
        • disas 0x400544:反汇编位于地址0x400544附近的函数
        • disas 0x400540,0x40054d 反汇编指定地址范围内的代码
        • print /x $rip:以16进制输出程序计数器的值
        • print $rax:以十进制输出%rax的内容
        • print /t $rax:以二进制输出
      • 检查数据
        • print 0x100:以十进制输出0x100
        • print /x ($rsp+8):以16进制输出%rsp的内容加上8
        • print *(long *)($rsp+8):输出位于地址%rsp+8处的长整数
        • x/2g 0x7fffffffe818:检查从地址0x7fffffffe818开始的双(8字节)字
        • x/20b multscore:检查函数multscore的前20个字节
        • info frame:有关当前栈帧的信息
        • info registers:所有寄存器的值
      • 有用的信息
        • help
        • 缓冲区溢出:8字节buf,上面是%rbp值,所以当输入小于等于15个字符时,程序不会崩溃
        • 可以在攻击代码前加入一段nop指令,运用蛮力攻击,只要地址落在这一段代码上即可实现攻击。
    • nop指令(no operation):是程序计数器加一,没有其他效果
    [root@localhost csapp]# ./a.out
    123456
    123456
    [root@localhost csapp]# ./a.out
    123456789012345
    123456789012345
    [root@localhost csapp]# ./a.out
    1234567890123456
    1234567890123456
    Segmentation fault (core dumped)
    
    // echo.c
    #include 
    
    void echo(){
    	char buf[8];
    	gets(buf);
    	puts(buf);
    }
    int main(){
    
    	echo();
    	return 0;
    }
    
    // cc echo.c
    // objdump -d a.out
    ...
    0000000000401136 :
      401136:	55                   	push   %rbp
      401137:	48 89 e5             	mov    %rsp,%rbp
      40113a:	48 83 ec 10          	sub    $0x10,%rsp
      40113e:	48 8d 45 f8          	lea    -0x8(%rbp),%rax ; buf
      401142:	48 89 c7             	mov    %rax,%rdi
      401145:	b8 00 00 00 00       	mov    $0x0,%eax
      40114a:	e8 f1 fe ff ff       	callq  401040 
      40114f:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
      401153:	48 89 c7             	mov    %rax,%rdi
      401156:	e8 d5 fe ff ff       	callq  401030 
      40115b:	90                   	nop
      40115c:	c9                   	leaveq
      40115d:	c3                   	retq
    ...
    
    • 对抗缓冲区溢出
      • 栈随机化:使栈的位置在程序每次运行都有变化。为了在系统中插入攻击代码,攻击者既要插入代码,也要插入指向这段代码的指针,产生这个指针需要知道这个字符串放置的栈地址,栈随机化避免了此类攻击。
        • 栈破坏检测:程序每次随机产生一个值,当出现缓冲区溢出时,修改了哨兵值,函数运行完会检测此值是否改变。
        • 限制可执行代码区域:限制栈只能读写,不能代码执行
      • 在较早版本的x86代码中,每个函数调用都使用了栈指针(frame pointer)。而现在,只有栈帧长可变的情况才使用
      • pushq %rbp; movq %rsp,%rbp
    // 栈破坏检测
    // gcc echo.c -fstack-protector
    [root@localhost csapp]# ./a.out
    123456789
    123456789
    *** stack smashing detected ***: terminated
    Aborted (core dumped)
    
    0000000000401146 :
      401146:	55                   	push   %rbp
      401147:	48 89 e5             	mov    %rsp,%rbp
      40114a:	48 83 ec 10          	sub    $0x10,%rsp
      40114e:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax ; 栈保护
      401155:	00 00
      401157:	48 89 45 f8          	mov    %rax,-0x8(%rbp) ; 金丝雀canary值
      40115b:	31 c0                	xor    %eax,%eax
      40115d:	48 8d 45 f0          	lea    -0x10(%rbp),%rax ; buf
      401161:	48 89 c7             	mov    %rax,%rdi
      401164:	b8 00 00 00 00       	mov    $0x0,%eax
      401169:	e8 e2 fe ff ff       	callq  401050 
      40116e:	48 8d 45 f0          	lea    -0x10(%rbp),%rax
      401172:	48 89 c7             	mov    %rax,%rdi
      401175:	e8 b6 fe ff ff       	callq  401030 
      40117a:	90                   	nop
      40117b:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
      40117f:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax ; 查看是否栈溢出
      401186:	00 00
      401188:	74 05                	je     40118f 
      40118a:	e8 b1 fe ff ff       	callq  401040 <__stack_chk_fail@plt>
      40118f:	c9                   	leaveq
      401190:	c3                   	retq
    
    • 支持变长栈帧:long vframe(long n,long idx,long *q){ long i; long *p[n]; p[0]=&i; for(i=1;i
      • AVX浮点体系结构的16个YMM寄存器(256位):%ymm0~%ymm15
  • SSE XMM寄存器(128位):%xmm0~%xmm15
  • 浮点代码
    • 媒体寄存器
      • 传送单精度:vmovss M32,X
      • 传送单精度:vmovss X,M32
    • 浮点传送指令:不做任何转换的传送指令。X:XMM寄存器
      • 传送双精度:vmovsd M64,X
      • 传送双精度:vmovsd X,M64
      • 传送对其的封装好的单精度:vmovaps X,X
      • 传送对其的封装好的双精度:vmovapd X,X
      • float float_mov(float v1,float *src,float *dst){ float v2 = *src; *dst = v1; return v2; } // v1 in %xmm0, src in %rdi, dst in %rsi float_mov: movaps %xmm0, %xmm1 ; copy v1 movss (%rdi), %xmm0 ; Read v2 from src movss %xmm1, (%rsi) ; Write v1 to dst ret ; return v2 in %xmm0
      • 对于三个 *** 作数的指令,第一个 *** 作数读自内存或一个通用寄存器,第二个 *** 作数数只会影响结果的高位字节,一般忽略,第三个 *** 作数为目的寄存器
  • XMM寄存器%xmm0~%xmm7最多可以传递8个浮点参数。多于的使用栈
    • 浮点数和整数转换即不同浮点格式之间进行转换
      • vcvttss2si X/M32,R32:单精度->整数
      • vcvttsd2si X/M64,R32:双精度->整数
      • vcvttss2siq X/M32,R64:单精度->四字整数
      • vcvttsd2siq X/M64,R64:双精度->四字整数
      • vcvtsi2ss M32/R32,X,X:整数->单精度
      • vcvtsi2sd M32/R32,X,X:四字整数->双精度
      • vcvtsi2ssq M64/R64,X,X:整数->单精度
      • vcvtsi2sdq M64/R64,X,X:四字整数->双精度
      • 函数使用%xmm0来返回浮点数
    • 过程中的浮点代码
        所有的XMM寄存器都是调用者保存。被调用者可以不用保存就覆盖这些寄存器中的任何一个。
      • 当函数包含指针、整数、浮点数混合的参数时,指针和整数通过通用寄存器传递
      • vaddss vaddsd:浮点加 单精度 双精度
      • vsubss vsubsd:浮点减
    • 浮点运算 *** 作
      • vmulss vmulsd:乘
      • vdivss vdivsd:除
      • vmaxss vmaxsd:最大
      • vminss vminsd:最小
      • sqrtss sqrtsd:平方根
      • 定义和使用浮点常数:AVX浮点数不能以立即数作为 *** 作数。编译器必须为所有的常量分配和初始化存储空间,然后代码再把这些值从内存读入。
      • double cel2fahr(double temp){ return 1.8*temp+32.0; } cel2fahr: mulsd .LC0(%rip), %xmm0 addsd .LC1(%rip), %xmm0 ret .LC0: .long -858993459 ; 1.8的低4位 .long 1073532108 ; 1.8的高四位 .LC1: .long 0 .long 1077936128
    double funct(double a,float x,double b,int i){
    	return a*x-b/i;
    }
    
    funct:
    	cvtss2sd	%xmm1, %xmm1  ; x -> double
    	mulsd	%xmm0, %xmm1      ; a*x
    	pxor	%xmm0, %xmm0      ; 0
    	cvtsi2sdl	%edi, %xmm0   ; int -> double
    	divsd	%xmm0, %xmm2      ; b/i
    	subsd	%xmm2, %xmm1      ; a*x - b/i
    	movapd	%xmm1, %xmm0 
    	ret
    
    • vxorps xorpd:异或 单精度 双精度
  • vandps andpd
    • 位级 *** 作:
      • vucomiss:单精度比较
      • vucomisd:双精度比较
    • 浮点比较
        [+++]
    )
    File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
    File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
    File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
    深入理解计算机系统_C_内存溢出

    深入理解计算机系统

    深入理解计算机系统,第1张

    • 程序调试:gcc -Og -o p p1.c p2.c
      • 反汇编 objdump -d p
    • 寄存器
      • 函数参数第1到第6个参数:%rdi %rsi %rdx %rcx %r8 %r9
      • 返回值 %rax
      • 栈指针 %rsp
      • 被调用者保存 %rbx %rbp %r12 %r13 %r14 %r15
      • 调用者保存:所有其他寄存器,除了栈指针%rsp,都分类为调用者保存寄存器
    • *** 作数格式
      • $lmm 立即数寻址
      • %rax 寄存器寻址
      • lmm 内存绝对寻址 (lmm)
      • (%rax) 间接寻址
      • lmm(%rax) 基址+偏移量寻址 (%rax+lmm)
      • (%rax,%rsi) 变址寻址 (%rax+%rsi)
      • (,%rsi,s) 比例变址寻址 (s*%rsi)
      • (%rax,%rsi,s) 比例变址寻址 (%rax+s*%rsi)
    • 数据传输指令:mov,字节b,字w,双字l,四字q
      • 将一个值从一个内存位置复制到另一个内存位置需要两条指令——第一条指令将源值加载到寄存器中,第二条将该寄存器值写入目的地址。
    • 间接引用指针就是将指针放在一个寄存器中,然后在内存引用中使用这个寄存器。
      • 压栈 pushq:将栈指针减8,然后将值写到新的栈顶地址
      • 出栈 popq:从栈顶读出数据,然后将栈指针加8
    • 算法指令
      • inc 加1
      • dec 减1
      • neg 取负
      • not 取补
      • add sub imul div 加减乘除
      • xor 异或
      • or 或
      • and 与
    • 加载有效地址(leaq):从内存读取数据到寄存器,但是不引用内存。它的第一个 *** 作数看上去是内存引用,但是该指令并不是从指定的位置读入数据,而是将有效地址写入到目的 *** 作数
    long scale(long x,long y,long z){
        long t = x + 4 *y + 12 *z;
        return t;
    }
    // 汇编代码 x rdi ; y rsi; z rdx
    scale:
    	leaq	(%rdi,%rsi,4), %rax ; rax=x + 4*y
    	leaq	(%rdx,%rdx,2), %rdx ; rdx=3*z
    	leaq	(%rax,%rdx,4), %rax ; rax=x+4*y + 4*(3*z)
    
    • 移位 *** 作:sal(shl) 左移;sar 算术右移(填符号位),shr 逻辑右移(填0)
      • 移位量可以是一个立即数,也可以放在单字节寄存器%cl中(只允许这个%cl寄存器)
      • 移位 *** 作对w位长的数据值进行 *** 作,移位量由%cl寄存器的低m位决定,这里2m=w。当%cl=0xFF,对于salb,23=8,所以会左移7位,salw会移15位,sall会移31位,而salq会移63位
    • 乘法:
      • imulq %rdx, %rsi;有符号乘法生成64位,高位溢出丢掉
      • 全乘法:要求一个参数必须在寄存器%rax中,而另一个作为指令的源 *** 作数给出。然后乘积存放在寄存器%rdx(高64位)和%rax(低64位中)。
        • 无符号全乘法:mulq
        • 有符号全乘法:imulq
    // mul.c
    // cc mul.c -O2 -S
    #include 
    typedef unsigned __int128 uint128_t;
    typedef  __int128 int128_t;
    void store_uprod(uint128_t *dest,uint64_t x,uint64_t y){
            *dest = x * (uint128_t) y;
    }
    void store_uprod2(int128_t *dest,int64_t x,int64_t y){
            *dest = x *  (int128_t)y;
    }
    
    store_uprod:
    	movq	%rsi, %rax
    	mulq	%rdx
    	movq	%rax, (%rdi)  ;存储低8字节在目的地 小端序机器
    	movq	%rdx, 8(%rdi) ;存储高8字节在目的地+8
    	ret
    store_uprod2:
    	movq	%rsi, %rax
    	imulq	%rdx
    	movq	%rax, (%rdi)
    	movq	%rdx, 8(%rdi)
    	ret
    
    • 除法:
      • 有符号除法指令idivq将寄存器%rdx(高64位)和%rax(低64位)中的128位作为被除数,而除数作为指令的 *** 作数给出,指令将商存储在寄存器%rax,将余数存储在寄存器%rdx中。
      • 无符号除法指令idivq将寄存器%rdx(高64位,置为0)和%rax(低64位)中的128位作为被除数,而除数作为指令的 *** 作数给出,指令将商存储在寄存器%rax,将余数存储在寄存器%rdx中。
    void remdiv(long x,long y,long *qp,long *rp){
    	long q=x/y;
    	long r=x%y;
    	*qp = q;
    	*rp = r;
    }
    void remdiv2(unsigned long x,unsigned long y,unsigned long *qp,unsigned long *rp){
    	long q=x/y;
    	long r=x%y;
    	*qp = q;
    	*rp = r;
    }
    // x rdi,y rsi,qp rdx,rp rcx
    
    remdiv:
    	movq	%rdi, %rax    ; x低8字节存储在%rax
    	movq	%rdx, %r8     ; qp地址存储在r8
    	cqto                  ; 符号扩展%rdx为全0或全1
    	idivq	%rsi          ; 除法 y
    	movq	%rax, (%r8)   ; 商存储在*qp
    	movq	%rdx, (%rcx)  ; 余数存储在*rp
    	ret
    	
    remdiv2:
    	movq	%rdx, %r8
    	movq	%rdi, %rax
    	xorl	%edx, %edx ; 将%rdx置为0
    	divq	%rsi
    	movq	%rax, (%r8)
    	movq	%rdx, (%rcx)
    	ret
    
    控制
    • 条件码
      • CF:进位标志。最近的 *** 作使最高位产生了进位。用来检查无符号 *** 作的溢出
      • ZF:零标志。最近的 *** 作得出的结果为0
      • SF:符号标志。最近的 *** 作得到的结果为负数。
      • OF:溢出标志。最近的 *** 作导致一个补码溢出——正溢出或负溢出
    • CMP指令根据两个 *** 作数之差来设置条件码,不会修改目的 *** 作数。CMP S1,S2 ;S2-S1
    • TEST指令根据两个 *** 作数AND来设置条件码,不会修改目的 *** 作数。TEST S1,S2 ; S1&S2
      • testq %rax,%rax 用来检查%rax是负数、零,还是正数
    • 根据条件码的某种组合,将一个字节设置为0或者1.
      • sete 相等或零设置为1
      • setne 不相等或非零设置为1
      • sets 负数设置为0 setns
      • setg setge setl setle:有符号大于 大于等于 小于 小于等于
      • seta setae setb setbe:超过(无符号>) 超过或等于 低于 低于或等于
    int comp(int a,int b){
      return a=0;
    }
    
    // a in %rdi
    comp:
    	testq	%rdi, %rdi 
    	setge	%al
    	movzbl	%al, %eax
    
    • 跳转指令:jmp Label 使用控制的条件转移。但条件满足的时,程序沿着一条执行路径执行,而当条件不满足时,就走另一条路径。
      • 无条件跳转:jmp
        • 直接跳转:跳转目标是作为指令的一部分编码的。jmp Label
        • 间接跳转:跳转目标是从寄存器或内存位置中读出的。jmp *Operand
      • 有条件跳转:只能是直接跳转
        • 相等与不相等:je(jz) jne(jnz)
        • 负数与非负数:js jns
        • 有符号比较:jg(jnle) jge(jnl) jl(jnge) jle(jng)
        • 无符号比较:ja(jnbe) jae(jnb) jb(jnae) jbe(jna)
      • rep(repz):空 *** 作,可以忽略。因为在AMD中,当ret指令通过跳转指令到达时,处理器不能正确的处理它,所以加入rep空 *** 作。
      • jmp 相对寻址,以当前jmp指令的下一条指令地址+jmp后的数字,如下objdump -d中有详细展示
    // jmp.c
    long lt_cnt = 0;
    long ge_cnt = 0;
    long absdiff_se(long x,long y){
    	long result;
    	if (x=y 跳转 .L2
    	addq	$1, lt_cnt(%rip)  ; lt_cnt++
    	movq	%rsi, %rax        ; rax = y
    	subq	%rdi, %rax        ; rax = y-x
    	ret
    .L2:
    	addq	$1, ge_cnt(%rip)  ; ge_cnt++
    	movq	%rdi, %rax        ; rax = x
    	subq	%rsi, %rax        ; rax = x-y
    	ret
    	
    // cc jmp.c -Og -c
    // objdump -d jmp.o
    0000000000000000 :
       0:	48 89 f8             	mov    %rdi,%rax
       3:	48 39 f7             	cmp    %rsi,%rdi
       6:	7d 0c                	jge    14  ; 0x14 = 0x08 + 0x0c
       8:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 10 
       f:	01
      10:	48 29 f0             	sub    %rsi,%rax
      13:	c3                   	retq
      14:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 1c 
      1b:	01
      1c:	48 29 f0             	sub    %rsi,%rax
      1f:	c3                   	retq
    
    • 用条件传送来实现条件分支。 这种方法计算一个条件 *** 作的两种结果,然后再根据条件是否满足从中选取一个。
      • 指令 cmov S,D
        • 相等(或零)与不相等(或非零):comve(comvz) comvne(comvnz)
        • 负数与非负数:comvs comvns
        • 有符号比较:comvg(comvnle) comvge(comvnl) comvl(comvnge) comvle(comvng)
        • 无符号比较:comva(comvnbe) comvae(comvnb) comvb(comvnae) comvbe(comvna)
      • 由于当前的CPU通过使用流水线来获得高性能。当机器遇到条件跳转时,只有当分支条件求值完成之后,才能决定分支往哪边走。处理器采用非常精密的分支预测逻辑来猜测每条跳转指令是否会执行。只要它猜测的比较可靠,指令流水线中就会充满指令。另一方面,错误预测一个跳转,要求处理器丢掉它为该跳转指令后所有指令已做的工作,然后开始用从正确位置处起始的指令去填充流水线。这样一个错误预判会招致很严重的惩罚,浪费15~30个时钟周期,导致程序性能严重下降。
      • 而条件传送,先各分支求值,没有跳转,性能稳定。但是当分支计算复杂时,程序性能可能会下降。
    long absdiff(long a,long b){
      if (a>b)
        return a-b;
      else
        return b-a;
    }
    
    // cc absdiff.c -O1 -S
    // a in %rdi, b in %rsi
    absdiff:
    	movq	%rdi, %rdx
    	subq	%rsi, %rdx ; rdx = a-b
    	movq	%rsi, %rax
    	subq	%rdi, %rax ; rax = b-a
    	cmpq	%rsi, %rdi ; compare a:b
    	cmovg	%rdx, %rax ; if a>b, rax = rdx(a-b)
    	ret
    
    循环
    • do-while
    long fact_do(long n){
    	long result = 1;
    	do {
    		result *=n;
    		n = n-1;
    	}while(n>1);
    	return result;
    }
    long fact_do_goto(long n){
    	long result = 1;
    	loop:
    	    result *=n;
    		n = n-1;
    		if (n>1) goto loop;
    	return result;
    }
    // n in %rdi
    fact_do:
    	movl	$1, %eax
    .L2:
    	imulq	%rdi, %rax
    	subq	$1, %rdi
    	cmpq	$1, %rdi ; compare x:1
    	jg	.L2           ; if > , goto .L2
    	ret
    
    • while
    long fact_do_while(long n){
    	long result = 1;
    	while(n>1) {
    		result *=n;
    		n = n-1;
    	};
    	return result;
    }
    long fact_do_while_goto(long n){
    	long result = 1;
    	if(n<=1) goto done;
    	loop:
    	    result *=n;
    		n = n-1;
    		if (n>1) goto loop;
    	done:
    	  return result;
    }
    fact_do_while:
    	movl	$1, %eax
    	cmpq	$1, %rdi
    	jle	.L4
    .L3:
    	imulq	%rdi, %rax
    	subq	$1, %rdi
    	cmpq	$1, %rdi
    	jne	.L3
    	ret
    .L4:
    	ret
    
    • 判断二进制数字中1的奇偶个数
    // 返回0表示有偶数个1,返回1奇数个1
    long func_a(unsigned long x){
    	long val=0;
    	while(x){
    		val ^= x;
    		x >>= 1;
        }
        return val&1;
    }
    long func_a_goto(unsigned long x){
    	long val=0;
    	goto test;
    	loop:
    		val ^= x;
    		x >>= 1;
        test:
          if (x!=0) goto loop;
        return val&1;
    }
    func_a:
    	movl	$0, %eax
    	jmp	.L7
    .L8:
    	xorq	%rdi, %rax
    	shrq	%rdi
    .L7:
    	testq	%rdi, %rdi
    	jne	.L8
    	andl	$1, %eax
    	ret
    
    • for
    long fact_for(long n){
    	long i;
    	long result = 1;
    	for(i=2;i<=n;i++){
    		result *= i;
    	}
    	return result;
    }
    long fact_for_goto(long n){
    	long i = 2;
    	long result = 1;
    	goto test;
    	loop:
    	  result *= i;
    	  i++;
    	test:
    	  if (i<=n) goto loop;
    	return result;
    }
    
    // n in %rdi
    fact_for:
    	movl	, %edx
    	movl	, %eax
    	jmp	.L10
    .L11:
    	imulq	%rax, %rdx
    	addq	, %rax
    .L10:
    	cmpq	%rdi, %rax
    	jle	.L11
    	movq	%rdx, %rax
    	ret
    
    过程
    • P调用Q,共同机制
      • 传递控制:在进入过程Q的时候,程序计数器必须被设置为Q的代码的起始地址,然后在返回时,要把程序计数器设置为P中调用Q后面那条指令的地址
      • 传递数据:P必须能够向Q提供一个或多个参数,Q必须能够向P返回一个值
      • 分配和释放内存:在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些存储空间
    • 当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间。这个部分称为过程的栈帧(stack frame)
      • 参数大于6个的部分参数放在栈中
      • 栈上的局部存储
        • 寄存器不足够存放所有的本地数据
        • 对一个局部变量使用地址运算符’&',因此必须能够为它产生一个地址。(leaq 18(%rsp), %r9 ; 参数 &x3)
        • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用被访问到。
    void proc(long a1,long *a1p,
    		int a2,int *a2p,
    		short a3,short *a3p,
    		char a4, char *a4p
    	 ){
    	*a1p += a1;
    	*a2p += a2;
    	*a3p += a3;
    	*a4p += a4;
    }
    
    long call_proc(){
    	long x1 = 1;
    	int x2 = 2;
    	short x3 = 3;
    	char x4 = 4;
    	proc(x1,&x1,x2,&x2,x3,&x3,x4,&x4);
    	return (x1+x2)*(x3-x4);
    }
    
    // rdi rsi rdx rcx r8 r9
    proc:
    	movq	16(%rsp), %rax    ; 获取 *a4p
    	addq	%rdi, (%rsi)      ; *a1p += a1
    	addl	%edx, (%rcx)      ; *a2p += a2
    	addw	%r8w, (%r9)       ; *a3p += a3
    	movl	8(%rsp), %edx     ; 获取 a4
    	addb	%dl, (%rax)       ; *a4p += a4;
    	ret
    	
    call_proc:
    	subq	$16, %rsp           ; 分配参数栈空间
    	movq	$1, 8(%rsp)         ; 局部变量x1
    	movl	$2, 4(%rsp)         ; 局部变量x2
    	movw	$3, 2(%rsp)         ; 局部变量x3
    	movb	$4, 1(%rsp)         ; 局部变量x4
    	leaq	1(%rsp), %rax       ; &x4
    	pushq	%rax                ; 参数 &x4
    	pushq	$4                  ; 参数 x4
    	leaq	18(%rsp), %r9       ; 参数 &x3
    	movl	$3, %r8d            ; 参数 x3
    	leaq	20(%rsp), %rcx      ; 参数 &x2
    	movl	$2, %edx            ; 参数 x2
    	leaq	24(%rsp), %rsi      ; 参数 &x1
    	movl	$1, %edi            ; 参数 x1
    	call	proc
    	movslq	20(%rsp), %rax      ; x2 int->long
    	addq	24(%rsp), %rax      ; rax=x1+x2
    	movswl	18(%rsp), %edx      ; x3 short->int
    	movsbl	17(%rsp), %ecx      ; x4 char->int
    	subl	%ecx, %edx          ; edx=x3-x4
    	movslq	%edx, %rdx          ; edx int->long
    	imulq	%rdx, %rax          ; rax = (x1+x2)*(x3-x4)
    	addq	$32, %rsp           ; 释放栈空间
    	ret
    
    • 寄存器中的局部存储空间:当寄存器不足于保存临时变量时,使用栈保存。
      • 根据惯例,寄存器%rbx、%rbp和%r12~%r15被划分为被调用者保存寄存器。当过程P调用过程Q时,Q必须保存这些寄存器的值,保证它们的值在Q返回到P时与Q被调用时是一样的。
      • 所有其他寄存器,除了栈指针%rsp,都分类为调用者保存寄存器。这就意味着任何函数都可以修改它们。也就是说,过程P在某个此类寄存器中有局部数据,然后调用过程Q,因为Q可以随意修改这个寄存器,所以在调用之前首先保存好这个数据时P(调用者)的责任。
    • 递归
    rfact:
    	cmpq	, %rdi
    	jg	.L8
    	movl	, %eax
    	ret
    .L8:
    	pushq	%rbx ; save rbx
    	movq	%rdi, %rbx ; store n
    	leaq	-1(%rdi), %rdi ; n=n-1
    	call	rfact
    	imulq	%rbx, %rax ; result = n*rfact(n-1)
    	popq	%rbx ; restore rbx
    	ret
    
    数组
    • 嵌套数组:行主序,对于int P[M][N]类型的数组,P[i][j] 地址为 P+(i*N+j)*4
    #define N 16
    typedef int fix_matrix[N][N];
    int fix_prod_ele(fix_matrix A,fix_matrix B,long i,long k){
    	long j;
    	int result =0 ;
    
    	for(j=0;i变长数组:对于int A[m][n],A[i][j]地址为i * n+j,必须用乘法指令对i伸缩n倍(i * j),而不能使用一系列的移位和加法, %r8d            ; result = 0
    	movl	结构struct的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址, %eax            ; j = 0
    	jmp	.L3
    .L4:
    	movq	%rdx, %rdi          ; rdi = i
    	salq	, %rdi            ; rdi = 64*i
    	addq	%r9, %rdi           ; rdi = A+64*i
    	movq	%rax, %rsi          ; rsi = j
    	salq	, %rsi            ; rsi = j*64
    	addq	%r10, %rsi          ; rsi = B+j*64
    	movl	(%rsi,%rcx,4), %esi ; M[B+j*64+4*k]【B[j][k]】
    	addl	(%rdi,%rax,4), %esi ; esi = M[A+64*i+4*j]+M[B+j*64+4*k]
    	addl	%esi, %r8d          ; r8d += esi
    	addq	, %rax            ; rax ++
    .L3:
    	cmpq	, %rdx           ; compare i:n
    	jle	.L4                     ; if <= ,goto .L4
    	movl	%r8d, %eax          ; return result
    	ret
    
    • 联合
    异质的数据结构
    • 数据对齐,通常为2、4、8
    • quit 退出GDB
    • run 运行程序(在此给出命令行参数)
    GDB调试器
    • 使用
      • 开始和停止
        • kill 停止程序
        • break multscore:在函数multscore入口处设置断点
        • break * 0x400540:在地址0x400540处设置断点
      • 短点
        • delete 1:删除断点1
        • delete:删除所有断点
        • stepi:执行一条指令
        • stepi 4:执行4条指令
      • 执行
        • nexti:类似于stepi,但以函数调用为单位
        • continue:继续执行
        • finish:运行到当前函数返回
        • disas:反汇编当前函数
        • disas multscore:反汇编函数multscore
      • 检查代码
        • disas 0x400544:反汇编位于地址0x400544附近的函数
        • disas 0x400540,0x40054d 反汇编指定地址范围内的代码
        • print /x $rip:以16进制输出程序计数器的值
        • print $rax:以十进制输出%rax的内容
        • print /t $rax:以二进制输出
      • 检查数据
        • print 0x100:以十进制输出0x100
        • print /x ($rsp+8):以16进制输出%rsp的内容加上8
        • print *(long *)($rsp+8):输出位于地址%rsp+8处的长整数
        • x/2g 0x7fffffffe818:检查从地址0x7fffffffe818开始的双(8字节)字
        • x/20b multscore:检查函数multscore的前20个字节
        • info frame:有关当前栈帧的信息
        • info registers:所有寄存器的值
      • 有用的信息
        • help
        • 缓冲区溢出:8字节buf,上面是%rbp值,所以当输入小于等于15个字符时,程序不会崩溃
        • 可以在攻击代码前加入一段nop指令,运用蛮力攻击,只要地址落在这一段代码上即可实现攻击。
    • nop指令(no operation):是程序计数器加一,没有其他效果
    [root@localhost csapp]# ./a.out
    123456
    123456
    [root@localhost csapp]# ./a.out
    123456789012345
    123456789012345
    [root@localhost csapp]# ./a.out
    1234567890123456
    1234567890123456
    Segmentation fault (core dumped)
    
    // echo.c
    #include 
    
    void echo(){
    	char buf[8];
    	gets(buf);
    	puts(buf);
    }
    int main(){
    
    	echo();
    	return 0;
    }
    
    // cc echo.c
    // objdump -d a.out
    ...
    0000000000401136 :
      401136:	55                   	push   %rbp
      401137:	48 89 e5             	mov    %rsp,%rbp
      40113a:	48 83 ec 10          	sub    $0x10,%rsp
      40113e:	48 8d 45 f8          	lea    -0x8(%rbp),%rax ; buf
      401142:	48 89 c7             	mov    %rax,%rdi
      401145:	b8 00 00 00 00       	mov    $0x0,%eax
      40114a:	e8 f1 fe ff ff       	callq  401040 
      40114f:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
      401153:	48 89 c7             	mov    %rax,%rdi
      401156:	e8 d5 fe ff ff       	callq  401030 
      40115b:	90                   	nop
      40115c:	c9                   	leaveq
      40115d:	c3                   	retq
    ...
    
    • 对抗缓冲区溢出
      • 栈随机化:使栈的位置在程序每次运行都有变化。为了在系统中插入攻击代码,攻击者既要插入代码,也要插入指向这段代码的指针,产生这个指针需要知道这个字符串放置的栈地址,栈随机化避免了此类攻击。
        • 栈破坏检测:程序每次随机产生一个值,当出现缓冲区溢出时,修改了哨兵值,函数运行完会检测此值是否改变。
        • 限制可执行代码区域:限制栈只能读写,不能代码执行
      • 在较早版本的x86代码中,每个函数调用都使用了栈指针(frame pointer)。而现在,只有栈帧长可变的情况才使用
      • pushq %rbp; movq %rsp,%rbp
    // 栈破坏检测
    // gcc echo.c -fstack-protector
    [root@localhost csapp]# ./a.out
    123456789
    123456789
    *** stack smashing detected ***: terminated
    Aborted (core dumped)
    
    0000000000401146 :
      401146:	55                   	push   %rbp
      401147:	48 89 e5             	mov    %rsp,%rbp
      40114a:	48 83 ec 10          	sub    $0x10,%rsp
      40114e:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax ; 栈保护
      401155:	00 00
      401157:	48 89 45 f8          	mov    %rax,-0x8(%rbp) ; 金丝雀canary值
      40115b:	31 c0                	xor    %eax,%eax
      40115d:	48 8d 45 f0          	lea    -0x10(%rbp),%rax ; buf
      401161:	48 89 c7             	mov    %rax,%rdi
      401164:	b8 00 00 00 00       	mov    $0x0,%eax
      401169:	e8 e2 fe ff ff       	callq  401050 
      40116e:	48 8d 45 f0          	lea    -0x10(%rbp),%rax
      401172:	48 89 c7             	mov    %rax,%rdi
      401175:	e8 b6 fe ff ff       	callq  401030 
      40117a:	90                   	nop
      40117b:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
      40117f:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax ; 查看是否栈溢出
      401186:	00 00
      401188:	74 05                	je     40118f 
      40118a:	e8 b1 fe ff ff       	callq  401040 <__stack_chk_fail@plt>
      40118f:	c9                   	leaveq
      401190:	c3                   	retq
    
    • 支持变长栈帧:long vframe(long n,long idx,long *q){ long i; long *p[n]; p[0]=&i; for(i=1;i
      • AVX浮点体系结构的16个YMM寄存器(256位):%ymm0~%ymm15
  • SSE XMM寄存器(128位):%xmm0~%xmm15
  • 浮点代码
    • 媒体寄存器
      • 传送单精度:vmovss M32,X
      • 传送单精度:vmovss X,M32
    • 浮点传送指令:不做任何转换的传送指令。X:XMM寄存器
      • 传送双精度:vmovsd M64,X
      • 传送双精度:vmovsd X,M64
      • 传送对其的封装好的单精度:vmovaps X,X
      • 传送对其的封装好的双精度:vmovapd X,X
      • float float_mov(float v1,float *src,float *dst){ float v2 = *src; *dst = v1; return v2; } // v1 in %xmm0, src in %rdi, dst in %rsi float_mov: movaps %xmm0, %xmm1 ; copy v1 movss (%rdi), %xmm0 ; Read v2 from src movss %xmm1, (%rsi) ; Write v1 to dst ret ; return v2 in %xmm0
      • 对于三个 *** 作数的指令,第一个 *** 作数读自内存或一个通用寄存器,第二个 *** 作数数只会影响结果的高位字节,一般忽略,第三个 *** 作数为目的寄存器
  • XMM寄存器%xmm0~%xmm7最多可以传递8个浮点参数。多于的使用栈
    • 浮点数和整数转换即不同浮点格式之间进行转换
      • vcvttss2si X/M32,R32:单精度->整数
      • vcvttsd2si X/M64,R32:双精度->整数
      • vcvttss2siq X/M32,R64:单精度->四字整数
      • vcvttsd2siq X/M64,R64:双精度->四字整数
      • vcvtsi2ss M32/R32,X,X:整数->单精度
      • vcvtsi2sd M32/R32,X,X:四字整数->双精度
      • vcvtsi2ssq M64/R64,X,X:整数->单精度
      • vcvtsi2sdq M64/R64,X,X:四字整数->双精度
      • 函数使用%xmm0来返回浮点数
    • 过程中的浮点代码
        所有的XMM寄存器都是调用者保存。被调用者可以不用保存就覆盖这些寄存器中的任何一个。
      • 当函数包含指针、整数、浮点数混合的参数时,指针和整数通过通用寄存器传递
      • vaddss vaddsd:浮点加 单精度 双精度
      • vsubss vsubsd:浮点减
    • 浮点运算 *** 作
      • vmulss vmulsd:乘
      • vdivss vdivsd:除
      • vmaxss vmaxsd:最大
      • vminss vminsd:最小
      • sqrtss sqrtsd:平方根
      • 定义和使用浮点常数:AVX浮点数不能以立即数作为 *** 作数。编译器必须为所有的常量分配和初始化存储空间,然后代码再把这些值从内存读入。
      • double cel2fahr(double temp){ return 1.8*temp+32.0; } cel2fahr: mulsd .LC0(%rip), %xmm0 addsd .LC1(%rip), %xmm0 ret .LC0: .long -858993459 ; 1.8的低4位 .long 1073532108 ; 1.8的高四位 .LC1: .long 0 .long 1077936128
    double funct(double a,float x,double b,int i){
    	return a*x-b/i;
    }
    
    funct:
    	cvtss2sd	%xmm1, %xmm1  ; x -> double
    	mulsd	%xmm0, %xmm1      ; a*x
    	pxor	%xmm0, %xmm0      ; 0
    	cvtsi2sdl	%edi, %xmm0   ; int -> double
    	divsd	%xmm0, %xmm2      ; b/i
    	subsd	%xmm2, %xmm1      ; a*x - b/i
    	movapd	%xmm1, %xmm0 
    	ret
    
    • vxorps xorpd:异或 单精度 双精度
  • vandps andpd
    • 位级 *** 作:
      • vucomiss:单精度比较
      • vucomisd:双精度比较
    • 浮点比较

      欢迎分享,转载请注明来源:内存溢出

      原文地址: https://outofmemory.cn/langs/734020.html

      (0)
      打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
      上一篇 2022-04-27
      下一篇 2022-04-27

      发表评论

      登录后才能评论

      评论列表(0条)