X86处理器汇编技术系列6

 

第44部分-Linux x86 64位汇编SIMD整数

单指令多数据(SIMD, Single Instruction Multiple Data)。

MMX整数

提供3种新的整数类型。

64位打包字节整数。

64位打包字整数。

64位打包双字节整数。

8个字节整数,4个字整数或2个双字整数都可以打包到单一的64位MMX寄存器中。

     MMX寄存器被映射到FPU寄存器。

 

SSE整数

流化SIMD扩展(SSE,Streaming SIMD Extension)技术提供用于处理打包数据的8个128位XMM寄存器(名为XMM0到XMM7)

SSE2技术提供4个额外的打包带符号整数数据类型:

128位打包字节整数

128位打包字整数

128位打包双字整数

128位打包四字整数

         MOVDQA和MOVDQU指令用于把128位数据传送到XMM寄存器中。

第45部分-Linux x86 64位汇编 BCD

二进制编码的十进制(BCD, Binary Coded Decimal), 用于简化对使用十进制数字的设备。

         每个BCD值都是一个无符号8位整数,值的范围是0到9.

         在BCD中,大于9的8位值被认为是非法的。8位有些浪费,出现了打包的BCD,使用4位表示一个值。

二进制编码的十进制(BCD,Binary Coded Decimal)格式是用于处理人可读的数字常见方法,处理器中可以快速处理这种格式。

很多高级的BCD处理操作位于FPU中,但是处理器会包含一些简化的指令。我们来看下基本的BCD运算指令。

不打包BCD运算

不打包的BCD值:是一个字节中包含单个十进制位(0到9).

应用程序要对不打包的BCD值执行数学操作时,应用程序假设结果也应该按照不打包BCD格式存储。

X86处理器提供了专门的指令用于从一般数学操作生成不打包BCD值。

把二进制运算结果转换为不打包BCD格式的指令有4条:

AAA:调整加法操作的结果

AAS:调整减法操作的结果

AAM:调整乘法操作的结果

AAD:准备除法操作的被除数

这些指令必须和一般无符号整数指令ADD,ADC,SUB,MUL和DIV组合一起使用。

AAA,AAS,AAM指令在各自的操作之后,把二进制结果转换为不打包BCD格式,AAD指令有些不同,在DIV指令之前使用它,用于准备被除数以便生成不打包BCD结果。

这个指令都使用一个隐含的操作数——AL寄存器。AAA,AAS,AAM指令假设前一个操作的结果存放在AL寄存器,并且把这个值转换为不打包BCD格式。

不过,坏消息是由于这些指令不是经常使用,所以在64位处理器中已经不支持了。

`aaa' is not supported in 64-bit mode

打包BCD运算

打包BCD是一个字节中包含两个十进制数字。

在64位处理器中叶已经不支持了。

第46部分-Linux x86 64位汇编 斐波那契

斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

C语言版本

 
  1. #include "stdio.h"
  2. unsigned long Fibon1(int n)
  3. {
  4. if (n == 1 || n == 2)
  5. {
  6. return 1;
  7. }
  8. else
  9. {
  10. return Fibon1(n - 1) + Fibon1(n - 2);
  11. }
  12. }
  13. int main()
  14. {
  15. int n = 0;
  16. unsigned long ret = 0;
  17. //scanf("%d", &n);
  18. n=50;
  19. ret = Fibon1(n);
  20. printf("ret=%lu\n", ret);
  21. return 0;
  22. }
 

可以直接编译运行。

#gcc -o fibo-c fibo.c

# time ./fibo-c

ret=12586269025

 

real   1m9.897s

user    1m9.931s

sys 0m0.020s

Nasm版本

 
  1. extern printf ;//调用外部的printf函数
  2. section .data
  3. fmt db "result is: %lu ", 0xa
  4.  
  5. global _start
  6.  
  7. fibo:
  8.  
  9. push rbx
  10. push rdx
  11. cmp rax, 1
  12. je _get_out
  13. cmp rax, 2
  14. je _get_out;//相当于if(n == 1 || n == 2) return 1;
  15.  
  16. mov rdx, rax;//相当于if(n!=1&&n!=2),先保存rax
  17. sub rax, 1
  18. call fibo;//相当于fibo(n-1)
  19. mov rbx, rax;//保存fibo(n-1)结果到rbx
  20.  
  21. mov rax, rdx;//获取rax值。
  22. sub rax, 2
  23. call fibo;//相当于fibo(n-2)
  24. mov rcx, rax;//保存fibo(n-2)到rbx
  25.  
  26. mov rax, rbx
  27. add rax, rcx;//fibo(n-1)+fibo(n-2)
  28.  
  29. pop rdx
  30. pop rbx
  31. ret
  32.  
  33. _get_out:
  34. pop rdx
  35. pop rbx
  36. mov rax, 1
  37. ret;//直接返回fibo函数
  38.  
  39. _start:
  40. mov rax, 50 ;//求数列中第七个数
  41. call fibo
  42. mov rdi,fmt
  43. mov rsi,rax
  44. call printf
  45.  
  46. mov rax,60
  47. syscall
 

nasm  -f elf64 -o fibo.o fibo.asm

ld -o fibo fibo.o -lc -I /lib64/ld-linux-x86-64.so.2

 

# time ./fibo

result is: 12586269025

real    0m41.641s

user    0m41.639s

sys 0m0.000s

 

Gas版本

 
  1. .extern printf ;//调用外部的printf函数
  2. .section .data
  3. fmt: .ascii "result is: %lu \n"
  4.  
  5. .global _start
  6.  
  7. fibo:
  8.  
  9. push %rbx
  10. push %rdx
  11. cmp $1,%rax
  12. je _get_out
  13. cmp $2,%rax
  14. je _get_out;//相当于if(n == 1 || n == 2) return 1;
  15.  
  16. mov %rax, %rdx;//相当于if(n!=1&&n!=2),先保存rax
  17. sub $1,%rax
  18. call fibo;//相当于fibo(n-1)
  19. mov %rax, %rbx;//保存fibo(n-1)结果到rbx
  20.  
  21. mov %rdx, %rax;//获取rax值。
  22. sub $2,%rax
  23. call fibo;//相当于fibo(n-2)
  24. mov %rax, %rcx;//保存fibo(n-2)到rbx
  25.  
  26. mov %rbx, %rax
  27. add %rcx, %rax;//fibo(n-1)+fibo(n-2)
  28.  
  29. pop %rdx
  30. pop %rbx
  31. ret
  32.  
  33. _get_out:
  34. pop %rdx
  35. pop %rbx
  36. mov $1,%rax
  37. ret;//直接返回fibo函数
  38.  
  39. _start:
  40. mov $50,%rax ;//求数列中第七个数
  41. call fibo
  42. mov $fmt,%rdi
  43. mov %rax,%rsi
  44. call printf
  45.  
  46. mov $60,%rax
  47. syscall
 

编译连接:

as -g -o fibo_att.o fibo_att.s

ld -o fibo_att fibo_att.o -lc -I /lib64/ld-linux-x86-64.so.2

# time ./fibo_att

result is: 12586269025

 

real    0m41.625s

user    0m41.623s

sys 0m0.000s

 

性能差异

 

Nasm汇编

Gas汇编

C语言

C语言-O1

编译优化

C语言-O2

编译优化

执行时间

41.6s

41.6s

1m0.876s

NA,异常

NA,异常

可以看到汇编程序编译后的明显性能优势。性能提升31.5%。

使用GCC的-O参数优化后会影响程序本身的功能。

第47部分- Linux x86 64位汇编 C调用汇编

这个部分我们直接以例子来展示。

示例

定义casm.c文件如下:

 
  1. #include <string.h>
  2.  
  3. int main() {
  4. char* str = "Hello World\n";
  5. int len = strlen(str);
  6. printHelloWorld(str, len);
  7. return 0;
  8. }
 

调用printHelloWorld函数,我们知道在X86处理中调用函数的前6个参数位于寄存器rdi, rsi, rdx, rcx, r8 and r9中,其他通过栈来实现。这样printHelloWorld函数可以通过rdi,rsi来获取前两个参数。

Nasm汇编器语法

再来看汇编文件,定义汇编文件printHelloWorld.s如下:

 
  1. global printHelloWorld
  2. section .text
  3. printHelloWorld:
  4. ;; 参数一
  5. mov r10, rdi
  6. ;; 参数二
  7. mov r11, rsi
  8. ;; 调用write系统调用
  9. mov rax, 1
  10. mov rdi, 1
  11. mov rsi, r10
  12. mov rdx, r11
  13. syscall
  14. ret
 

nasm -f elf64 -o printHelloWorld.o printHelloWorld.asm

gcc printHelloWorld.o casm.c -o casm

然后执行我们可以得到如下:

#./casm

Hello World

Gas汇编器语法

修改成gas的AT&T语法

printHelloWorld.s如下:

 
  1. .global printHelloWorld
  2. .section .text
  3. printHelloWorld:
  4. mov %rdi, %r10
  5. mov %rsi, %r11
  6. mov $1,%rax
  7. mov $1,%rdi
  8. mov %r10,%rsi
  9. mov %r11,%rdx
  10. syscall
  11. ret
 

as -o printHelloWorld.o printHelloWorld.s

gcc printHelloWorld.o casm.c -o casm

得到的结果是一样的。

 

示例二求平方

先定义个求平方的汇编函数在文件square.s中如下:

 
  1. .type square, @function
  2. .globl square
  3. square:
  4. movq %rdi,%rax
  5. imul %rax, %rax
  6. ret
 

然后C语言去调用

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int i = 2;
  6. int j = square(i);
  7. printf("The square of %d is %d\n", i, j);
  8.  
  9. j = square(10);
  10. printf("The square of 10 is %d\n", j);
  11. return 0;
  12. }
 

编译

gcc -o inttest inttest.c square.s

我们看到第一次使用了C变量i,第二次使用了立即数10。返回值被赋值给C变量j。这里我们要知道的是函数传递的时候前面六个寄存器是:

rdi,rsi,rdx,rcx,r8,r9.

示例三返回字符串

字符串比较长,只能通过返回字符串指针的方式来进行了。

这里需要提醒的事C/C++程序中处理器字符串时候,字符串必须使用空字符结尾。

默认情况下,C程序假设函数返回值是整数值。需要通知编译器这个函数范湖字符串指针,通过创建函数调用的原型来完成这个任务。

原型在使用函数之间定义。

以获取CPUID字符串为例子。

定义cpuidfunc.s汇编文件如下:

 
  1. .section .bss
  2. .comm output, 13
  3. .section .text
  4. .type cpuidfunc, @function
  5. .globl cpuidfunc
  6. cpuidfunc:
  7. movq $0, %rax
  8. cpuid
  9. movq $output, %rdi
  10. movq %rbx, (%rdi)
  11. movq %rdx, 4(%rdi)
  12. movq %rcx, 8(%rdi)
  13. movq $output, %rax
  14. ret
 

这里定义了一个缓冲区,将被主C程序访问。

定义C文件stringtest.c如下:

 
  1. #include <stdio.h>
  2.  
  3. char *cpuidfunc(void);
  4.  
  5. int main()
  6. {
  7. char *spValue;
  8. spValue = cpuidfunc();
  9. printf("The CPUID is: '%s'\n", spValue);
  10. return 0;
  11. }
 

编译:

gcc -o stringtest stringtest.c cpuidfunc.s -no-pie

 

示例三返回浮点值

浮点返回值是特殊情况。

整数和字符串返回值都是使用EAX寄存器把值从汇编语言函数返回到发出调用的C程序。

C样式使用xmm0寄存器在函数之间交换浮点值。函数把返回值存放到FPU堆栈中,然后调用程序负责把返回值弹出堆栈并且把它赋值给变量。

定义汇编函数areafunc.s如下:

 
  1. .section .text
  2. .type areafunc, @function
  3. .globl areafunc
  4. areafunc:
  5. fldpi
  6. push %rdi
  7. fild (%rsp)
  8. fmul %st(0), %st(0)
  9. fmul %st(1), %st(0)
  10. fstps (%rsp)
  11. movq (%rsp),%xmm0
  12. popq %rdi
  13. ret
 

先加载π到fpu堆栈中。然后加载半径到st0,然后半径自己相乘,最后与π相乘。最后要把结果放到xmm0中才可以。

定义C文件floattest.c如下:

 
  1. #include <stdio.h>
  2.  
  3. float areafunc(int);//声明函数
  4.  
  5. int main()
  6. {
  7. int radius = 10;
  8. float result;
  9. result = areafunc(radius);
  10. printf("The result is %f\n", result);
  11.  
  12. result = areafunc(2);
  13. printf("The result is %f\n", result);
  14. return 0;
  15. }
 

编译:

gcc -o floattest floattest.c areafunc.s

输出结果如下:

# ./floattest

The result is 314.159271

The result is 12.566371

 

示例四混合数据类型输入值

以一个双精度浮点输入值和一个整数输入值的汇编语言函数为例子。

汇编文件testfunc.s如下:

 
  1. .section .text
  2. .type testfunc, @function
  3. .globl testfunc
  4. testfunc:
  5. pushq %rdi;//第一个整型参数位于rdi寄存器中
  6. filds (%rsp)
  7. movq %xmm0, %rsi;//浮点参数位于xmm0寄存器中
  8. pushq %rsi
  9. fldl (%rsp)
  10. fmul %st(1),%st(0)
  11. fstpl (%rsp)
  12. movq (%rsp),%xmm0
  13. popq %rsi
  14. popq %rdi
  15.  
  16. ret
 

第一个参数是双精度浮点值,第二个参数是整数。

定义C语言文件prog.c如下:

 
  1. #include <stdio.h>
  2.  
  3. double testfunc(double, int);
  4.  
  5. int main()
  6. {
  7. double data1 = 3.14159;
  8. int data2 = 10;
  9. double result;
  10.  
  11. result = testfunc(data1, data2);
  12. printf("The proper result is %f\n", result);
  13. return 0;
  14. }
 

编译文件如下:

gcc -g -o prog prog.c testfunc.s

示例五超6个参数输入

这里共8个参数。计算表达式:((43.65/22+(76.34*3.1))/((12.43*6)-(140.2/94.21))

定义汇编文件fpmathfunc.s

 
  1. .section .text
  2. .type fpmathfunc, @function
  3. .globl fpmathfunc
  4. fpmathfunc:
  5.  
  6. push %rdi;//第一个整数参数value2, 56(%rsp)
  7. push %rsi;//第二个整数参数value6, 48(%rsp)
  8. movq %xmm0, %rsi
  9. push %rsi;// 第一个浮点参数value1, 40(%rsp)
  10. movq %xmm1, %rsi
  11. push %rsi;// 第二个浮点参数value3, 32(%rsp)
  12. movq %xmm2, %rsi
  13. push %rsi;// 第三个浮点参数value4, 24(%rsp)
  14. movq %xmm3, %rsi
  15. push %rsi ;// 第四个浮点参数value5, 16(%rsp)
  16. movq %xmm4, %rsi
  17. push %rsi ;// 第五个浮点参数value7,8(%rsp)
  18. movq %xmm5, %rsi
  19. push %rsi ;// 第六个浮点参数value8,(%rsp)
  20. flds 40(%rsp)
  21. fidiv 56(%rsp)
  22. flds 32(%rsp)
  23. flds 24(%rsp)
  24. fmul %st(1), %st(0)
  25. fadd %st(2), %st(0)
  26. flds 16(%rsp)
  27. fimul 48(%rsp)
  28. flds 8(%rsp)
  29. flds (%rsp)
  30. fdivrp
  31. fsubr %st(1), %st(0)
  32. fdivr %st(2), %st(0)
  33. fsts (%rsp)
  34. movq (%rsp),%xmm0
  35. //恢复栈,压栈几次出栈几次,都可以丢给rdi寄存器,反正用不到了,或者直接操作rsp寄存器。
  36. addq $64,%rsp
  37. ret
  38. # fpmathfunc.s - An example of reading multiple input values
 

定义C文件mathtest.c程序如下:

 
  1. #include <stdio.h>
  2.  
  3. float fpmathfunc(float, int, float, float, float, int, float, float);
  4.  
  5. int main()
  6. {
  7. float value1 = 43.65;//浮点参数
  8. int value2 = 22; //第一个整型参数
  9. float value3 = 76.34; //浮点参数
  10. float value4 = 3.1; //浮点参数
  11. float value5 = 12.43; //浮点参数
  12. int value6 = 6; //整型参数
  13. float value7 = 140.2; //浮点参数
  14. float value8 = 94.21; //浮点参数
  15. float result;
  16. result = fpmathfunc(value1, value2, value3, value4,
  17. value5, value6, value7, value8);
  18. printf("The final result is %f\n", result);
  19. return 0;
  20. }
 

编译gcc -o -g mathtest mathtest.c fpmathfunc.s

# ./mathtest

The final result is 3.264907

第48部分- Linux x86 64位汇编 C++调用汇编

C++程序中使用所有函数都使用C++样式的命名和调用约定。

但是使用汇编函数也是使用C语言的调用约定。

但是要通知编译器使用哪些函数是C函数,通过extern语句来完成。

示例

定义C++文件externtest.cpp如下:

 
  1. #include <iostream>
  2.  
  3. extern "C" {
  4. int square(int);
  5. float areafunc(int);
  6. char *cpuidfunc(void);
  7. }
  8.  
  9.  
  10. int main()
  11. {
  12. int radius = 10;
  13. int radsquare = square(radius);
  14. char* cpuid = cpuidfunc();
  15. std::cout << "The radius squared is " << radsquare << std::endl;
  16. float result;
  17. result = areafunc(radius);
  18. std::cout << "The area is " << result << std::endl;
  19. std::cout << "The CPUID is " << cpuid << std::endl;
  20. return 0;
  21. }
 

需要包含之前的3个汇编语言文件:

square.s,areafunc.s,cpuidfunc.s

编译命令:

g++ -o externtest externtest.cpp square.s areafunc.s  cpuidfunc.s  -no-pie

# ./externtest

The radius squared is 100

The area is 314.159

The CPUID is GenuineIntel

第49部分- Linux x86 64位汇编 创建静态库和动态库

如果为每个汇编语言函数创建单独的目标文件,会导致文件数量剧增。

可以通过使用库来简化汇编函数的目标文件问题。

GNC的C编译器可以不在命令行中独立的包含每个单独的函数目标文件,允许把所有目标文件组合在单一存档文件中。当编译C主程序时,要做的所有工作就是包含单一的目标存档文件。

存档文件可以用于编译任何使用存档文件中包含的任何函数的程序,这种存档文件就是库文件library file.

         经常按照应用程序类型或者函数类型把函数分组在一起,单一应用程序项目中可以使用多个库文件。

         库文件中包含的目标代码被编译器编译到了主程序中,这类库文件成为静态文件。函数的目标代码被编译到可执行代码中之后,可执行程序的运行就不需要库文件了。这样每个程序都包含了函数的代码。

         在linux中使用ar命令创建静态文件。

         Ar的命令行选项如下:

可以用一个或多个修饰符修改基本选项:

创建静态库

Linux操作系统中的静态库命名为libx.a

其中x是库的名称。

我们将C调用汇编中的几个文件汇编成一个静态文件。

先生成目标文件:

as -o square.o square.s

as -o areafunc.o areafunc.s

as -o cpuidfunc.o cpuidfunc.s

生成库文件:

ar -r liblearn.a square.o areafunc.o cpuidfunc.o

通过tv参数可以查看

ar -tv liblearn.a

创建库文件后,可以创建库的索引来帮助必须和库连接的其他程序的编译速度。

ranlib liblearn.a

可以通过nm命令来查看库文件中的函数的符号

nm liblearn.a

使用静态库

然后我们重新编译之前的函数:

gcc -o inttest inttest.c square.s

现在使用静态编译库编译命令如下:

gcc -o intest inttest.c liblearn.a

两者编译出来的大小是一样的。

创建动态库

我们知道windows中的动态库是DLL文件。

在Linux里面就是.so文件。

在说明动态库之前,我们来看下为什么要用动态库。

静态文件的优缺点

  • 库文件已修改,所有依赖此函数的应用程序都要重新编译。强耦合。
  • 程序要包含使用到的每个函数代码,程序会变大。浪费空间。
  • 多个程序使用相同的函数,都需要将该函数 加载到内存,浪费内存。

共享库就是解决这些问题的,它包含函数目标代码的单独文件,被加载到操作系统的通用区域中。当应用程序需要访问共享库中的函数时,操作系统自动把函数代码加载到内存中,并且允许应用程序访问它。

创建共享库

Linux对共享库的命名约定是libx.so

X是库名称,扩展名.so是表示共享库。

根据上面的3个汇编文件我们创建一个共享库

gcc -shared -o liblearn.so square.o areafunc.o

gcc -shared -fPIC -o liblearn.so square.o areafunc.o

 

这里去掉了cpuidfunc函数,因为该汇编文件不符合动态库要求。

也可以使用nm进行查看。

nm -D liblearn.so

使用共享库

还是以之前的inttest.c文件为例,之前的编译命令为:

gcc -o inttest inttest.c square.s

动态库的编译方式如下:

gcc -o inttest -L. -llearn inttest.c
或者如下:

gcc inttest.c  liblearn.so -o inttest

我们要知道动态库不会编译到C程序中,但是编译器需要知道如何访问函数,使用-l选项加上共享库的名称(减去lib部分和.so扩展名)。使用-L选项通知编译器在哪里查找它。如果共享库位于和程序文件相同的目录中,可以使用本地的目录。

反汇编后,我们可以发现可执行文件中不包含函数代码。

 

 

另外可以使用ldd命令来查看可执行文件依赖什么共享库。

# ldd inttest

    linux-vdso.so.1 (0x00007ffcafbe0000)

    liblearn.so => /root/liblearn.so (0x00007fcb5c89b000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcb5c4aa000)

    /lib64/ld-linux-x86-64.so.2 (0x00007fcb5cc9f000)

 

共享库的程序的变量

动态加载器访问动态库的方法有两种:

  • LD_LIBRARY_PATH
  • /etc/ld.so.conf文件

例如 export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:."

/etc/ld.so.conf文件保存动态加载器在哪些目录中查找库的目录清单,主要是系统动态库。

修改后需要执行ldconfig进行加载。

第50部分- Linux x86 64位汇编 汇编调用C

汇编文件可以直接调用C函数。

定义print.c文件如下:

 
  1. #include <stdio.h>
  2.  
  3. extern int print();
  4. int print() {
  5. printf("Hello World\n");
  6. return 0;
  7. }
 

然后定义汇编文件

Nasm语法

 
  1. global _start
  2. extern print
  3. section .text
  4. _start:
  5. call print
  6.  
  7. mov rax, 60
  8. mov rdi, 0
  9. syscall
 

gcc  -c print.c -o c.o

nasm -f elf64 print.asm -o print.o

ld   -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc print.o c.o -o print

 

Gas语法

 
  1. .global _start
  2. .extern print
  3. .section .text
  4. _start:
  5. call print
  6.  
  7. movq $60,%rax
  8. movq $0,%rdi
  9. syscall
 

gcc  -c print.c -o c.o

as -o print.o print.s

ld   -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc print.o c.o -o print

第51部分- Linux x86 64位汇编内嵌汇编

用汇编编写的程序运行速度快,但开发效率很低。如果只是想对关键代码段进行优化,更好的办法是将汇编指令嵌入到 C 语言程序中。但在 C 代码中嵌入汇编语句要比"纯粹"的汇编语言代码复杂,因为需要解决如何分配寄存器,以及如何与C代码中的变量相结合等问题。

GCC 提供了很好的内联汇编支持,最基本的格式是:

__asm__("asm statements");

Instruction List是汇编指令序列,可以是空的,比如: __asm__ ("");只不过这条语句没有什么意义。

__volatile__是GCC关键字volatile的宏定义,如果用了它,则是向GCC声明“不要动我所写的Instruction List,需要原封不动的保留每一条指令”,否则当使用了优化选项(-O)进行编译时,GCC将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

示例

 
  1. #include "stdio.h"
  2.  
  3. int main()
  4. {
  5. int a = 10, b = 0;
  6. __asm__ __volatile__("movl %1,%%eax \n\t"
  7. "movl %%eax,%0 \n\t"
  8. :"=g"(b) /* 输出 */
  9. :"g"(a) /* 输入 */
  10. ); /* 不受影响的寄存器 */
  11. printf("Result: %d, %d \n", a, b);
  12. return 0;
  13. }
 

程序完成将变量a的值赋予变量b。

%%是寄存器前缀符号。

实例二

 
  1. #include <string.h>
  2. int main() {
  3. char* str = "Hello World\n";
  4. long len = strlen(str);
  5. int ret = 0;
  6. __asm__("movq $1, %%rax \n\t"
  7. "movq $1, %%rdi \n\t"
  8. "movq %1, %%rsi \n\t"
  9. "movl %2, %%edx \n\t"
  10. "syscall"
  11. : "=g"(ret)
  12. : "g"(str), "g" (len));
  13.  
  14. return 0;
  15. }
 

gcc inlineasm.c -o inlineasm

这里涉及到输出修饰符。

第52部分- Linux x86 64位汇编基本内嵌汇编

用汇编语言去实现函数有如下3种选择。

  • 从头开始编写汇编语言代码来实现函数,然后C程序来调用
  • 使用-S创建C代码的汇编版本,然后修改汇编语言代码,然后连接汇编语言代码生成可执行文件
  • 在原始的C代码内创建函数的湖边语言代码,然后使用标准C编译器编译

第二种是优化使用。

第三种就是内联汇编。

仅仅实现汇编语言代码不能说并不能完成很多任务,需要把数据进行传递进出。

基本的内联汇编代码可以利用应用程序中定义的全局变量。这里是管局的变量。

基本内联汇编

基本内联汇编格式比较直观,可以直接这样写:
asm("assembly code");

例如:

asm("movl %rcx, %rax"); /* 把 rcx 内容移动到 rax */

__asm__("movb %bh , (%eax)"); /* 把bh中一个字节的内容移动到eax指向的内存 */
 

两个不同的关键字 asm 和__asm__。这两个关键字都可以使用。不过当遇到asm关键字与程序其他变量有冲突的时候就必须用__asm__了

内联汇编有多条指令,则每行要加上双引号,并且该行要以\n\t结尾。因为GCC会将每行指令作为一个字符串传给as(GAS),使用换行和TAB可以将正确且格式良好的代码行传递给汇编器。

如果在内联代码中操作了一些寄存器,比如修改了寄存器内容(而之后也没有进行还原操作),程序很可能会产生一些难以预料的情况。因为GCC并不知道已经将寄存器内容修改了。尤其是在编译器对代码进行了一些优化的情况下而导致问题。编译器注意不到寄存器内容已经被改掉,程序将当作它没有被修改过而继续执行。所以尽量不要使用这些会产生附加影响的操作,或者当退出的时候还原这些操作。否则很可能会造成程序崩溃。

第53部分- Linux x86 64位汇编内联汇编扩展ASM

基本asm汇编时候,汇编代码通过C全局变量名称整合输入和输出值。

基本内联汇编只涉及到嵌入汇编指令,而在扩展形式中,还可以指定操作数,并且可以选择输入输出寄存器,以及指明要修改的寄存器列表。对于要访问的寄存器,并不一定要要显式指明,也可以留给GCC自己去选择,这可能让GCC更好去优化代码。扩展内联汇编格式如下:

asm ( assembler template

        : output operands                /* optional */

        : input operands                   /* optional */

        : list of clobbered registers   /* optional */

);

 

 

 

使用扩展ASM格式,可以使用局部和全局变量。

如果没有输出操作数但有输入操作数,必须放两个连续冒号。

扩展ASM格式中,寄存器必须使用两个百分号符号。

指定输入和输出约束

扩展格式中,可以从寄存器和内存位置给输入值和输出值复制。

输入值和输出值列表的格式是:

“constraint” (varialbe)

其中variable是程序中声明的C变量。

约束是单一字符的代码,其中约束代码如下:

除了这些约束外,输出值还包含一个约束修饰符,只是编译器如何处理输出值。

输出值的约束修饰符如下:

使用寄存器示例

 
  1. #include <stdio.h>
  2. int main()
  3. {
  4. int data1 = 10;
  5. int data2 = 20;
  6. int result;
  7.  
  8. asm ("imul %%rdx, %%rcx\n\t"//rdx和rcx相等,就是data1data2相乘,保存到rcx
  9. "movq %%rcx, %%rax"//将rcx移动到rax
  10. : "=a"(result)//结果使用rax寄存器输出,只写
  11. : "d"(data1), "c"(data2));//使用rdx寄存器和rcx寄存器
  12.  
  13. printf("The result is %d\n", result);
  14. return 0;
  15. }
 

gcc -o regtest regtest.c

# ./regtest

The result is 200

进行汇编:

gcc -o regtest.s -S regtest.c

可以看到代码片段:

        movl    $10, -12(%rbp);//变量1放到堆栈中

        movl    $20, -8(%rbp;//变量2放到堆栈中

        movl    -12(%rbp), %eax;//根据内联汇编,将10放入到eax

        movl    -8(%rbp), %ecx;//根据内联汇编,将20放入到ecx

        movl    %eax, %edx;// 根据内联汇编最后将10放入到edx

#APP

# 8 "regtest.c" 1

        imul %rdx, %rcx;// 内联汇编内容,相乘

        movq %rcx, %rax// 内联汇编内容,结果放到eax

# 0 "" 2

#NO_APP

        movl    %eax, -4(%rbp) ;//将结果保存到堆栈中内联汇编内容

        movl    -4(%rbp), %eax//将结果根据内联汇编存放到eax寄存器中

不指定输出值示例

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. char input[30]={"This is a test message.\n"};
  6. char output[30];
  7. int length=25;
  8.  
  9. asm volatile ("cld\n\t"
  10. "rep movsb"
  11. :
  12. : "S"(input),"D"(output),"c"(length));//使用esi寄存器表示input,使用edi寄存器表示output,使用ecx寄存表示length。作为3个输入。
  13. printf("%s",output);
  14. return 0;
  15. }
 

gcc -o regtest regtest2.c

要复制的字符串放在esi寄存器中,目标位置放在edi寄存器中,要复制的字符串长度存放在ecx寄存器中。

没有定义专门的输出值。

第54部分- Linux x86 64位汇编内联汇编使用占位符

使用寄存器可以处理只有几个输入值的情况,如果有很多输入值的函数,需要占位符。可以在内联汇编代码中引用输入值和输出值,方便编译器在任何寄存器和内存位置中声明输入和输出值。

占位符是前面加上百分号符号的数字。每个值被赋予一个从零开始的数字。

将前面使用寄存器示例使用占位符示例

示例

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int data1 = 10;
  6. int data2 = 20;
  7. int result;
  8.  
  9. asm ("imull %1, %2\n\t"
  10. "movl %2, %0"
  11. : "=r"(result)
  12. : "r"(data1), "r"(data2));
  13.  
  14. printf("The result is %d\n", result);
  15. return 0;
  16. }
 

gcc -o regtest regtest3.c

使用约束r。表示使用寄存器满足所有数据需求。

这里%0将表示result,%1表示data1,%2表示data2。

占位符提供在内联汇编代码中利用寄存器和内存位置的方法。

进行反汇编

#gcc -o regtest.s -S regtest3.c查看:

有片段如下:

        movl    $10, -8(%rbp)

        movl    $20, -4(%rbp)

        movl    -8(%rbp), %edx;//使用edx保存10

        movl    -4(%rbp), %eax;//使用eax保存20

#APP

# 9 "regtest3.c" 1

        imull %edx, %eax;//eax和edx相乘,结果直接到eax中。没有在需要使用ecx寄存器了,还优化了一点。

# 0 "" 2

#NO_APP

        movl    %eax, -4(%rbp)

        movl    -4(%rbp), %eax

相比直接使用寄存器还优化了一点。

第55部分- Linux x86 64位汇编内联汇编引用占位符

有时候使用相同的变量作为输入值和输出值是有好处的。必须在扩展asm段中区别定义输入值和输出值。

         如果内联汇编代码中的输入值和输出值共享程序中相同的C变量,可以指定占位符作为约束值。可以减少代码中需要的寄存器。

#include <stdio.h>
 
int main()
{
   int data1 = 10;
   int data2 = 20;
 
   asm ("imull %1, %0"
        : "=r"(data2)
        : "r"(data1), "0"(data2));
 
   printf("The result is %d\n", data2);
   return 0;
}

gcc -o regtest regtest3.c

得到的效果是一样的。

第56部分- Linux x86 64位汇编内联汇编替换占位符

处理很多输入值和输出值,数字型的占位符很快会变得混乱。GNU编译器允许声明替换的名称作为占位符。

替换的名称在声明输入值和输出值的段中定义。

格式如下:

%[name] “constraint” (variable)

定义name成为内联汇编代码中变量的新的占位符标识符。

示例

#include <stdio.h>
 
int main()
{
   int data1 = 10;
   int data2 = 20;
 
   asm ("imull %[value1], %[value2]"
        : [value2] "=r"(data2);//定义替换符号value2
        : [value1] "r"(data1), "0"(data2)); //定义替换符号value1
 
   printf("The result is %d\n", data2);
   return 0;
}

gcc -o alttest alttest.c

第57部分- Linux x86 64位汇编改动寄存器列表

编译器假设输入值和输出值使用的寄存器会被改动,并相应的做出处理。

程序员不需要在改动的寄存器列表中包含这些值。

示例

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int data1 = 10;
  6. int result = 20;
  7.  
  8. asm ("movl %1, %%eax\n\t"
  9. "addl %%eax, %0"
  10. : "=r"(result)
  11. : "r"(data1), "0"(result)
  12. : "%eax");
  13.  
  14. printf("The result is %d\n", result);
  15. return 0;
  16. }
 

gcc -o changedtest changedtest.c

这里内联汇编代码中使用eax寄存器作为存储数据的中间位置,这个寄存器没有被声明为输入值或输出值,所以必须在改动的寄存器列表中包含。然后编译器会知道EAX寄存器不可用会使用其他寄存器。

第58部分- Linux x86 64位汇编内联汇编使用内存位置

内联汇编中使用寄存器比使用内存要快,但是也可以直接使用C变量的内存位置的。约束为m.

示例

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int dividend = 20;
  6. int divisor = 5;
  7. int result;
  8.  
  9. asm("divb %2\n\t"
  10. "movl %%eax, %0"
  11. : "=m"(result)
  12. : "a"(dividend), "m"(divisor));
  13.  
  14. printf("The result is %d\n", result);
  15. return 0;
  16. }
 

gcc -o memtest memtest.c

asm代码段中将被除数放到了eax寄存器中,除数放到内存位置中,值被加载到内存位置中。

第59部分- Linux x86 64位汇编内联汇编使用浮点值

FPU堆栈方式使用寄存器在内联汇编中有一点区别。

内联汇编中:

  • f引用任何可用的浮点寄存器
  • t引用顶部的浮点寄存器
  • u引用第二个浮点寄存器

在获取输出值的时候不能使用约束f,必须声明约束t或者u来指定输出值所在的fpu寄存器。

示例

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. float angle = 90;
  6. float radian, cosine, sine;
  7.  
  8. radian = angle / 180 * 3.14159;
  9.  
  10. asm("fsincos";//求解sin和cos值,此时st0是radian值,结果存放于st0和st1
  11. :"=t"(cosine), "=u"(sine);//输出是st0和st1
  12. :"0"(radian));
  13.  
  14. printf("The cosine is %f, and the sine is %f\n", cosine, sine);
  15. return 0;
  16. }
 

gcc -o sincostest sincostest.c

反汇编:

gcc -o sincostest.s -S sincostest.c

有代码片段:

        flds    -20(%rbp)

#APP

# 11 "sincostest.c" 1

        fsincos

# 0 "" 2

#NO_APP

        fxch    %st(1)

        fstps   -20(%rbp)

        fstps   -8(%rbp)

先使用了flds加载,最后将st0和st1都通过fstps进行了弹出操作。保持了fpu寄存器的干净。

 

示例二

如果FPU堆栈执行的操作没有被清除,就必须在改动的寄存器列表中指定适当的FPU寄存。

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int radius = 10;
  6. float area;
  7.  
  8. asm("fild %1\n\t"
  9. "fimul %1\n\t"
  10. "fldpi\n\t"
  11. "fmul %%st(1), %%st(0)"
  12. : "=t"(area)
  13. :"m"(radius)
  14. : "%st(1)");
  15.  
  16. printf("The result is %f\n", area);
  17. return 0;
  18. }
 

gcc -o areatest areatest.c

把半径值放在一个内存radius位置中,通过fild加载radius中到st0中。

然后将内存radius位置和st0相乘,存在st0中。加载π到st0中,原半径相乘值移动到st1中。将st0和st1相乘。将st0结果复制给area变量。这里我们看到st0作为了输出寄存器使用,但是st1没有在输入和输出中标记出来,所以需要在改动的寄存器列表中列出它。

第60部分- Linux x86 64位汇编内联汇编处理跳转

内联汇编语言代码也可以包含定义其中位置的标签。

内联汇编代码中使用标签时有两个限制,第一个限制是只能跳转到相同的asm段内的标签,不能从一个asm段跳转到另一个asm段中的标签。

第二个限制是汇编后的代码清单不能使用相同的标签。

跳转示例

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int a = 10;
  6. int b = 20;
  7. int result;
  8.  
  9. asm("cmp %1, %2\n\t"
  10. "jge greater\n\t"
  11. "movl %1, %0\n\t"
  12. "jmp end\n"
  13. "greater:\n\t"
  14. "movl %2, %0\n"
  15. "end:"
  16. :"=r"(result)
  17. :"r"(a), "r"(b));
  18.  
  19. printf("The larger value is %d\n", result);
  20. return 0;
  21. }
 

gcc -o jmptest jmptest.c

数字标签示例

条件分支和无条件分支都允许指定一个数字加上方向标志作为标签,方向标志指出处理器应该向哪个方向查找数字型标签。

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int a = 10;
  6. int b = 20;
  7. int result;
  8.  
  9. asm("cmp %1, %2\n\t"
  10. "jge 0f\n\t"
  11. "movl %1, %0\n\t"
  12. "jmp 1f\n"
  13. "0:\n\t"
  14. "movl %2, %0\n"
  15. "1:"
  16. :"=r"(result)
  17. :"r"(a), "r"(b));
  18.  
  19. printf("The larger value is %d\n", result);
  20. return 0;
  21. }
 

gcc -o jmptest jmptest2.c

第61部分- Linux x86 64位汇编内联汇编宏函数

C程序中的任何位置都可以防止内联汇编代码,但是大多数程序员把内联汇编代码用作宏函数。

C宏函数

先回顾下C宏函数示例

 
  1. #include <stdio.h>
  2.  
  3. #define SUM(a, b, result) \
  4. ((result) = (a) + (b))
  5.  
  6. int main()
  7. {
  8. int data1 = 5, data2 = 10;
  9. int result;
  10. float fdata1 = 5.0, fdata2 = 10.0;
  11. float fresult;
  12.  
  13. SUM(data1, data2, result);
  14. printf("The result is %d\n", result);
  15. SUM(1, 1, result);
  16. printf("The result is %d\n", result);
  17. SUM(fdata1, fdata2, fresult);
  18. printf("The floating result is %f\n", fresult);
  19. SUM(fdata1, fdata2, result);
  20. printf("The mixed result is %d\n", result);
  21. return 0;
  22. }
 

gcc -o mactest1 mactest1.c

宏函数中定义的变量完全独立于程序中定义的结果变量。

之类的宏函数SUM()可以处理整数输入值、数字型输入值、浮点输入值,甚至还可以处理混合的输入值和输出值。

 

汇编宏函数

也可以声明包含内联汇编代码的宏函数。必须使用扩展asm格式,以便定义正确的输入值和输出值。

Asm语句必须括在一对花括号中,以便指出语句的开头和结尾。如果没有花括号,每次在C代码中使用宏时,编译器都会生成错误消息。

示例

 
  1. #include <stdio.h>
  2.  
  3. #define GREATER(a, b, result) ({ \
  4. asm("cmp %1, %2\n\t" \
  5. "jge 0f\n\t" \
  6. "movl %1, %0\n\t" \
  7. "jmp 1f\n\t" \
  8. "0:\n\t" \
  9. "movl %2, %0\n\t" \
  10. "1:" \
  11. :"=r"(result) \
  12. :"r"(a), "r"(b)); })
  13.  
  14. int main()
  15. {
  16. int data1 = 10;
  17. int data2 = 20;
  18. int result;
  19.  
  20. GREATER(data1, data2, result);
  21. printf("a = %d, b = %d result: %d\n", data1, data2, result);
  22.  
  23. data1 = 30;
  24. GREATER(data1, data2, result);
  25. printf("a = %d, b = %d result: %d\n", data1, data2, result);
  26. return 0;
  27. }
 

gcc -o mactest2 mactest2.c

可以成功运行。

输入变量a和b被赋值给寄存器,可以在CMP指令中使用它们。

JGE/JMP指令使用数字型的标签,以便可以在程序中多次使用宏函数而不会出现重复的汇编标签。

第62部分- Linux x86 64位汇编 浮点数

单精度是这样的格式,1位符号,8位指数,23位小数。

https://pic4.zhimg.com/80/v2-749cc641eb4d5dafd085e8c23f8826aa_1440w.jpg

指数是从-128到127的8位有符号整数,或者是从0到255的8位无符号整数。指数部分采用的偏置码(biased)的形式来表示正负指数,若小于127则为负的指数,否则为非负的指数。所以0到126代表-127到-1,127代表零,128-255代表1-128。

另外补码,一个数字的补码就是将该数字作比特反相运算(即反码),再将结果加1。在补码系统中,一个负数就是用其对应正数的补码来表示。

补码系统的最大优点是可以在加法或减法处理中,不需数字的正负而使用不同的计算方式。只要一种加法电路就可以处理各种有号数加法,而且减法可以用一个数加上另一个数的补码来表示,因此只要有加法电路及补码电路即可完成各种有号数加法及减法,在电路设计上相当方便。

单精度公式:(-1)^S*(1.M)*2^(E-127)  

S是符号。

M是尾数。

E是指数。

双精度是1位符号,11位指数,52位小数。

https://pic2.zhimg.com/80/v2-48240f0e1e0dd33ec89100cbe2d30707_1440w.jpg

双精度公式: (-1)^S*(1.M)*2^(E-1023) 

扩展双精度是80位的,1位符号,15位指数,112位小数。

f、j、e 和 s 四个字段中的位模式值将决定整个位模式所表示的值。

计算模式相对复杂一点,大家可以忽略之。

双精度扩展位模式 (x86)

j = 0, 0 < e <32767

不支持

j = 1, 0 < e < 32767

(–1)s × 2e–16383 × 1.f(正规数)

j = 0, e = 0; f ≠ 0(f 中至少有一位不为零)

(–1)s × 216382 × 0.f(次正规数)

j = 1, e = 0

(–1)s × 216382 × 1.f(伪非正规数)

j = 0, e = 0, f = 0(f 中的所有位均为零)

(–1)s × 0.0(有符号的零)

j = 1; s = 0; e = 32767; f = 0(f 中的所有位均为零)

+INF(正无穷大)

j = 1; s = 1; e = 32767; f = 0(f 中的所有位均为零)

–INF(负无穷大)

j = 1; s = u; e = 32767; f = .1uuu — uu

QNaN(quiet NaN,静态 NaN)

j = 1; s = u; e = 32767; f = .0uuu — uu ≠ 0

(f 中至少一个 u 不为零)

SNaN(signaling NaN,信号 NaN)

SSE浮点数据类型

除了3种标准浮点数据类型之外,SSE技术的INTEL处理器还包含两种高级浮点数据类型。8个128位XMM寄存器可以保存打包浮点数。类似打包BCD概念。

数据移动如下:

         SSE2数据传送指令如下:

数据类型转换

PS是打包单精度FP

PD是打包双精度FP

PI是打包双字整数,使用MMX

DQ是打包双字整数

示例

转换数据类型如下:

 
  1. .section .data
  2. value1:
  3. .float 1.25, 124.79, 200.0, -312.5
  4. value2:
  5. .int 1, -435, 0, -25
  6. .section .bss
  7. data:
  8. .lcomm datas, 16
  9. .section .text
  10. .globl _start
  11. _start:
  12. nop
  13. cvtps2dq value1, %xmm0;//打包单精度到打包双字整型
  14. cvttps2dq value1, %xmm1;//打包单精度到打包双字整型,会截断
  15. cvtdq2ps value2, %xmm2;//打包双字整型到打包单精度,会截断
  16. movdqu %xmm0, datas;//保存到datas变量中。
  17.  
  18. movl $1, %eax
  19. movl $0, %ebx
  20. int $0x80
 

在value1中定义了一个打包单精度浮点值。

value2中定义了打包双字整数值。

使用gdb进行调试,可以看到如下xmm寄存器。

(gdb) print $xmm0

$8 = {v4_float = {1.40129846e-45, 1.75162308e-43, 2.80259693e-43, -nan(0x7ffec8)}, v2_double = {2.6524947387115311e-312, -nan(0xffec8000000c8)}, v16_int8 = {1, 0, 0, 0, 125, 0,

    0, 0, -56, 0, 0, 0, -56, -2, -1, -1}, v8_int16 = {1, 0, 125, 0, 200, 0, -312, -1}, v4_int32 = {1, 125, 200, -312}, v2_int64 = {536870912001, -1340029796152},

  uint128 = 340282342201751762702250093524836941825}

(gdb) print $xmm1

$9 = {v4_float = {1.40129846e-45, 1.7376101e-43, 2.80259693e-43, -nan(0x7ffec8)}, v2_double = {2.6312747808018783e-312, -nan(0xffec8000000c8)}, v16_int8 = {1, 0, 0, 0, 124, 0,

    0, 0, -56, 0, 0, 0, -56, -2, -1, -1}, v8_int16 = {1, 0, 124, 0, 200, 0, -312, -1}, v4_int32 = {1, 124, 200, -312}, v2_int64 = {532575944705, -1340029796152},

  uint128 = 340282342201751762702250093520541974529}

(gdb) print $xmm2

$10 = {v4_float = {1, -435, 0, -25}, v2_double = {-7.3498756827903427e+18, -805306368}, v16_int8 = {0, 0, -128, 63, 0, -128, -39, -61, 0, 0, 0, 0, 0, 0, -56, -63}, v8_int16 = {

    0, 16256, -32768, -15399, 0, 0, 0, -15928}, v4_int32 = {1065353216, -1009156096, 0, -1043857408}, v2_int64 = {-4334292427813683200, -4483333429047328768},

  uint128 = 257579462558195729010253313545846390784}

可以看到cvttps2dq指令会进行截断。

而cvtps2dq指令会进行舍入。

另外,最后在调试的时候大家会发现,xmm寄存器已经被扩展了,已经变成了256位的zmm了。

第63部分- Linux x86 64位汇编 FPU之概要

X87浮点单元FPU,提供高性能浮点处理。从80486就开始支持了。

Intel x87 FPU专门用于执行标量浮点计算,对单精度浮点(32位)、双精度浮点(64位)以及扩展双精度浮点(80位)进行计算,并顺从IEEE754标准。

FPU的数据寄存器的个数都一样,只有8个。对x87 FPU的数据寄存器的访问方式与一般的寄存器有所不同,是栈式访问。通过FLD指令把外部数据搬到x87 FPU的数据寄存器中时,那么x87 FPU会根据所搬数据的长度(32位、64位、80位)将输入数据分别对待为单精度浮点、双精度浮点和扩展双精度浮点方式,然后统一转为双精度扩展模式放到数据栈顶。因此x87 FPU的数据寄存器长度为80位,并且后续的浮点计算都是基于扩展双精度进行的。

在栈顶的数据寄存器索引为0,那么它下面一个就是1, 然后是2, 以此类推,到栈底则为7。

数据输出到存储器时使用FST或FSTP指令,前者仅仅是根据目标存储器的长度(32位、64位、80位)将扩展双精度类型分别转为单精度、双精度、扩展双精度类型,然后输出到指定的存储器位置中。FSTP指令,除了将数据搬到外部外,还会执行推出堆栈的操作。

FLD和FST以及FSTP还能搬移FPU内部寄存器的数据,FLD的作用是将指定的FPU数据寄存器位置的数据搬移到栈顶寄存器中,而FST则是将栈顶寄存器的数据搬移到指定的FPU数据寄存器的位置。

由于80位(10个字节)并不是32位(4个字节)的整数倍,因此数据加载或存储双精度扩展浮点往往是用96位(12个字节)为单位进行,所以“tbyte”所指的就是“twelve bytes”

Intel汇编格式的字节、字宽指定修饰词:byte(字节,8位),word(字,16位),dword(双字,32位),qword(四字,64位),tbyte(十二字节,96位),mmword(64位,但只能用于MMX指令集中),xmmword(128位,用于SSE指令集),ymmword(256位,用于最新的AVX指令集)。

FPU有以环形堆栈组织的八个10字节寄存器。 堆栈顶部-寄存器ST(0),其他寄存器为ST(1),ST(2)…ST(7)。 在处理浮点数据时,通常会使用它。

数据传输指令

FDL-加载浮点。

FST-存储浮点(在ST(0)寄存器中)。

FSTP-存储浮点数(在ST(0)寄存器中)并弹出。

 

FLD类似于PUSH指令

FSTP类似于POP指令

FADD类似于ADD指令

 

算术指令:

FADD-浮点加法;// ST(0)与 ST(1)相加,结果暂存在 ST(1)。然后 ST(0) 弹出堆栈,把加法结果保留在栈顶。

FIADD-将整数添加到浮点

FSUB-减去浮点

FISUB-从浮点数减去整数

FABS-获得绝对值

FIMUL-整数和浮点数相乘

FIDIV-设备整数和浮点

 

 

示例

定义printResult.c文件如下:

 
  1. #include <stdio.h>
  2.  
  3. extern int printResult(double result);
  4.  
  5. int printResult(double result) {
  6. printf("Circle radius is - %f\n", result);
  7. return 0;
  8. }
 
      1. Nasm语法

定义实现的汇编文件printResult.asm如下:

 
  1. extern printResult
  2. section .data
  3. radius dq 1.7
  4. result dq 0
  5.  
  6. SYS_EXIT equ 60
  7. EXIT_CODE equ 0
  8.  
  9. global _start
  10. section .text
  11. _start:
  12. fld qword [radius];//保存到st(0)
  13. fld qword [radius] ;//保存到st(1)
  14. fmul;//将 ST(O) 与 ST(1) 相乘,乘积暂存于ST(1)。然后 ST(0) 弹出堆栈,将乘积则继续留在栈顶。
  15. fldpi;//装入浮点的pi,就是装入st(1)
  16. fmul;//将ST(O) 与 ST(1) 相乘,保存到st(0)
  17. fstp qword [result];//将结果保存到[result]
  18.  
  19. mov rax, 0;// 表示xmmN寄存器的数量放入rax寄存器
  20. movq xmm0, [result];//将参数也就是结果放到xmm0.
  21. call printResult
  22.  
  23. mov rax, SYS_EXIT;退出系统调用的号
  24. mov rdi, EXIT_CODE
  25. syscall
 

其中DQ作为汇编语言中的伪操作命令,用来定义操作数占用的字节数,占有4个字,即8个字节(64位),可用来存放双精度浮点数。

浮点参数的传参有特殊的寄存器:sse提供的xmm0-xmm15(而不是通常的rdi,rsi……)。需要将xmmN寄存器的数量放入rax寄存器(本例中为0),然后将结果放入xmm0寄存器。

编译如下:

gcc -g -c printResult.c -o c.o

nasm -f elf64 printResult.asm -o printResult.o

ld   -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc printResult.o  c.o -o printResult

gas语法

定义实现的汇编文件如下:

printResult.s

 
  1. .extern printResult
  2.  
  3. .section .data
  4. radius: .double 1.7
  5. result: .double 0
  6.  
  7. .equ SYS_EXIT , 60
  8. .equ EXIT_CODE, 0
  9.  
  10. .global _start
  11. .section .text
  12. _start:
  13. leaq radius,%rbx
  14. fldl (%rbx)
  15. fldl (%rbx)
  16. fmulp
  17.  
  18. fldpi
  19. fmulp
  20. fstl result
  21.  
  22. movq $0,%rax
  23. movq result, %xmm0
  24. call printResult
  25.  
  26. movq $SYS_EXIT,%rax
  27. movq $EXIT_CODE,%rdi
  28. syscall
 

编译如下:

gcc -g -c printResult.c -o c.o

as -g -o printResult.o  printResult.s

ld   -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc printResult.o  c.o -o printResult

tips:在gdb中可以时候用print $st0来打印寄存器中 值。

第64部分- Linux x86 64位汇编 FPU之一介绍

FPU是一个自持单元,使用与标准处理器寄存器分离的一组寄存器处理浮点操作。包括8个80位数据寄存器和3个16位寄存器(用于状态,控制和标记)。

FPU数据寄存器称为R0到R7,它们连接在一起形成一个堆栈(R7是顶端就是st0寄存器)。

状态寄存器

FPU有自己的状态寄存器,包含在一个16位寄存器中。可以通过fstsw指令来读取。

控制寄存器

控制FPU内的浮点功能,用于计算浮点值的精度以及用于舍入浮点结果的方法。可以通过fstcw把控制寄存器进行加载。

精度控制可以设置为四位

00 单精度

01 未使用

10 双精度

11 扩展双精度(默认为扩展双精度)

舍入控制

00 舍入到最近值(默认舍入到最近值)

01 向下舍入

10 向上舍入

11 向零舍入

示例

 
  1. .section .data
  2. newvalue:
  3. .byte 0x7f, 0x00
  4. .section .bss
  5. .lcomm control, 2
  6. .section .text
  7. .globl _start
  8. _start:
  9. nop
  10. fstcw control
  11. fldcw newvalue
  12. fstcw control
  13.  
  14. movl $1, %eax
  15. movl $0, %ebx
  16. int $0x80
 

执行过中,查看获取到的寄存器的值。

(gdb) x /x &control

0x6000d6 <control>:     0x00000000

(gdb) s

(gdb) x /x &control

0x6000d6 <control>:     0x0000037f

(gdb) s

(gdb) s

(gdb) x /x &control

0x6000d6 <control>:     0x0000007f

 

标记寄存器

标记寄存器用于标识8个80位FPU数据寄存器中的值。

每个标记值对应一个物理的FPU寄存器。每个寄存器对应2位值可以表明寄存器内容的4个特殊代码之一。

00 一个合法的扩展双精度

01 零值

10 特殊的浮点值

11 无内容

 

FPU基本使用例子

 
  1. .section .data;//定义数据段
  2. value1:
  3. .int 40
  4. value2:
  5. .float 92.4405
  6. value3:
  7. .double 221.440321
  8. .section .bss
  9. .lcomm int1, 4
  10. .lcomm control, 2
  11. .lcomm status, 2
  12. .lcomm result, 4
  13. .section .text
  14. .globl _start
  15. _start:
  16. nop
  17. finit;//初始化FPU
  18. fstcw control;//控制器寄存器
  19. fstsw status;//状态寄存器
  20. filds value1;//把一个双字整数值加载到FPU寄存器堆栈中,st0
  21. fists int1;//获取寄存器堆栈顶部的值,并存放到目标位置
  22. flds value2;//加载values2的单精度浮点值到FPU寄存器堆栈中
  23. fldl value3;// 加载values3的双精度浮点值到FPU寄存器堆栈中
  24. fst %st(4) ;//将st0寄存器传送到st4
  25. fxch %st(1) ;//交换st0和st1
  26. fstps result;//将st0加载到result,并弹出st0.
  27. movl $1, %eax
  28. movl $0, %ebx
  29. int $0x80
 

as -o stacktest.o stacktest.s

ld -o stacktest stacktest.o

执行后如下:

(gdb) i r st0

st0 221.440321000000011509   (raw 0x4006dd70b8e086bdf800)

(gdb) i r st1

st1 92.44049835205078125     (raw 0x4005b8e1890000000000)

(gdb) i r st2

st2 40       (raw 0x4004a000000000000000)

第65部分- Linux x86 64位汇编 FPU之二浮点基本运算

FPU提供对浮点值执行基本数学功能的指令。

例如:

FADD source: 内存中32位或64位和ST0寄存器相加。

FADD %st(x),%st(0): st(x)和st(0)相加,结果存于st(0)

FADD %st(0),%st(x): st(x)和st(0)相加,结果存于st(x)

FADDP %st(0),%st(x): st(x)和st(0)相加,结果存于st(x),并弹出st(0)

在GNU汇编器中,指定内存中的值的指令的助记符要包含长度(s用于32位单精度浮点值,l用于双精度浮点值)

示例

我们来计算一个表达式:

((43.65/22+(76.34*3.1))/((12.43*6)-(140.2/94.21))

具体思路如下:

  1. 把43.65加载到st0
  2. st0除以22,结果保存到st0
  3. 把76.34加载st0(步骤2结果移动到st1)
  4. 把3.1记载到st0(76.34移动到st1,步骤2结果移动到st2)
  5. St0和st1相乘,存放于st0
  6. St0和st2相加,存放于st0。表达式的分子部分完成结果。
  7. 把12.43加载到st0,步骤六结果移动到st1
  8. St0乘以6,存放在st0
  9. 把140.2加载到st0,步骤8移动到st1,步骤6移动到st2
  10. 把94.21加载到st0,步骤8移动到st2,步骤6移动到st3
  11. St1/st0,弹出堆栈并把结果保存到st0(步骤8答案移到st1,步骤6移动到st2)
  12. St1-st0,保存到st0. 表达式的分目部分完成结果。
  13. St2除以st0,结果保存到st0中。完成。
 
  1. .extern printf ;//调用外部的printf函数
  2. .section .data
  3. fmt: .ascii "result is: %f \n"
  4. value1:
  5. .float 43.65
  6. value2:
  7. .int 22
  8. value3:
  9. .float 76.34
  10. value4:
  11. .float 3.1
  12. value5:
  13. .float 12.43
  14. value6:
  15. .int 6
  16. value7:
  17. .float 140.2
  18. value8:
  19. .float 94.21
  20. result: .double 0.0
  21.  
  22. .section .text
  23. .globl _start
  24. _start:
  25. nop
  26. finit;//初始化fpu
  27. flds value1;//1.加载value1 43.65到st0
  28. fidiv value2;// 2.st0除以22,结果保存到st0
  29. flds value3;//3.把76.34加载st0(步骤2结果移动到st1)
  30. flds value4;// 4.把3.1记载到st0(76.34移动到st1,步骤2结果移动到st2)
  31. fmul %st(1), %st(0) ;//5. St0和st1相乘,存放于st0
  32. fadd %st(2), %st(0) ;//6. St0和st2相加,存放于st0。表达式的分子部分完成结果。
  33. flds value5;// 7.把12.43加载到st0,步骤六结果移动到st1
  34. fimul value6;//8. St0乘以6,存放在st0
  35. flds value7;//9. 把140.2加载到st0,步骤8移动到st1,步骤6移动到st2
  36. flds value8;//10. 把94.21加载到st0,步骤8移动到st2,步骤6移动到st3
  37. fdivrp;//11. 反向除法,St1/st0,弹出堆栈并把结果保存到st0(步骤8答案移到st1,步骤6移动到st2)
  38. fsubr %st(1), %st(0) ;//12. 反向减法,St1-st0,保存到st0. 表达式的分目部分完成结果。
  39. fdivr %st(2), %st(0) ;//13.反向触发,St2除以st0,结果保存到st0中。完成。
  40. fstpl result ;//st0加载到result目标中
  41.  
  42. mov $fmt,%rdi
  43. movq result,%xmm0
  44. call printf
  45.  
  46. mov $60,%rax
  47. syscall
 

FIDIV指令在执行除法之前将整数源操作数转换为双精度扩展浮点数格式。

FDIVR是反向除法。

编译连接:

as -g -o fpmath1.o fpmath1.s

ld -o fpmath1 fpmath1.o -lc -I /lib64/ld-linux-x86-64.so.2

对应的图示结果如下:

第66部分- Linux x86 64位汇编 FPU之三高级浮点运算

除了简单的加减乘除外,还有许多浮点运算。

浮点除法例子

 
  1. .section .data
  2. fmt: .ascii "result is: %f \n"
  3.  
  4. value1:
  5. .float 20.65
  6. value2:
  7. .float 3.97
  8.  
  9. .section .bss
  10. .lcomm result, 8
  11. .section .text
  12. .globl _start
  13. _start:
  14. nop
  15. finit;//初始化fpu
  16. flds value2;//加载单精度值value2到st0
  17. flds value1;//加载单精度值value1到st0,value2移到st1
  18. loop:
  19. fprem1;//执行简单的浮点除法
  20. fstsw %ax;//加载fpu状态寄存器
  21. testb $4, %ah;//检测条件代码位2(第10位),如果被设置了test会生产非零值,需要迭代时设置C2位,当迭代完成时,就清空C2位。
  22. jnz loop
  23.  
  24. mov $60,%rax
  25. syscall
 

as -g -o premtest.o premtest_att.s

ld -o premtest premtest.o

第67部分- Linux x86 64位汇编 FPU之三角函数

一般的三角函数,比如正弦,余弦和正切等都可以轻松的通过FPU来计算得到。

正弦的指令是fsin,余弦是fcos.同时获得正弦值和余弦值可以使用FSINCOS.

正切和反正切的指令是FPTAN和FPATAN。

FPATAN指令使用两个隐含的源操作数,计算角值ST1/ST0的反正切值。把值保存在ST1,然后弹出ST0,值移动到ST0位置。

不过就是在使用三角函数之前,需要把值转换为弧度。

弧度=(角度*pi)/180

 

正弦示例

.extern printf ;//调用外部的printf函数
.section .data
   fmt: .ascii  "result is: %f \n"
degree1:
   .float 30.0
val180:
   .int 180
.section .bss
   .lcomm radian1, 4
   .lcomm result1, 8
   .lcomm result2, 8
.section .text
.globl _start
_start:
   nop
   finit;//初始化fpu
   flds degree1;//加载角度90
   fidivs val180;//除以180,保存在st0
   fldpi;//加载π到st1
   fmul %st(1), %st(0) ;//乘以π得到弧度,在st0中。
   fsts radian1;//保存弧度到radian1中
   fsin;//求正弦,保存在st0
   fstl result1;//正弦结果保存到result1中。
 
   movq $fmt,%rdi;//调用printf函数
   movq result1,%xmm0
   call printf
 
   flds radian1;//加载弧度到st0
   fcos;//计算余弦,
   fstl result2;//
 
   movq $fmt,%rdi
   movq result2,%xmm0
   call printf
 
   movq $60,%rax
   syscall

as -g -o trigtest.o trigtest_att.s

ld -o trigtest trigtest.o -lc -I /lib64/ld-linux-x86-64.so.2

正弦余弦示例

.extern printf ;//调用外部的printf函数
.section .data
   fmt: .ascii  "result is: %f \n"
degree1:
   .float 30.0
val180:
   .int 180
.section .bss
   .lcomm sinresult, 8
   .lcomm cosresult, 8
.section .text
.globl _start
_start:
   nop
   finit
   flds degree1
   fidivs val180
   fldpi
   fmul %st(1), %st(0)
   fsincos
   fstpl cosresult
movq $fmt,%rdi
   movq cosresult,%xmm0
   call printf
 
   fstl sinresult
   movq $fmt,%rdi
   movq sinresult,%xmm0
   call printf
 
   movq $60,%rax
   syscall

as -g -o sincostest.o sincostest.s

ld -o sincostest sincostest.o -lc -I /lib64/ld-linux-x86-64.so.2

第68部分- Linux x86 64位汇编 FPU之对数函数

对数函数提供用于底数为2的对数计算。

FYL2X

执行如下:

ST(1)*log2(st(0))

FYL2X1

执行如下:

ST(1)*log2(st(0)+1.0)

FSCALE指令计算ST(0)乘以ST(1)次乘方

示例-fscale

计算:

10*2210*2-2

.extern printf ;//调用外部的printf函数
.section .data
   fmt: .ascii  "result is: %f \n"
value:
   .float 10.0
scale1:
   .float 2.0
scale2:
   .float -2.0
.section .bss
   .lcomm result1, 8
   .lcomm result2, 8
.section .text
.globl _start
_start:
   nop
   finit;//初始化fpu
   flds scale1;//加载scale1值到st0
   flds value;//加载10值到st0,scale1移动到st1
   fscale;//执行fscale,10*22
   fstl result1;//将结果报错到result1中
   movq $fmt,%rdi
   movq result1,%xmm0
   call printf
 
   flds scale2;//加载sacle2到st0
   flds value;//记载value到st0,scale2移动到st1
   fscale;//执行fscale,10*2-2
   fstl result2;//将结果报错到result2中
movq $fmt,%rdi
   movq result2,%xmm0
   call printf
 
   movq $60,%rax
   syscall
 

as -g -o fscaletest.o fscaletest.s

ld -o fscaletest fscaletest.o -lc -I /lib64/ld-linux-x86-64.so.2

示例—求对数

计算log (base b) X=(1/log(base 2) b) * log ( base 2) X

即log1012= log212/log210

.extern printf ;//调用外部的printf函数
.section .data
   fmt: .ascii  "result is: %f \n"
value:
   .float 12.0
base:
   .float 10.0
.section .bss
   .lcomm result, 8
.section .text
.globl _start
_start:
   nop
   finit;//初始化fpu
   fld1;//fld1加载1.0到st0
   flds base;//加载base到st0,1.0移动到st1
   fyl2x;//执行 ST(1)*log2(st(0)),即1*log2(10)
   fld1;// 加载1到st0中,刚才的结果移动到st1
   fdivp;// 执行st0/st1,即1/log2(10)
   flds value;//加载12到st0,上步结果1/log2(10)保存到st1
   fyl2x;// ST(1)*log2(st(0)),即1/log2(10)*log2(12)=log10(12)
   fstl result;//加载结果到result
   movq $fmt,%rdi
   movq result,%xmm0
   call printf
 
   movq $60,%rax
   syscall

as -g -o logtest.o logtest_att.s

ld -o logtest logtest.o -lc -I /lib64/ld-linux-x86-64.so.2

第69部分- Linux x86 64位汇编 FPU之浮点条件分支

浮点的比较不像整数那么容易,在处理整数时候,容易使用CMP指令并且评估EFLAGS寄存器中的值来确定是否大于,等于还是小于。

浮点数,不能使用CMP指令,FPU提供了一些自己的指令来比较浮点值。

FCOM比较指令

比较的结果设置在状态寄存器的C0,C2和C3条件代码位中。

需要使用FSTSW指令把状态寄存器的值复制到AX寄存器,然后使用test指令判断比较结果。

示例

 
  1. .extern printf ;//调用外部的printf函数
  2. .section .data
  3. fmtg: .asciz "greate \n"
  4. fmtb: .asciz " less \n"
  5.  
  6. value1:
  7. .float 100.923
  8. value2:
  9. .float 400.5532
  10. .section .text
  11. .globl _start
  12. _start:
  13. nop
  14. flds value1;//加载value1 10.923到st0
  15. fcoms value2;// st0value2 4.5532对比。
  16. fstsw;//加载状态寄存器到AX
  17. sahf;//加载AH寄存器到EFLAGS中。
  18. ja greater;//大于则跳转到greater
  19. jb lessthan;//小于则跳转到lessthan
  20. greater:
  21. movq $fmtg,%rdi
  22. call printf
  23. jmp exit
  24.  
  25. lessthan:
  26. movq $fmtb,%rdi
  27. call printf
  28. exit:
  29. movq $60,%rax
  30. syscall
 

as -g -o fcomtest.o fcomtest_att.s

ld -o fcomtest fcomtest.o -lc -I /lib64/ld-linux-x86-64.so.2

这里有指令:

LAHF(加载状态标志位到 AH)指令将 EFLAGS 寄存器的低字节复制到 AH。

被复制的标志位包括:符号标志位、零标志位、辅助进位标志位、奇偶标志位和进位标志位。

SAHF(保存 AH 内容到状态标志位)指令将 AH 内容复制到 EFLAGS(或 RFLAGS)寄存器低字节。将AH寄存器的第0,2,4,6,7分别传送到进位、奇偶校验、对准、零和符号标志、不影响EFLAGS寄存器的其他位。

FSTSW和SAHF指令组合传送如下:

  • C0位传送到EFLAGS的进位标志
  • C2位传送到EFLAGS的奇偶校验标志
  • C3位传送到EFLAGS的零标志

为后续的JA,JB,JZ指令提供了很好的工作。

 

FCOMI指令

此外,还可以使用FSTSW和SAHF指令组合效果的指令,就是FCOMI。

只能比较FPU寄存器中的值,不能比较FPU寄存器和内存中的值。

FUCOMI和FUCOMP是确保被比较的值是合法的浮点数。

示例

 
  1. .extern printf ;//调用外部的printf函数
  2. .section .data
  3. fmtg: .asciz "greate \n"
  4. fmtb: .asciz " less \n"
  5. value1:
  6. .float 10.923
  7. value2:
  8. .float 4.5532
  9. .section .text
  10. .globl _start
  11. _start:
  12. nop
  13. flds value2;//加载value2 4.5532到st0
  14. flds value1;//加载value1 10.923到st0
  15. fcomi %st(1), %st(0);//比较st1,st0
  16. ja greater
  17. jb lessthan
  18.  
  19. greater:
  20. movq $fmtg,%rdi
  21. call printf
  22. jmp exit
  23.  
  24. lessthan:
  25. movq $fmtb,%rdi
  26. call printf
  27. exit:
  28. movq $60,%rax
  29. syscall
 

as -g -o fcomitest.o fcomitest_att.s

ld -o fcomitest fcomitest.o -lc -I /lib64/ld-linux-x86-64.so.2

 

FCMOV指令

类似整数的CMOV指令,FCMOV指令可以编写浮点值的条件传送。根据EFLAGS寄存器中的值,FCMOV系列的指令把FPU寄存器ST(x)中的源操作数传送到FPU寄存器ST(0)中目标操作数。如果条件为true,就把ST(x)寄存器中值传送到ST(0)寄存器。

常用的方法是在FCMOV之前使用FCOMI

格式是:

Fcmovx source, destination

其中source是ST(x)寄存器,destination是ST(0)寄存器。

示例

 
  1. .extern printf ;//调用外部的printf函数
  2. .section .data
  3. fmt: .ascii "result is: %f \n"
  4. value1:
  5. .float 20.5
  6. value2:
  7. .float 10.90
  8. .section .text
  9. .globl _start
  10. _start:
  11. nop
  12. finit
  13. flds value1;//加载value1 20.5到st0
  14. flds value2;//加载value2 10.90到st0,20.5移动到st1
  15. fcomi %st(1), %st(0);//如果st0小于st1,设置eflags,然后fcmovb会将st1值复制到st0.
  16. fcmovb %st(1), %st(0)
  17.  
  18. fstl result;//加载结果到result
  19. movq $fmtb,%rdi
  20. movq result,%xmm0
  21. call printf
  22. exit:
  23. movq $60,%rax
  24. syscall
 

as -g -o fcmovtest.o fcmovtest_att.s

ld -o fcmovtest fcmovtest.o -lc -I /lib64/ld-linux-x86-64.so.2

 

第70部分- Linux x86 64位汇编 FPU之保存和恢复

FPU数据寄存器用于自己的FPU计算完,还用于MMX技术的计算。

所以涉及到FPU状态的保存和恢复。

FSTENV指令用于把FPU环境存储到一个内存块中。

会保存如下寄存器:

控制寄存器/状态寄存器/标记寄存器/FPU指令指针偏移量/FPU数据指针/FPU最后执行的操作码,并没有保存数据,如果要保存数据,则必须要使用FSAVE指令。

FLDENV指令用于把内存块的值加载会FPU环境中。

FSTENV示例

 
  1. .section .data
  2. value1:
  3. .float 12.34
  4. value2:
  5. .float 56.789
  6. rup:
  7. .byte 0x7f, 0x0b
  8. .section .bss
  9. .lcomm buffer, 28
  10. .section .text
  11. .globl _start
  12. _start:
  13. nop
  14. finit;//初始化FPU,所有stx设置为0
  15. flds value1;//加载value1到st0
  16. flds value2;// 加载value2到st0,value1到st1中。
  17. fldcw rup;//加载控制器内容为0x7f,0x0b
  18. fstenv buffer;//加载FPU环境到buffer
  19. finit;//初始化FPU
  20. flds value2;//重新加载value2到st0
  21. flds value1;// 重新加载value1到st0,value2到st1中。
  22. fldenv buffer;//从buffer中恢复FPU环境
  23.  
  24. movq $60,%rax
  25. syscall
 

as -g -o fpuenv.o fpuenv.s

ld -o fpuenv fpuenv.o

这里例子通过gdb来进行反汇编查看。

通过(gdb) x /28b &buffer来查看buffer中内容。保存之后内容如下:

(gdb) x /28b &buffer

0x600100 <buffer>: 0x7f    0x0b    0xff    0xff    0x00    0x30    0xff    0xff

0x600108 <buffer+8>:   0xff    0x0f    0xff    0xff    0xbb    0x00    0x40    0x00

0x600110 <buffer+16>:  0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

0x600118 <buffer+24>:  0x00    0x00    0xff    0xff

通过info all查看fpu的寄存器。

FSAVE示例

 
  1. .section .data
  2. value1:
  3. .float 12.34
  4. value2:
  5. .float 56.789
  6. rup:
  7. .byte 0x7f, 0x0b
  8. .section .bss
  9. .lcomm buffer, 108
  10. .section .text
  11. .globl _start
  12. _start:
  13. nop
  14. finit
  15. flds value1
  16. flds value2
  17. fldcw rup
  18. fsave buffer;//此处设置断点进行寄存器观察
  19.  
  20. flds value2
  21. flds value1
  22.  
  23. frstor buffer
  24.  
  25. movq $60,%rax ;//此处设置断点进行寄存器观察恢复后
  26. syscall
 

as -g -o fpusave.o fpusave.s

ld -o fpusave fpusave.o

在fsave之前如下:

st0            56.78900146484375   (raw 0x4004e327f00000000000)

st1            12.340000152587890625   (raw 0x4002c570a40000000000)

st2            0    (raw 0x00000000000000000000)

st3            0    (raw 0x00000000000000000000)

st4            0    (raw 0x00000000000000000000)

st5            0    (raw 0x00000000000000000000)

st6            0    (raw 0x00000000000000000000)

st7            0    (raw 0x00000000000000000000)

fctrl          0xb7f   2943

fstat          0x3000  12288

ftag           0xfff   4095

在fsave之后如下,堆栈顶部值移动了:

st0            0    (raw 0x00000000000000000000)

st1            0    (raw 0x00000000000000000000)

st2            0    (raw 0x00000000000000000000)

st3            0    (raw 0x00000000000000000000)

st4            0    (raw 0x00000000000000000000)

st5            0    (raw 0x00000000000000000000)

st6            56.78900146484375   (raw 0x4004e327f00000000000)

st7            12.340000152587890625   (raw 0x4002c570a40000000000)

fctrl          0x37f   895

第71部分- Linux x86 64位汇编 FPU之等待和非等待指令

浮点指令可能生成6种浮点异常。表明运算过程中出现了某些错误,例如零为除数。

大多出浮点指令在执行之前必须等待以便确保前面的指令没有跑出异常。如果出现异常,在执行下一条指令之前必须先处理异常。

还有一种方式,不等待浮点异常的检查,允许程序保存或者复位当前的FPU状态,不处理悬而未决的异常。

FPU之优化浮点运算

如何优化浮点代码:

  • 确保浮点值不会上溢和下溢
  • 精度控制设置为单精度
  • 使用查找表实现三角函数
  • 断开依赖链,例如不计算Z=A+b+c+d,而是计算x=a+b,y=c+d
  • 在FPU寄存器中尽可能多保留方程式的值
  • 尽可能使用FCOMI,而不是FCOM。
  • 涉及整数和浮点,将整数加载到FPU寄存器中,比对整数使用浮点指令要快。

第72部分- Linux x86 64位汇编调用汇编库

如果希望汇编语言函数和C以及C++程序一起工作,必须显式的遵守C样式的函数格式。

汇编语言函数的源代码文件包含在编译器命令行中。

例如:

gcc -o exe xx.c xx.s xx2.s xx3.s

创建汇编语言函数的目标文件时,不必使用ld命令连接代码,因为本身可能缺少_start标签。使用as进行汇编即可。

一起编译C文件和汇编文件

汇编文件asmfunc.s如下:

 
  1. .section .data
  2. testdata:
  3. .ascii "This is a test message from the asm function\n"
  4. datasize:
  5. .int 45
  6. .section .text
  7. .type asmfunc, @function
  8. .globl asmfunc
  9. asmfunc:
  10.  
  11. movl $1, %rax;//write系统调用
  12. movl $1, %rdi;//stdout
  13. movl $testdata, %rsi;//字符串
  14. movl datasize, %rdx;//字符串长度
  15. syscall;//系统调用
  16.  
  17. ret
 

C文件mainprog.c如下:

 
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. printf("This is a test.\n");
  6. asmfunc();
  7. printf("Now for the second time.\n");
  8. asmfunc();
  9. printf("This completes the test.\n");
  10. return 0;
  11. }
 

进行编译连接如下:

# gcc -o mainprog mainprog.c asmfunc.s -no-pie

产生一个完整的可执行程序文件。

这里使用了参数-no-pie,因为不用会报错:

`.data' can not be used when making a PIE object;

tips:默认编译器是PIE(position independent executable)对象的,在支持它的目标上生成与位置无关的可执行文件, 生成一个可以在任意基地址处加载的可执行文件,而不是其加载地址在ld时间固定的“普通”可执行文件。PIE通常被认为是一种强化机制(允许地址随机化来影响主程序中代码和数据的地址),但它也可以具有其他用途,例如使二进制文件更适合于无MMU的系统。

这里-no-pie表示非PIE对象。

PIC和PIE

位置无关的代码(PIC)和位置无关的可执行文件(PIE)并不是什么新鲜事物,但是编译开关,可以忽略,直到我们不能使用为止。

位置无关代码(PIC)是很老的东西。 早在我们拥有MMU和分页之前,所有进程都必须共享物理地址空间,可以确保可以在任何地址加载程序。 当CPU演进并且每个进程可以将其自己的逻辑地址空间任意映射到任何物理地址时,PIC不再是必需的。

早期被教导要在构建.so文件时放置-fPIC的原因是为了确保多个进程可以使用同一代码,这些进程会将同一物理内存映射到各种虚拟地址。 实际上,它消除了所有绝对寻址,而用相对寻址或可以为每个进程分叉的小型跳转表代替它。

在2000年代初之前,PIC仅限于共享库。 可执行文件仍使用绝对寻址,不是PIC时可以依赖PIC,反之则不然。 随着地址空间布局随机化(ASLR)的出现,也使可执行文件与位置无关也变得有意义,这样攻击者就无法预测其内存映射,从而使缓冲区溢出漏洞更加复杂.

现在的操作系统将检查可执行文件是否与位置无关(PIE),如果启用,则启用ASLR。 默认情况下,编译器可能会或可能不会强制执行此操作,具体取决于您的系统。

实际上,位置独立共享库是从使用-fPIC构建的对象创建的,而位置独立可执行文件是从通过-fPIE构建的对象创建的。

这里需要注意的是如下几点:

  • 可以从PIC或PIE对象(.o)和PIC或PIE静态库(.a)创建位置独立PIE的可执行文件
  • 只能从PIC对象或静态库创建PIC共享库
  • 可以从任何对象或静态库中创建非PIE可执行文件1

Linux下编译共享库时,必须加上-fPIC参数,否则在链接时会有错误提示。

如果-fPIE和-shared同时使用,生成的结果即可作为动态库,也可作为可执行程序。作为动态库时,必须满足:不存在main函数且模块中不存在对外输出的全局变量。这是因为-fPIE默认总是将生成的位置无关代码看作是属于程序本身。

PIC是在生成动态链接库时使用(Linux中的so),PIE是在生成可执行文件时使用。

 

反汇编

使用反汇编查看二进制文件

objdump -D mainprog

可以看到有一个段是main,这个段包含来实现C程序代码在系统上生成的汇编语言代码。

00000000004004e7 <main>:

  4004e7:       55                      push   %rbp

  4004e8:       48 89 e5                mov    %rsp,%rbp

  4004eb:       48 8d 3d e2 00 00 00    lea    0xe2(%rip),%rdi        # 4005d4 <_IO_stdin_used+0x4>

  4004f2:       e8 f9 fe ff ff          callq  4003f0 <puts@plt>

  4004f7:       b8 00 00 00 00          mov    $0x0,%eax

  4004fc:       e8 29 00 00 00          callq  40052a <asmfunc>

  400501:       48 8d 3d dc 00 00 00    lea    0xdc(%rip),%rdi        # 4005e4 <_IO_stdin_used+0x14>

  400508:       e8 e3 fe ff ff          callq  4003f0 <puts@plt>

  40050d:       b8 00 00 00 00          mov    $0x0,%eax

  400512:       e8 13 00 00 00          callq  40052a <asmfunc>

  400517:       48 8d 3d df 00 00 00    lea    0xdf(%rip),%rdi        # 4005fd <_IO_stdin_used+0x2d>

  40051e:       e8 cd fe ff ff          callq  4003f0 <puts@plt>

  400523:       b8 00 00 00 00          mov    $0x0,%eax

  400528:       5d                      pop    %rbp

  400529:       c3                      retq

 

000000000040052a <asmfunc>:

  40052a:       48 c7 c0 01 00 00 00    mov    $0x1,%rax

  400531:       48 c7 c7 01 00 00 00    mov    $0x1,%rdi

  400538:       48 c7 c6 30 10 60 00    mov    $0x601030,%rsi

  40053f:       48 8b 14 25 5d 10 60    mov    0x60105d,%rdx

  400546:       00

第一列是程序内存空间中的内存位置。第二列显示汇编语言代码生产的指令代码。

可以看到另一个段就是asmfunc。我们编译的时候是合在一起了。

第73部分- Linux x86 64位汇编 反汇编objdump

通过objdump可以进行反汇编操作。

通过objdump -d xxx可以查看二进制目标程序。

准备一个经典代码如下,我们进行编译,gcc -S disass.c  -o disass.s,得到汇编代码。

#include <unistd.h>
int main(void) {
	write(1, "Hello World\n", 15);
	return 0;
}

得到的汇编代码如下:

        .file   "disass.c"
        .text
        .section        .rodata
.LC0:
        .string "Hello World\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $15, %edx
        leaq    .LC0(%rip), %rsi
        movl    $1, %edi
        call    write@PLT
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
        .section        .note.GNU-stack,"",@progbits

也可以编译成二进制文件后通过命令objdump命令进行反汇编。

 

第74部分- Linux x86 64位汇编hexdump

通过hexdump -C xx 可以看到二进制文件具体16进制和字符串显示情况。

例如下:

00000070  b8 01 00 00 00 cd 80 55  89 e5 83 ec 04 8b 5d 08  |.......U......].|

00000080  8b 4d 0c 89 5d fc 83 f9  01 74 0c 8b 45 fc 0f af  |.M..]....t..E...|

00000090  c3 89 45 fc 49 eb ef 8b  45 fc 89 ec 5d c3 00 00  |..E.I...E...]...|

000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

000000b0  00 00 00 00 54 80 04 08  00 00 00 00 03 00 01 00  |....T...........|

000000c0  01 00 00 00 00 00 00 00  00 00 00 00 04 00 f1 ff  |................|

000000d0  1e 00 00 00 77 80 04 08  00 00 00 00 02 00 01 00  |....w...........|

000000e0  09 00 00 00 86 80 04 08  00 00 00 00 00 00 01 00  |................|

000000f0  1a 00 00 00 97 80 04 08  00 00 00 00 00 00 01 00  |................|

00000100  13 00 00 00 54 80 04 08  00 00 00 00 10 00 01 00  |....T...........|

00000110  24 00 00 00 9e 90 04 08  00 00 00 00 10 00 01 00  |$...............|

00000120  30 00 00 00 9e 90 04 08  00 00 00 00 10 00 01 00  |0...............|

00000130  37 00 00 00 a0 90 04 08  00 00 00 00 10 00 01 00  |7...............|

00000140  00 70 6f 77 65 72 2e 6f  00 70 6f 77 65 72 5f 6c  |.power.o.power_l|

00000150  6f 6f 70 5f 73 74 61 72  74 00 65 6e 64 5f 70 6f  |oop_start.end_po|

00000160  77 65 72 00 5f 5f 62 73  73 5f 73 74 61 72 74 00  |wer.__bss_start.|

00000170  5f 65 64 61 74 61 00 5f  65 6e 64 00 00 2e 73 79  |_edata._end...sy|

00000180  6d 74 61 62 00 2e 73 74  72 74 61 62 00 2e 73 68  |mtab..strtab..sh|

00000190  73 74 72 74 61 62 00 2e  74 65 78 74 00 00 00 00  |strtab..text....|

000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

第75部分- Linux x86 64位汇编 kdbg

在ubnut环境中通过apt install kdbg进行安装。

即可使用。

kdbg是一个带有图形化界面的调试器,可以在程序的任何地方暂停并观察变量值。

可以直接选择二进制文件进行debug, 文件编译的时候要加上-g参数,这样源码文件可以获取的。

 

F5(Run运行)使程序运行到结束或到达断点为止。 可以根据需要在程序中放置任意多个断点。

F8(Step Into)在程序中执行一行。 如果该行是函数调用,则跳至函数的第一行。

F10(Step Over)在程序中执行一行。 如果该行是函数调用,则整个函数将一步执行。

F6(Step Out)继续执行,直到返回当前函数或到达函数中的断点为止。

F7(Run to Cursor)将继续执行,直到计算机到达当前选择的源代码行或到达断点为止。 通过单击选择一条线。

编译时候加入-g参数,然后选择可执行文件进行运行。

类似的工具还有ddd图形化工具。

还有cgdb图形化工具, cgdb主要功能是在调试时进行代码的同步显示,增加了调试的方便性,提高了调试效率。

第76部分- Linux x86 64位汇编 CPUID

CPUID指令是一条汇编语言指令。

处理器把厂商字符串返回到ebx,edx和ecx寄存器中。

示例

如下:

 
  1. #cpuid.s Sample program to extract the processor Vendor ID
  2. .section .data
  3. output:
  4. .ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
  5. .section .text
  6. .globl _start
  7. _start:
  8. movl $0, %eax;//选择功能0
  9. cpuid
  10. movl $output, %edi;//写地址到edi寄存器
  11. movl %ebx, 28(%edi) ;//从ebx读结果出来
  12. movl %edx, 32(%edi) ;//从edx读结果出来
  13. movl %ecx, 36(%edi) ;//从ecx读结果出来
  14. movl $4, %eax;//系统调用写到控制台上
  15. movl $1, %ebx
  16. movl $output, %ecx
  17. movl $42, %edx
  18. int $0x80
  19. movl $60, %eax
  20. syscall
 

第77部分- Linux x86 64位汇编 优化编译器代码-O1/-O2/-O3

仅仅使用汇编语言代码替换C或者C++不会必然使得程序执行的更好,因为编译器已经把所有高级语言代码都转化成了汇编语言。

这里的关键是编写比编译器生成的代码更好的汇编语言。当然也可以使用若干优化技巧来指示编译器生成汇编语言代码。我们需要了解如何从编译器生成代码,如何使用各种优化级别,以及利用了什么优化技术。

编译器的-O选项提供了GNU编译器的优化步骤。共有3个级别

-O:提供基础的级别的优化

-O2:提供更加高级的代码优化

-O3:提供最高级别的优化

O1优化

-O1支持如下的优化选项。

                  -fauto-inc-dec

                   -fbranch-count-reg

                   -fcombine-stack-adjustments

                   -fcompare-elim

                   -fcprop-registers

                   -fdce

                   -fdefer-pop

                   -fdelayed-branch

                   -fdse

                   -fforward-propagate

                   -fguess-branch-probability

                   -fif-conversion2

                   -fif-conversion

                   -finline-functions-called-once

                   -fipa-pure-const

                   -fipa-profile

                   -fipa-reference

                   -fmerge-constants

                   -fmove-loop-invariants

                   -freorder-blocks

                   -fshrink-wrap

                   -fshrink-wrap-separate

                   -fsplit-wide-types

                   -fssa-backprop

                   -fssa-phiopt

                   -ftree-bit-ccp

                   -ftree-ccp

                   -ftree-ch

                   -ftree-coalesce-vars

                   -ftree-copy-prop

                   -ftree-dce

                   -ftree-dominator-opts

                   -ftree-dse

                   -ftree-forwprop

                   -ftree-fre

                   -ftree-phiprop

                   -ftree-sink

                   -ftree-slsr

                   -ftree-sra

                   -ftree-pta

                   -ftree-ter

                   -funit-at-a-time

O2优化

-O2优化结合了第一个O1优化的所有优化技术,再加上了很多其他技术。

-fthread-jumps
                   -falign-functions -falign-jumps
                   -falign-loops -falign-labels
                   -fcaller-saves
                   -fcrossjumping
                   -fcse-follow-jumps -fcse-skip-blocks
                   -fdelete-null-pointer-checks
                   -fdevirtualize -fdevirtualize-speculatively
                   -fexpensive-optimizations
                   -fgcse -fgcse-lm
                   -fhoist-adjacent-loads
                   -finline-small-functions
                   -findirect-inlining
                   -fipa-cp
                   -fipa-bit-cp
                   -fipa-vrp
                   -fipa-sra
                   -fipa-icf
                   -fisolate-erroneous-paths-dereference
                   -flra-remat
                   -foptimize-sibling-calls
                   -foptimize-strlen
                   -fpartial-inlining
                   -fpeephole2
                   -freorder-blocks-algorithm=stc
                   -freorder-blocks-and-partition -freorder-functions
                   -frerun-cse-after-loop
                   -fsched-interblock -fsched-spec
                   -fschedule-insns -fschedule-insns2
                   -fstore-merging
                   -fstrict-aliasing -fstrict-overflow
                   -ftree-builtin-call-dce
                   -ftree-switch-conversion -ftree-tail-merge
                   -fcode-hoisting
                   -ftree-pre
                   -ftree-vrp
                   -fipa-ra

O3优化

O3选项访问编译器提供的最高级别。整合了O1和O2,还提供了非常专门的附加优化技术。

-finline-functions
-funswitch-loops
-fpredictive-commoning
-fgcse-after-reload
-ftree-loop-vectorize
-ftree-loop-distribute-patterns
-fsplit-paths
-ftree-slp-vectorize
-fvect-cost-model
-ftree-partial-pre
-fpeel-loops
-fipa-cp-clone

这些大家可以在

《Using the GNU Compiler Collection》中查看到。

第78部分- Linux x86 64位汇编 创建优化的代码

我们可以用gcc编译器从C程序创建编译器优化后的汇编代码,然后分析优化。

以tempconv.c文件为例,将华氏温度转为摄氏温度。

 
  1. #include <stdio.h>
  2.  
  3. float convert(int deg)
  4. {
  5. float result;
  6. result = (deg - 32.) / 1.8;
  7. return result;
  8. }
  9.  
  10. int main()
  11. {
  12. int i = 0;
  13. float result;
  14. printf(" Temperature Conversion Chart\n");
  15. printf("Fahrenheit Celsius\n");
  16. for(i = 0; i < 230; i = i + 10)
  17. {
  18. result = convert(i);
  19. printf(" %d %5.2f\n", i, result);
  20. }
  21. return 0;
  22. }
 

汇编之后得到汇编代码:

gcc -S tempconv.c

编译器产生汇编代码

汇编代码如下,我们将其进行了注释。:

 
  1. .file "tempconv.c";//源文件名字
  2. .text ;//定义代码段,是只读和可执行的,后面那些指令都属于.text段。
  3. .globl convert ;//定义函数符号,.globl指示告诉汇编器,这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号
  4. .type convert, @function ;//定义convert函数
  5. convert:
  6. .LFB0:
  7. .cfi_startproc ;//函数开始符号
  8. pushq %rbp;//压栈rbp保存
  9. .cfi_def_cfa_offset 16;// CFA(Canonical Frame Address),CFA定义为调用站点上的前一帧的堆栈指针的值, CFA现在与当前堆栈指针的偏移量为16个字节。CFI是CFI代表调用帧信息.
  10. .cfi_offset 6, -16;//CFI指令用于调试。 它允许调试器展开堆栈。寄存器的先前值保存在与CFA偏移的位置。register 6
  11. movq %rsp, %rbp;//复制当前rsp到rbp中
  12. .cfi_def_cfa_register 6;// .cfi_def_cfa_register修改用于计算CFA的规则。将使用寄存器而不是旧的寄存器。 偏移量保持不变。register 6
  13. movl %edi, -20(%rbp) ;//复制edi到rbp中,在堆栈下方空地处,这里就是函数参数
  14. cvtsi2sd -20(%rbp), %xmm0;//将参数,从 1个双字有符号整数变成1个双精度浮点数,到xmm0中。
  15. movsd .LC0(%rip), %xmm1;//将双字节32传送到xmm1中。
  16. subsd %xmm1, %xmm0
  17. movsd .LC1(%rip), %xmm1;//将双字节1.8传送到xmm1中。
  18. divsd %xmm1, %xmm0;//除以1.8
  19. cvtsd2ss %xmm0, %xmm2;// 将双精度转换为单精度浮点值
  20.  
  21. movss %xmm2, -4(%rbp);//结果移动到堆栈
  22. movss -4(%rbp), %xmm0;//通过xmm0返回
  23. popq %rbp
  24. .cfi_def_cfa 7, 8
  25. ret ;//函数返回
  26. .cfi_endproc ;//函数结束符号
  27. .LFE0:
  28. .size convert, .-convert;//函数字节数量
  29. .section .rodata;//只读段
  30. .align 8 ;// .align的作用在于对指令或者数据的存放地址进行对齐,有些CPU架构要求固定的指令长度并且存放地址相对于2的幂指数圆整,否则程序无法正常运行,如arm。.align的作用范围只限于紧跟它的那条指令或者数据,而接下来的指令或者数据的地址由上一条指令的地址和其长度决定。
  31. .LC2:
  32. .string " Temperature Conversion Chart" ;//定义字符串
  33. .LC3:
  34. .string "Fahrenheit Celsius" ;//定义字符串
  35. .LC4:
  36. .string " %d %5.2f\n"
  37. .text ;//定义代码段,只读并可执行
  38. .globl main
  39. .type main, @function ;//定义main函数
  40. main:
  41. .LFB1:
  42. .cfi_startproc;//函数开始符号
  43. pushq %rbp;//压栈rbp保存
  44. .cfi_def_cfa_offset 16;// CFA现在与当前堆栈指针的偏移量为16个字节
  45. .cfi_offset 6, -16;//6号寄存器是rbp
  46. movq %rsp, %rbp;//复制当前rsp到rbp中
  47. .cfi_def_cfa_register 6
  48. subq $16, %rsp;//腾出堆栈空地,2格。
  49. movl $0, -8(%rbp) ;//复制本地变量0到堆栈中。
  50. leaq .LC2(%rip), %rdi;//字符串地址赋值给rdi,调用输出函数puts,源代码中的printf
  51. call puts@PLT
  52. leaq .LC3(%rip), %rdi;// 字符串地址赋值给rdi,调用输出函数puts, 源代码中的printf
  53. call puts@PLT
  54. movl $0, -8(%rbp) ;//复制本地变量i=0到堆栈中
  55. jmp .L4
  56. .L5:
  57. movl -8(%rbp), %eax;//复制本地变量i到eax
  58. movl %eax, %edi;//复制本地变量i到edi
  59. call convert;//调用函数convert。
  60. movd %xmm0, %eax;//从xmm0获取结果
  61. movl %eax, -4(%rbp)
  62. cvtss2sd -4(%rbp), %xmm0;// 将单精度转换为双精度浮点值
  63. movl -8(%rbp), %eax;//华氏摄氏度,取出->eax->esi,调用printf
  64. movl %eax, %esi;//华氏设置度整型放于esi.
  65. leaq .LC4(%rip), %rdi;//字符串地址。
  66. movl $1, %eax
  67. call printf@PLT;//调用printf函数
  68. addl $10, -8(%rbp) ;//调用增加10给变量i,即是for中i+10
  69. .L4:
  70. cmpl $229, -8(%rbp);//对比229和-8(%rbp)的变量,就是for循环中的比较
  71. jle .L5;//小于等于就调整到.L5,否则就退出程序。
  72. movl $0, %eax;//移动0到eax,函数返回。
  73. leave;// Leave的作用相当mov esp,ebp和pop ebp。
  74. .cfi_def_cfa 7, 8;//7号寄存器是rsp。实现rsp+8
  75. ret
  76. .cfi_endproc;//函数开始符号
  77. .LFE1:
  78. .size main, .-main;//该函数的函数字节数
  79. .section .rodata;//只读段,不可执行
  80. .align 8
  81. .LC0:;//保存32浮点。
  82. .long 0
  83. .long 1077936128
  84. .align 8
  85. .LC1: ;//保存1.8浮点。
  86. .long 3435973837
  87. .long 1073532108
  88. .ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0";//GCC注释,连接器会移除。
  89. .section .note.GNU-stack,"",@progbits
 

这里的.section是汇编指示(Assembler Directive)或叫做伪操作(Pseudo-operation)

.section指示把代码划分成若干个段(Section),程序被操作系统加载执行时,每个段被加载到不同的地址,操作系统对不同的页面设置不同的读、写、执行权限。

这里的CFA描述如下:

:                :
|    whatever    | <--- CFA
+----------------+
| return address |
+----------------+
| reserved space | <--- %rsp == CFA - 16
+----------------+

使用.cfi_def_cfa_offset指令在调试信息中声明了堆栈指针的更改,并且可以看到CFA现在与当前堆栈指针的偏移量为16个字节。

优化后代码分析

注释结束后,我们看下优化点分析。

#gcc -S -O3 tempconv3.s tempconv.c

 
  1. .file "tempconv.c"
  2. .text
  3. .p2align 4,,15
  4. .globl convert
  5. .type convert, @function
  6. convert:
  7. .LFB23:
  8. .cfi_startproc
  9. pxor %xmm0, %xmm0;//异或清零
  10. cvtsi2sd %edi, %xmm0;//是参数,华氏摄氏度。
  11. subsd .LC0(%rip), %xmm0;//直接减去LC0中的38
  12. divsd .LC1(%rip), %xmm0;//直接除去LC1中的1.8
  13. cvtsd2ss %xmm0, %xmm0;//转化为单精度。
  14. ret
  15. .cfi_endproc
  16. .LFE23:
  17. .size convert, .-convert
  18. .section .rodata.str1.8,"aMS",@progbits,1
  19. .align 8
  20. .LC2:
  21. .string " Temperature Conversion Chart"
  22. .section .rodata.str1.1,"aMS",@progbits,1
  23. .LC3:
  24. .string "Fahrenheit Celsius"
  25. .LC4:
  26. .string " %d %5.2f\n"
  27. .section .text.startup,"ax",@progbits
  28. .p2align 4,,15
  29. .globl main
  30. .type main, @function
  31. main:
  32. .LFB24:
  33. .cfi_startproc
  34. pushq %rbp
  35. .cfi_def_cfa_offset 16
  36. .cfi_offset 6, -16
  37. pushq %rbx;//保存rbx寄存器
  38. .cfi_def_cfa_offset 24
  39. .cfi_offset 3, -24
  40. leaq .LC2(%rip), %rdi
  41. leaq .LC4(%rip), %rbp
  42. xorl %ebx, %ebx;//ebx清零
  43. subq $8, %rsp
  44. .cfi_def_cfa_offset 32
  45. call puts@PLT
  46. leaq .LC3(%rip), %rdi
  47. call puts@PLT
  48. .p2align 4,,10
  49. .p2align 3
  50. .L4:
  51. pxor %xmm0, %xmm0
  52. movl %ebx, %edx
  53. movq %rbp, %rsi
  54. movl $1, %edi
  55. movl $1, %eax
  56. cvtsi2sd %ebx, %xmm0
  57. addl $10, %ebx
  58. subsd .LC0(%rip), %xmm0
  59. divsd .LC1(%rip), %xmm0
  60. cvtsd2ss %xmm0, %xmm0
  61. cvtss2sd %xmm0, %xmm0
  62. call __printf_chk@PLT
  63. cmpl $230, %ebx
  64. jne .L4
  65. addq $8, %rsp
  66. .cfi_def_cfa_offset 24
  67. xorl %eax, %eax
  68. popq %rbx
  69. .cfi_def_cfa_offset 16
  70. popq %rbp
  71. .cfi_def_cfa_offset 8
  72. ret
  73. .cfi_endproc
  74. .LFE24:
  75. .size main, .-main
  76. .section .rodata.cst8,"aM",@progbits,8
  77. .align 8
  78. .LC0:
  79. .long 0
  80. .long 1077936128
  81. .align 8
  82. .LC1:
  83. .long 3435973837
  84. .long 1073532108
  85. .ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
  86. .section .note.GNU-stack,"",@progbits
 

可以通过如下直接汇编执行

#as -o tempconv3.o tempconv3.s

#gcc -o tempconv3 tempconv3.o

我们发现main中其实没有调用convert函数了,被优化掉了,功能被直接嵌入到main函数中了。

而convert函数本身也是被大量精简化了。

 

参考

这里由于细节比较多,加入了一个参考链接,关于cfi的描述。

https://sourceware.org/binutils/docs-2.17/as/CFI-directives.html#CFI-directives

CFI directives in assembly files 

第79部分- Linux x86 64位汇编 优化技巧

优化汇编主要是5中常用的方法:

优化运算/优化变量/优化循环/优化条件分支/优化通用子表达式。

减少运算过程中的局部变量的大量使用。

处理变量有3种方式:使用.data或者.bss段内存中定义变量/堆栈中定义局部变量/使用可用的寄存器。将全局变量存储在FPU中,有个好处是,在使用存储到FPU时候会有固定的延时,但是处理器可以处理其他指令。

优化循环是应用程序中最消耗时间的部分之一。分支对性能的影响可能是灾难性的。预加载到指令缓存中的指令完全失去了利用价值。优化循环,要么是消除要么简化,使用的手段是循环展开。会导致程序变化,具体要看性能提高的收益是否超过程序。

优化条件分支会破坏预加载到指令缓存中的指令,如果采用了没有预测到的分支,就会导致处理器进行额外的工作。代码可以减少实现双重if-then-else语句需要的代码数量。

      通用表达式使用,可以计算一次永久收益,不用反复进行计算。

第80部分- Linux x86 64位汇编 使用文件

我们知道C或者C++进行程序设计有函数fopen/read/write。汇编中如何呢?

汇编语言程序中处理数据文件时必须使用特定的顺序。通过Linux系统调用执行。

打开

先来按下open系统调用如下:

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

通过arch/x86/entry/syscalls/syscall_64.tbl中查看:

2       common  open                    __x64_sys_open

系统调用号是2.

打开模式如下:

例如创建文件并且打开用于读写访问:

可以使用$0102

新数据文件追加到已有的文件,可以使用:

$02002

模式可以入下:
#define S_IRWXU 00700     文件所有者可读可写可执行

#define S_IRUSR 00400     文件所有者可读

#define S_IWUSR 00200     文件所有者可写

#define S_IXUSR 00100     文件所有者可执行

 

#define S_IRWXG 00070     文件用户组可写可读可执行

#define S_IRGRP 00040     文件用户组可读

#define S_IWGRP 00020     文件用户组可写

#define S_IXGRP 00010     文件用户组可执行

#define S_IRWXO 00007     其他用户可写可读可执行

#define S_IROTH 00004     其他用户可读

#define S_IWOTH 00002     其他用户可写

#define S_IXOTH 00001     其他用户可执行

 

    1. read文件

读文件使用系统调用

ssize_t read(int fd, void *buf, size_t count);

系统调用号是0.

写入文件

写入文件使用系统调用write.

ssize_t write(int fd, const void *buf, size_t count);

系统调用号是1.

关闭文件

   int close(int fd);

系统调用号是3.

我们来看下示例如下。

写示例

 
  1. .section .data
  2.  
  3. filename:
  4. .asciz "cpuid.txt"
  5. output:
  6. .asciz "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
  7. .section .bss
  8. .lcomm filehandle, 4
  9. .section .text
  10. .globl _start
  11. _start:
  12. movl $0, %eax
  13. cpuid;//调用cpuid指令
  14. movl $output, %edi;//保存结果到字符串中
  15. movl %ebx, 28(%edi)
  16. movl %edx, 32(%edi)
  17. movl %ecx, 36(%edi)
  18.  
  19. movl $2, %eax;//打开文件,系统调用open
  20. movq $filename, %rdi;//文件名
  21. movq $01101, %rsi;//打开模式,重新创建用于写。
  22. movq $0644, %rdx;//权限。
  23.  
  24. syscall
  25.  
  26. test %eax, %eax;//测试返回值
  27. js badfile
  28. movl %eax, filehandle;//否则返回保存eax文件句柄到filehandle
  29.  
  30. movl $1, %eax;//进行写
  31. movq filehandle, %rdi;//参数句柄
  32. movq $output, %rsi;//参数字符串
  33. movq $42, %rdx;//参数字符串数量
  34. syscall
  35. test %eax, %eax
  36. js badfile
  37.  
  38. movq $3, %rax;//关闭
  39. movq filehandle, %rdi;//对应的句柄
  40. syscall
  41.  
  42. badfile:
  43. movq %rax, %rbx
  44. movq $60, %rax
  45. syscall;//退出
 

as -g -o cpuidfile.o cpuidfile.s

ld -o cpuidfile cpuidfile.o

然后执行,

./cpuidfile可以发现执行成功。

 

读写示例

读后写入文件的示例

 
  1. .section .bss
  2. .lcomm buffer, 10
  3. .lcomm filehandle, 4
  4. .section .text
  5. .globl _start
  6. _start:
  7. nop
  8. movq %rsp, %rbp;//赋值rsp给rbp。
  9. movl $2, %eax;//打开文件open系统调用
  10. movq 16(%rbp), %rdi;//使用输入的参数为文件名字
  11. movq $00, %rsi;//flag为只读
  12. movq $0444, %rdx;//权限为只读
  13. syscall
  14. test %eax, %eax
  15. js badfile
  16. movl %eax, filehandle;//保存文件句柄,给read/write的系统调用使用
  17.  
  18. read_loop:
  19. movl $0, %eax
  20. movq filehandle, %rdi;//文件句柄
  21. movq $buffer, %rsi
  22. movq $10, %rdx;//每次10
  23. syscall
  24. test %eax, %eax
  25. jz done;//读完跳出
  26. js done
  27. movl %eax, %edx
  28.  
  29. movl $1, %eax;//写入系统调用
  30. movq $1, %rdi;//写到stdout
  31. movq $buffer, %rsi;//字符串
  32. syscall
  33. test %eax, %eax
  34. js badfile
  35. jmp read_loop
  36.  
  37. done:
  38. movl $3, %eax
  39. movq filehandle, %rdi
  40. syscall
  41.  
  42. badfile:
  43. movl %eax, %ebx
  44. movl $60, %eax
  45. syscall
 

as -g -o readwrite.o readwrite.s

ld -o readwrite readwrite.o

这里需要有个参数,就是一个文件名字。

将文件中的内容独处后放入到缓存中,然后将将缓存中写到stdout中。每次10个字节,使用更大块的长度可以减少read调用次数提高性能。

 

读后处理写入示例

相比上一个示例,这个示例增加数据处理的步骤。从文件读取数据,然后处理数据,最后把数据写入到另一个文件中。

 
  1. .section .bss
  2. .lcomm buffer, 10
  3. .lcomm infilehandle, 8
  4. .lcomm outfilehandle, 8
  5. .lcomm size, 4
  6. .section .text
  7. .globl _start
  8. _start:
  9. # open input file, specified by the first command line param
  10. movl $2, %eax;//open系统调用
  11. movq 16(%rsp), %rdi;//第一个参数,程序运行后栈指向的分别是参数数量/程序名字
  12. movq $00, %rsi;//只读
  13. movq $0444, %rdx;//权限
  14. syscall
  15. test %rax, %rax
  16. js badfile
  17. movq %rax, infilehandle;//保存输入文件的句柄
  18.  
  19. movl $2, %eax;// open系统调用,打开另一个文件
  20. movq 24(%rsp), %rdi;// 第二个参数
  21. movq $01101, %rsi;//创建新文件,模式是写,
  22. movq $0644, %rdx;//权限
  23. syscall
  24. test %rax, %rax;//出现负数则跳转
  25. js badfile
  26. movq %rax, outfilehandle;//保存输出文件句柄
  27.  
  28. # 从输入文件句柄中读取
  29. read_loop:
  30. movl $0, %eax;// read系统调用
  31. movq infilehandle, %rdi
  32. movq $buffer, %rsi
  33. movq $10, %rdx;//每次读取10个字节
  34. syscall
  35. test %rax, %rax
  36. jz done
  37. js badfile
  38. movl %eax, size
  39.  
  40. movq $buffer,%rdi;//字符串参数指针
  41. movq size,%rsi;// 第二个参数,已读出的字节数量。
  42. call convert;//调用convert函数
  43.  
  44. # 将转换后的数据存放 output file
  45. movl $1, %eax ;// write系统调用
  46. movq outfilehandle, %rdi
  47. movq $buffer,%rsi
  48. syscall
  49. test %rax,%rax
  50. js badfile
  51. jmp read_loop
  52.  
  53. done:;//读取完毕后跳转到done处
  54. movl $3,%eax
  55. movq outfilehandle,%rdi
  56. syscall;//关闭输出文件句柄
  57.  
  58. movl $3,%eax
  59. movq infilehandle,%rdi
  60. syscall;//关闭输入文件句柄
  61.  
  62. badfile:
  63. movl %eax,%ebx
  64. movl $60,%eax
  65. syscall
  66.  
  67. .type convert,@function
  68. convert:
  69. movq %rsi,%rcx;//第二个参数是字符数量,赋值为rcx.
  70. movq %rdi,%rsi;//第二个参数是rdi指向源字符串的内存位置,要赋值给rsi
  71.  
  72. convert_loop:
  73. lodsb;//加载字符串字节到al寄存器,rsi指向源字符串的内存位置
  74. cmpb $0x61,%al;//是否小于0x61,即97,小于则跳过
  75. jl skip
  76. cmpb $0x7a,%al;//是否大于于0x7a,即122,大于也跳过
  77. jg skip
  78. sub $0x20,%al;//改成小写
  79. skip:
  80. stosb;//重新加载al到rdi的字符串,就是源字符串地址。
  81. loop convert_loop
  82. ret
 

as -g -o readchange.o readchange.s

ld -o readchange readchange.o

可以执行如下

#readchange readchange.s out.txt

会将readchange.s文件中的内容转为大写并保存到了Out.txt文件中。

第81部分- Linux x86 64位汇编 内存映射文件

上个例子中,如果执行如下:

#readchange readchange.s readchange.s

则会发现readchange.s会空了。

因为系统不能在读取一个文件的同时把数据写入到同一个文件。

但是很多应用程序中涉及到更新文件,一种方法称为内存映射文件。

系统调用号文件:arch/x86/entry/syscalls/syscall_64.tbl

内存映射文件

内存映射文件调用mmap把部分文件映射到系统的内存中。程序可以使用标准的内存指令访问内存位置,还可以修改,可以在多个进程之间共享内存位置并同时更新。

内存映射文件是保存在内存中的,没有同步到原始文件,如果要同步到原始文件,需要调用msync/munmap系统调用。

mmap

这里涉及到的系统调用是mmap,系统调用号是9.

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

addr是内存中的什么位置映射文件。可以为0,系统选择位置。

length是加载到内存中的字节数量,设置为0,长度为文件长度。如果使用了offset值,则必须是系统页面长度的倍数。例如4k/8K……

prot是内存保护设置,可以是PROT_NONE/PROT_READ/PROT_WRITE/PROT_EXEC。

flags是创建的映射对象的类型,可以是MAP_SHARE/MAP_PRIVATE。

fd是要映射到内存的文件的文件句柄

offset是文件中要复制到内存的数据的起点。

msync

int msync(void *addr, size_t length, int flags);

输入值addr是内存映射文件在内存中的开始位置。调用mmap返回这个值。

Length是要写入原始文件的字节数量。

flags定义如何更新原始文件

            MS_ASYNC:下次可以写入文件时安排更新,并且系统调用返回。

      MS_SYNC:系统调用等待,知道做出更新,然后再返回调用程序。

内存映射文件是不能改动原始文件的长度的。

            系统调用号是26.

 

munmap

int munmap(void *addr, size_t length);

是mmap系统调用的逆向操作。

系统调用号是11.

 

mmap示例

通过系统调用mmap吧整个文件映射到内存中、修改内存映射文件中的数据以及数据写回原始文件。

先通过lseek获取文件的长度。

lseek系统调用号是8.

off_t lseek(int fd, off_t offset, int whence);

整体逻辑如下:

  • 使用读/写访问打开文件
  • 使用函数sizefunc确定文件长度
  • 使用系统调用mmap把文件映射到内存中
  • 把内存映射文件的全部内容转化为大写字母
  • 使用munmap把内存映射文件写入原始文件
  • 关闭原始文件并且推出。

代码如下:

 
  1. .code64;//本来没有这个伪代码,开发过程发现有个movq指令被截断成移动4个字节了,所以加上可以规避这个问题。
  2. .section .bss
  3. .lcomm filehandle, 8
  4. .lcomm size, 8
  5. .lcomm mappedfile, 8
  6. .section .text
  7. .globl _start
  8. _start:
  9. # 获取文件名字并以读写模式打开
  10. movl $2, %eax;//打开系统调用
  11. movq 16(%rsp), %rdi;//获取第一个参数,前两个是参数数量和程序名字
  12. movq $0102, %rsi;//读写访问权限
  13. movq $0644, %rdx;//644文件权限
  14. syscall
  15. test %eax, %eax
  16. js badfile
  17. movl %eax, filehandle;//保存到filehandle中
  18.  
  19. # 寻找文件的大小
  20. movl filehandle,%edi
  21. call sizefunc;//调用函数sizefunc
  22. movl %eax,size;//通过eax返回,并赋值给size中。
  23.  
  24. # 映射文件到内存中,void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  25.  
  26. movq $0,%rdi;//由系统分配内存空间
  27. movq size,%rsi;//长度length
  28. movq $3,%rdx;//prot是 PROT_READ | PROT_WRITE
  29. movq $1,%r10;// flags是MAP_SHARED,函数调用的第四个参数是rcx的,但是系统调用的话就是r10
  30. movq filehandle,%r8;//句柄
  31. movq $0,%r9;//offset
  32. movq $9, %rax;//系统调用mmap
  33. syscall
  34. test %rax, %rax
  35. js badfile
  36. movq %rax, mappedfile;//文件句柄,这里被截断成movl指令了,加上.code64规避
  37.  
  38. # 将内存映射文件中的字符变为大写的
  39. movq mappedfile,%rdi
  40. movq size,%rsi
  41. call convert;//调用convert函数
  42.  
  43. # 使用munmap将修改的文件同步到原来文件中。
  44. movl $11, %eax
  45. movq mappedfile, %rdi
  46. movq size, %rsi
  47. syscall
  48. test %eax, %eax
  49. jnz badfile
  50.  
  51. # 关闭文件句柄
  52. movl $3, %eax
  53. movl filehandle, %edi
  54. syscall
  55.  
  56. badfile:
  57. movl %eax, %ebx
  58. movl $60, %eax
  59. syscall
  60.  
  61. .type sizefunc, @function;//函数sizefunc
  62. sizefunc:
  63. movl $8, %eax;//lseek系统调用号是8
  64. movq $0, %rsi;//第一个参数是rdi,第二个参数是rsi,表示offset,从0开始。
  65. movq $2, %rdx;//第三个参数,到达文件结尾SEEK_END即是2.
  66. syscall
  67. ret
  68.  
  69. .type convert,@function;//convert函数
  70. convert:
  71. movq %rsi,%rcx;//第二个参数是字符数量,赋值为rcx.
  72. movq %rdi,%rsi;//第二个参数是rdi指向源字符串的内存位置,要赋值给rsi
  73.  
  74. convert_loop:
  75. lodsb;//加载字符串字节到al寄存器,rsi指向源字符串的内存位置
  76. cmpb $0x61,%al;//是否小于0x61,即97,小于则跳过
  77. jl skip
  78. cmpb $0x7a,%al;//是否大于于0x7a,即122,大于也跳过
  79. jg skip
  80. sub $0x20,%al;//改成小写
  81. skip:
  82. stosb;//重新加载al到rdi的字符串,就是源字符串地址。
  83. loop convert_loop
  84. ret
 

as -g -o mmapdemo.o mmapdemo.s

ld -o mmapdemo mmapdemo.o

执行如下:

#cp mmapdemo.s mmaptest.s

#./mmapdemo mmaptest.s

在使用gdb调试前,可以使用strace ./mmapdemo mmaptest.s来看下系统调用情况。

这里要注意的是mmap的系统调用第四个参数被放到了r10寄存器了。

 

第82部分- Linux x86 64位汇编 参考

NASM

The Netwide Assembler: NASM

NasmAssembly

https://nasm.us/doc/nasmdoc4.html

 

ATT

《Professional Assembly Language》

 

处理器

SIMD Instructions

 

编译器

GCC官方文档

CPUID

 

函数约定

系统调用约定

 

系统调用约定

 

1、windows 64位调用约定

windows64 位的调用很是奇怪,因为它不是通过栈来传参,而是通过寄存器来传参!

最为常见的是,当参数只有一个的时候,一般是选用rcx来传递参数!

2、Linux 32位调用约定

Linux 32位的系统调用时通过int 80h来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数。下面我们来看看hello world程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.section .data
msg:
.ascii "Hello world!\n"
 
.section .text
 
.globl _start
 
_start:
movl $4, %eax
movl $1, %ebx
movl $msg, %ecx
movl $13, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80

从 /usr/include/asm/unistd.h中可以看到exit的功能号_NR_exit为1,write(_NR_write)功能号为4,因此第一个int 0x80调用之前eax寄存器值为4,ebx为文件描述符,stdout的文件描述符为1,ecx则为buffer的内存地址,edx为buffer长度。第二个int 0x80之前eax为1表示调用exit,ebx为0表示返回0。

系统调用功能好可以参考System Call Number Definition
以及http://asm.sourceforge.net/syscall.html#2
或者 http://syscalls.kernelgrok.com/ 查找(推荐!)

2.1 系统调用及参数传递过程

这部分具体可以参考:
系统调用及参数传递过程
深入理解Linux的系统调用

我们以x86为例说明:
由于陷入指令是一条特殊指令,而且依赖与操作系统实现的平台,如在x86中,这条指令是int 0x80,这显然不是用户在编程时应该使用的语句,因为这将使得用户程序难于移植。所以在操作系统的上层需要实现一个对应的系统调用库,每个系统调用都在该库中包含了一个入口点(如我们看到的fork, open, close等等),这些函数对程序员是可见的,而这些库函数的工作是以对应系统调用号作为参数,执行陷入指令int 0x80,以陷入核心执行真正的系统调用处理函数。当一个进程调用一个特定的系统调用库的入口点,正如同它调用任何函数一样,对于库函数也要创建一个栈帧。而当进程执行陷入指令时,它将处理机状态转换到核心态,并且在核心栈执行核心代码。

这里给出一个示例(linux/include/asm/unistd.h):

1
2
3
4
5
6
7
8
9
10
#define _syscallN(type, name, type1, arg1, type2, arg2, . . . ) \
type name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
. . . . . .
__syscall_return(type,__res); \
}

在执行一个系统调用库中定义的系统调用入口函数时,实际执行的是类似如上的一段代码。这里牵涉到一些gcc的嵌入式汇编语言,不做详细的介绍,只简单说明其意义:
其中\_\NR\##name是系统调用号,如name == ioctl,则为\_\_NR\_ioctl,它将被放在寄存器eax中作为参数传递给中断0x80的处理函数。而系统调用的其它参数arg1, arg2, …则依次被放入ebx, ecx, . . .等通用寄存器中,并作为系统调用处理函数的参数,这些参数是怎样传入核心的将会在后面介绍。

注意该调用是从左至右依次传参,与普通函数的由右往左依次传参不同

3、Linux 64位调用约定

现在,我们将要讨论的是64位的系统调用。不过在此之前,我们需要知道linux的两个有关系统调用的重要文件unistd\_32.h和unistd\_64.h,这两个文件定义了系统调用号!

3.1 每种调用号需要传递哪些参数

在linux系统中某个程序执行时进行的系统调用可以通过strace命令来查看,solaris中对应的命令为dtrace,而mac os x中可以通过类似的dtruss命令来查看。当进程已经处于 D 状态(uninterruptible sleep)时,strace 也帮不上忙。这时候可以通过:

cat /proc//syscall

来查看。(详细内容可以到http://www.jb51.net/article/50923.htm查看)

32位的系统调用表的参数可以到 http://syscalls.kernelgrok.com/ 查找;关于32位系统中使用汇编语言调用syscall table,将在另一篇博文[linux下32位汇编的系统调用]中详述。

而在64位系统中,大神说了:可以通过grep在源代码中查找:

To find the implementation of a system call, grep the kernel tree for SYSCALL_DEFINE.\?(syscall,

For example, to find the read system call:

1
2
3
4
5
illusion:/usr/src/linux-source-3.19$ grep -rA3 'SYSCALL_DEFINE.\?(read,' *
fs/read_write.c:SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
fs/read_write.c-{
fs/read_write.c- struct file *file;
fs/read_write.c- ssize_t ret = -EBADF;

也可以在以下网址中查找:
http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

3.2 调用如何传递参数以及结果如何返回

在64位linux中,一般来说系统调用的参数统统放在寄存器中,最多可以用到6个寄存器;如果多余6个参数的系统调用怎么传递参数?这个还不清楚,有的文档说64位系统调用的参数最多不会超过6个;还有的文档说超过6个参数的话,其余参数全部通过堆栈来传递。超过6个参数的系统调用,本猫没有实际碰到,也不知到底该怎么办!?就这个问题,有兴趣的童鞋可以和本猫单独切磋讨论。

具体调用规则如下:

  1. 用户模式的系统调用依次传递的寄存器为:

    rdi,rsi,rdx,rcx,r8和r9

  2. 内核接口的系统调用一次传递的寄存器为:

    rdi,rsi,rdx,r10,r8和r9

    注意这里和用户模式的系统调用只有第4个寄存器不同,其他都相同。

  3. 系统调用通过syscall指令进入,不像32位下的汇编使用的是int 0x80指令;

  4. 系统调用号放在rax寄存器里;

  5. 系统调用限制最多6个参数,没有参数直接通过栈传递,原话是:

System-calls are limited to six arguments, no argument is passed directly on the stack

  1. 系统调用的返回结果,也就是syscall指令的返回放在rax寄存器中;
  2. 只有整形值和内存型的值可以传递给内核,这个也不十分明白,原话是:

Only values of class INTEGER or class MEMORY are passed to the kernel

有的童鞋可能要问了,要是浮点数怎么传递给接口!?有参数是浮点数的系统调用吗?这个还真不清楚,不过参数是浮点数的C标准库函数的调用那是大大的有,这个等到在另一篇博文[64汇编调用C标准库函数]中再详细给大家解答。

4、Linux系统调用实例

代码的功能很简单,显示一行文本,然后退出。我们使用了syscall中的write和exit调用,查一下前面的调用号和参数,我们初步总结如下:

write(即sys_write)调用号为1,需传递3个参数

1
2
3
unsigned int fd
const char *buf
size_t count

exit(sys_exit)调用号为60,只需传递一个错误码

1
int error_code

如果该值为0表示程序执行成功。

因为以上两个调用最多的也只有3个参数,所以我们依次只会用到3个寄存器rdi,rsi和rdx。

实际代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
section .text
;if use ld
global _start
;if use gcc
;global main
_start:
;main
mov rax,1 ;write NO
mov rdi,1 ;fd
mov rsi,msg ;addr of msg string
mov rdx,msg_len ;lenght of msg string
syscall
 
mov rax,60 ;exit NO
mov rdi,0 ;error_code
syscall
 
msg: db "Hello World!",0xa
msg_len:equ $-msg

编译连接命令如下:

1
2
nasm -f elf64 p.s
ld -o p p.o

如果是mac os x系统下命令如下:

1
2
nasm -f macho64 p.s
ld -o p p.o

如果你要生成32位的代码,在编译时把elf64改为elf32就可以了,不过我前面说过:32位和64位汇编结构变化较大,光改这个是没办法运行成功的。

不出所料代码运行输出一行:Hello World!并且程序返回后用echo $?看,应该为0.

这个例子很简单,下面看一下稍微复杂点的调用:mmap,该调用共有6个参数,我们再一次总结如下:
mmap(sys_mmap) 系统调用号为9,参数分别为:

1
2
3
4
5
6
unsigned long addr
unsigned long len
unsigned long prot
unsigned long flags
unsigned long fd
unsigned long offset

第一个参数是需要映射到的地址,我们这里传0,表示不关心映射到哪;第二个参数是映射空间的大小;第三个参数表示映射区域的保护方式,有很多种,我们这里只让它可读可写即可,所以只用到2者的组合:

PROT_WRITE|PROT_READ

第四个参数是映射区域的一些特性,有很多组合。这里只用MAP_SHARED|MAP_ANONYMOUS,后者表示建立匿名映射,会忽略参数fd,所以不设及文件。

第五个参数就是fd,前面说了可以忽略,所以我们传递-1;最后一个参数是映射的偏移量,我们也传递0.

该调用如果成功返回映射区域内存的起始地址,否则返回MAP_FAILED(-1),错误原因存于errno中,我们可以用strerror(errno)查看具体含义。不过该函数是C库中的,这里我们捎带的用一下。

提到mmap我们不得不提到munmap,用猜大家也知道该调用的含义吧:

munmap(sys_munmap) 调用号为11,需传递2个参数:

1
2
unsigned long start
size_t len

5、函数查询

例如execve的函数查询http://man7.org/linux/man-pages/man2/execve.2.html

6、附录

6.1 32系统调用表

# Name Registers Definition          
    eax ebx ecx edx esi edi  
0 sys_restart_syscall 0x00 - - - - - kernel/signal.c:2058
1 sys_exit 0x01 int error_code - - - - kernel/exit.c:1046
2 sys_fork 0x02 struct pt_regs * - - - - arch/alpha/kernel/entry.S:716
3 sys_read 0x03 unsigned int fd char __user *buf size_t count - - fs/read_write.c:391
4 sys_write 0x04 unsigned int fd const char __user *buf size_t count - - fs/read_write.c:408
5 sys_open 0x05 const char __user *filename int flags int mode - - fs/open.c:900
6 sys_close 0x06 unsigned int fd - - - - fs/open.c:969
7 sys_waitpid 0x07 pid_t pid int __user *stat_addr int options - - kernel/exit.c:1771
8 sys_creat 0x08 const char __user *pathname int mode - - - fs/open.c:933
9 sys_link 0x09 const char __user *oldname const char __user *newname - - - fs/namei.c:2520
10 sys_unlink 0x0a const char __user *pathname - - - - fs/namei.c:2352
11 sys_execve 0x0b char __user * char user *user * char user *user * struct pt_regs * - arch/alpha/kernel/entry.S:925
12 sys_chdir 0x0c const char __user *filename - - - - fs/open.c:361
13 sys_time 0x0d time_t __user *tloc - - - - kernel/posix-timers.c:855
14 sys_mknod 0x0e const char __user *filename int mode unsigned dev - - fs/namei.c:2067
15 sys_chmod 0x0f const char __user *filename mode_t mode - - - fs/open.c:507
16 sys_lchown16 0x10 const char __user *filename old_uid_t user old_gid_t group - - kernel/uid16.c:27
17 not implemented 0x11 - - - - -  
18 sys_stat 0x12 char __user *filename struct old_kernel_stat user *statbuf - - - fs/stat.c:150
19 sys_lseek 0x13 unsigned int fd off_t offset unsigned int origin - - fs/read_write.c:167
20 sys_getpid 0x14 - - - - - kernel/timer.c:1337
21 sys_mount 0x15 char __user *dev_name char __user *dir_name char __user *type unsigned long flags void __user *data fs/namespace.c:2118
22 sys_oldumount 0x16 char __user *name - - - - fs/namespace.c:1171
23 sys_setuid16 0x17 old_uid_t uid - - - - kernel/uid16.c:67
24 sys_getuid16 0x18 - - - - - kernel/uid16.c:212
25 sys_stime 0x19 time_t __user *tptr - - - - kernel/time.c:81
26 sys_ptrace 0x1a long request long pid long addr long data - kernel/ptrace.c:688
27 sys_alarm 0x1b unsigned int seconds - - - - kernel/timer.c:1314
28 sys_fstat 0x1c unsigned int fd struct old_kernel_stat user *statbuf - - - fs/stat.c:174
29 sys_pause 0x1d - - - - - kernel/signal.c:2700
30 sys_utime 0x1e char __user *filename struct utimbuf __user *times - - - fs/utimes.c:27
31 not implemented 0x1f - - - - -  
32 not implemented 0x20 - - - - -  
33 sys_access 0x21 const char __user *filename int mode - - - fs/open.c:356
34 sys_nice 0x22 int increment - - - - kernel/sched.c:4282
35 not implemented 0x23 - - - - -  
36 sys_sync 0x24 - - - - - fs/sync.c:98
37 sys_kill 0x25 int pid int sig - - - kernel/signal.c:2317
38 sys_rename 0x26 const char __user *oldname const char __user *newname - - - fs/namei.c:2765
39 sys_mkdir 0x27 const char __user *pathname int mode - - - fs/namei.c:2130
40 sys_rmdir 0x28 const char __user *pathname - - - - fs/namei.c:2244
41 sys_dup 0x29 unsigned int fildes - - - - fs/fcntl.c:131
42 sys_pipe 0x2a int __user *fildes - - - - fs/pipe.c:1117
43 sys_times 0x2b struct tms __user *tbuf - - - - kernel/sys.c:896
44 not implemented 0x2c - - - - -  
45 sys_brk 0x2d unsigned long brk - - - - mm/mmap.c:245
46 sys_setgid16 0x2e old_gid_t gid - - - - kernel/uid16.c:51
47 sys_getgid16 0x2f - - - - - kernel/uid16.c:222
48 sys_signal 0x30 int sig __sighandler_t handler - - - kernel/signal.c:2683
49 sys_geteuid16 0x31 - - - - - kernel/uid16.c:217
50 sys_getegid16 0x32 - - - - - kernel/uid16.c:227
51 sys_acct 0x33 const char __user *name - - - - kernel/acct.c:274
52 sys_umount 0x34 char __user *name int flags - - - fs/namespace.c:1132
53 not implemented 0x35 - - - - -  
54 sys_ioctl 0x36 unsigned int fd unsigned int cmd unsigned long arg - - fs/ioctl.c:613
55 sys_fcntl 0x37 unsigned int fd unsigned int cmd unsigned long arg - - fs/fcntl.c:429
56 not implemented 0x38 - - - - -  
57 sys_setpgid 0x39 pid_t pid pid_t pgid - - - kernel/sys.c:921
58 not implemented 0x3a - - - - -  
59 sys_olduname 0x3b struct oldold_utsname __user * - - - - kernel/sys.c:1132
60 sys_umask 0x3c int mask - - - - kernel/sys.c:1460
61 sys_chroot 0x3d const char __user *filename - - - - fs/open.c:408
62 sys_ustat 0x3e unsigned dev struct ustat __user *ubuf - - - fs/statfs.c:175
63 sys_dup2 0x3f unsigned int oldfd unsigned int newfd - - - fs/fcntl.c:116
64 sys_getppid 0x40 - - - - - kernel/timer.c:1348
65 sys_getpgrp 0x41 - - - - - kernel/sys.c:1020
66 sys_setsid 0x42 - - - - - kernel/sys.c:1055
67 sys_sigaction 0x43 int sig const struct old_sigaction __user *act struct old_sigaction __user *oact - - arch/mips/kernel/signal.c:300
68 sys_sgetmask 0x44 - - - - - kernel/signal.c:2657
69 sys_ssetmask 0x45 int newmask - - - - kernel/signal.c:2663
70 sys_setreuid16 0x46 old_uid_t ruid old_uid_t euid - - - kernel/uid16.c:59
71 sys_setregid16 0x47 old_gid_t rgid old_gid_t egid - - - kernel/uid16.c:43
72 sys_sigsuspend 0x48 int history0 int history1 old_sigset_t mask - - arch/s390/kernel/signal.c:58
73 sys_sigpending 0x49 old_sigset_t __user *set - - - - kernel/signal.c:2562
74 sys_sethostname 0x4a char __user *name int len - - - kernel/sys.c:1165
75 sys_setrlimit 0x4b unsigned int resource struct rlimit __user *rlim - - - kernel/sys.c:1275
76 sys_old_getrlimit 0x4c unsigned int resource struct rlimit __user *rlim - - - kernel/sys.c:1256
77 sys_getrusage 0x4d int who struct rusage __user *ru - - - kernel/sys.c:1452
78 sys_gettimeofday 0x4e struct timeval __user *tv struct timezone __user *tz - - - kernel/time.c:101
79 sys_settimeofday 0x4f struct timeval __user *tv struct timezone __user *tz - - - kernel/time.c:185
80 sys_getgroups16 0x50 int gidsetsize old_gid_t __user *grouplist - - - kernel/uid16.c:164
81 sys_setgroups16 0x51 int gidsetsize old_gid_t __user *grouplist - - - kernel/uid16.c:187
82 sys_old_select 0x52 struct sel_arg_struct __user *arg - - - - fs/select.c:701
83 sys_symlink 0x53 const char __user *old const char __user *new - - - fs/namei.c:2419
84 sys_lstat 0x54 char __user *filename struct old_kernel_stat user *statbuf - - - fs/stat.c:162
85 sys_readlink 0x55 const char __user *path char __user *buf int bufsiz - - fs/stat.c:311
86 sys_uselib 0x56 const char __user *library - - - - fs/exec.c:107
87 sys_swapon 0x57 const char __user *specialfile int swap_flags - - - mm/swapfile.c:1793
88 sys_reboot 0x58 int magic1 int magic2 unsigned int cmd void __user *arg - kernel/sys.c:368
89 sys_old_readdir 0x59 unsigned int struct old_linux_dirent __user * unsigned int - - fs/readdir.c:105
90 sys_old_mmap 0x5a struct mmap_arg_struct __user *arg - - - - mm/mmap.c:1141
91 sys_munmap 0x5b unsigned long addr size_t len - - - mm/mmap.c:2109
92 sys_truncate 0x5c const char __user *path long length - - - fs/open.c:127
93 sys_ftruncate 0x5d unsigned int fd unsigned long length - - - fs/open.c:178
94 sys_fchmod 0x5e unsigned int fd mode_t mode - - - fs/open.c:436
95 sys_fchown16 0x5f unsigned int fd old_uid_t user old_gid_t group - - kernel/uid16.c:35
96 sys_getpriority 0x60 int which int who - - - kernel/sys.c:216
97 sys_setpriority 0x61 int which int who int niceval - - kernel/sys.c:149
98 not implemented 0x62 - - - - -  
99 sys_statfs 0x63 const char __user * path struct statfs __user *buf - - - fs/statfs.c:102
100 sys_fstatfs 0x64 unsigned int fd struct statfs __user *buf - - - fs/statfs.c:136
101 sys_ioperm 0x65 unsigned long unsigned long int - - not found:
102 sys_socketcall 0x66 int call unsigned long __user *args - - - net/socket.c:2210
103 sys_syslog 0x67 int type char __user *buf int len - - kernel/printk.c:412
104 sys_setitimer 0x68 int which struct itimerval __user *value struct itimerval __user *ovalue - - kernel/itimer.c:279
105 sys_getitimer 0x69 int which struct itimerval __user *value - - - kernel/itimer.c:103
106 sys_newstat 0x6a char __user *filename struct stat __user *statbuf - - - fs/stat.c:237
107 sys_newlstat 0x6b char __user *filename struct stat __user *statbuf - - - fs/stat.c:247
108 sys_newfstat 0x6c unsigned int fd struct stat __user *statbuf - - - fs/stat.c:273
109 sys_uname 0x6d struct old_utsname __user * - - - - kernel/sys.c:1115
110 sys_iopl 0x6e unsigned int struct pt_regs * - - - not found:
111 sys_vhangup 0x6f - - - - - fs/open.c:1008
112 not implemented 0x70 - - - - -  
113 sys_vm86old 0x71 struct vm86_struct __user * struct pt_regs * - - - not found:
114 sys_wait4 0x72 pid_t pid int __user *stat_addr int options struct rusage __user *ru - kernel/exit.c:1726
115 sys_swapoff 0x73 const char __user *specialfile - - - - mm/swapfile.c:1533
116 sys_sysinfo 0x74 struct sysinfo __user *info - - - - kernel/timer.c:1565
117 sys_ipc 0x75 - - - - - ipc/syscall.c:16
118 sys_fsync 0x76 unsigned int fd - - - - fs/sync.c:221
119 sys_sigreturn 0x77 struct pt_regs *regs - - - - arch/alpha/kernel/entry.S:758
120 sys_clone 0x78 unsigned long unsigned long unsigned long unsigned long struct pt_regs * arch/alpha/kernel/entry.S:733
121 sys_setdomainname 0x79 char __user *name int len - - - kernel/sys.c:1214
122 sys_newuname 0x7a struct new_utsname __user *name - - - - kernel/sys.c:1097
123 sys_modify_ldt 0x7b int void __user * unsigned long - - not found:
124 sys_adjtimex 0x7c struct timex __user *txc_p - - - - kernel/time.c:206
125 sys_mprotect 0x7d unsigned long start size_t len unsigned long prot - - mm/mprotect.c:221
126 sys_sigprocmask 0x7e int how old_sigset_t __user *set old_sigset_t __user *oset - - kernel/signal.c:2573
127 not implemented 0x7f - - - - -  
128 sys_init_module 0x80 void __user *umod unsigned long len const char __user *uargs - - kernel/module.c:2611
129 sys_delete_module 0x81 const char __user *name_user unsigned int flags - - - kernel/module.c:720
130 not implemented 0x82 - - - - -  
131 sys_quotactl 0x83 unsigned int cmd const char __user *special qid_t id void __user *addr - fs/quota/quota.c:333
132 sys_getpgid 0x84 pid_t pid - - - - kernel/sys.c:990
133 sys_fchdir 0x85 unsigned int fd - - - - fs/open.c:382
134 sys_bdflush 0x86 int func long data - - - fs/buffer.c:3278
135 sys_sysfs 0x87 int option unsigned long arg1 unsigned long arg2 - - fs/filesystems.c:182
136 sys_personality 0x88 unsigned int personality - - - - kernel/exec_domain.c:191
137 not implemented 0x89 - - - - -  
138 sys_setfsuid16 0x8a old_uid_t uid - - - - kernel/uid16.c:118
139 sys_setfsgid16 0x8b old_gid_t gid - - - - kernel/uid16.c:126
140 sys_llseek 0x8c unsigned int fd unsigned long offset_high unsigned long offset_low loff_t __user *result unsigned int origin fs/read_write.c:191
141 sys_getdents 0x8d unsigned int fd struct linux_dirent __user *dirent unsigned int count - - fs/readdir.c:191
142 sys_select 0x8e int n fd_set __user *inp fd_set __user *outp fd_set __user *exp struct timeval __user *tvp fs/select.c:596
143 sys_flock 0x8f unsigned int fd unsigned int cmd - - - fs/locks.c:1569
144 sys_msync 0x90 unsigned long start size_t len int flags - - mm/msync.c:31
145 sys_readv 0x91 unsigned long fd const struct iovec __user *vec unsigned long vlen - - fs/read_write.c:711
146 sys_writev 0x92 unsigned long fd const struct iovec __user *vec unsigned long vlen - - fs/read_write.c:732
147 sys_getsid 0x93 pid_t pid - - - - kernel/sys.c:1027
148 sys_fdatasync 0x94 unsigned int fd - - - - fs/sync.c:226
149 sys_sysctl 0x95 struct sysctl_args user *args - - - - kernel/sysctl_binary.c:1462
150 sys_mlock 0x96 unsigned long start size_t len - - - mm/mlock.c:491
151 sys_munlock 0x97 unsigned long start size_t len - - - mm/mlock.c:519
152 sys_mlockall 0x98 int flags - - - - mm/mlock.c:556
153 sys_munlockall 0x99 - - - - - mm/mlock.c:584
154 sys_sched_setparam 0x9a pid_t pid struct sched_param __user *param - - - kernel/sched.c:4616
155 sys_sched_getparam 0x9b pid_t pid struct sched_param __user *param - - - kernel/sched.c:4651
156 sys_sched_setscheduler 0x9c pid_t pid int policy struct sched_param __user *param - - kernel/sched.c:4601
157 sys_sched_getscheduler 0x9d pid_t pid - - - - kernel/sched.c:4625
158 sys_sched_yield 0x9e - - - - - kernel/sched.c:4851
159 sys_sched_get_priority_max 0x9f int policy - - - - kernel/sched.c:4989
160 sys_sched_get_priority_min 0xa0 int policy - - - - kernel/sched.c:5014
161 sys_sched_rr_get_interval 0xa1 pid_t pid struct timespec __user *interval - - - kernel/sched.c:5039
162 sys_nanosleep 0xa2 struct timespec __user *rqtp struct timespec __user *rmtp - - - kernel/hrtimer.c:1606
163 sys_mremap 0xa3 unsigned long addr unsigned long old_len unsigned long new_len unsigned long flags unsigned long new_addr mm/mremap.c:510
164 sys_setresuid16 0xa4 old_uid_t ruid old_uid_t euid old_uid_t suid - - kernel/uid16.c:75
165 sys_getresuid16 0xa5 old_uid_t __user *ruid old_uid_t __user *euid old_uid_t __user *suid - - kernel/uid16.c:84
166 sys_vm86 0xa6 unsigned long unsigned long struct pt_regs * - - not found:
167 not implemented 0xa7 - - - - -  
168 sys_poll 0xa8 struct pollfd __user *ufds unsigned int nfds long timeout - - fs/select.c:915
169 sys_nfsservctl 0xa9 int cmd struct nfsctl_arg __user *arg void __user *res - - fs/nfsctl.c:86
170 sys_setresgid16 0xaa old_gid_t rgid old_gid_t egid old_gid_t sgid - - kernel/uid16.c:96
171 sys_getresgid16 0xab old_gid_t __user *rgid old_gid_t __user *egid old_gid_t __user *sgid - - kernel/uid16.c:106
172 sys_prctl 0xac int option unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5 kernel/sys.c:1466
173 sys_rt_sigreturn 0xad struct pt_regs * - - - - arch/alpha/kernel/entry.S:771
174 sys_rt_sigaction 0xae int sig const struct sigaction __user *act struct sigaction __user *oact size_t sigsetsize - kernel/signal.c:2624
175 sys_rt_sigprocmask 0xaf int how sigset_t __user *set sigset_t __user *oset size_t sigsetsize - kernel/signal.c:2111
176 sys_rt_sigpending 0xb0 sigset_t __user *set size_t sigsetsize - - - kernel/signal.c:2171
177 sys_rt_sigtimedwait 0xb1 const sigset_t __user *uthese siginfo_t __user *uinfo const struct timespec __user *uts size_t sigsetsize - kernel/signal.c:2242
178 sys_rt_sigqueueinfo 0xb2 int pid int sig siginfo_t __user *uinfo - - kernel/signal.c:2404
179 sys_rt_sigsuspend 0xb3 sigset_t __user *unewset size_t sigsetsize - - - kernel/signal.c:2710
180 sys_pread64 0xb4 unsigned int fd char __user *buf size_t count loff_t pos - not found:
181 sys_pwrite64 0xb5 unsigned int fd const char __user *buf size_t count loff_t pos - not found:
182 sys_chown16 0xb6 const char __user *filename old_uid_t user old_gid_t group - - kernel/uid16.c:19
183 sys_getcwd 0xb7 char __user *buf unsigned long size - - - fs/dcache.c:2104
184 sys_capget 0xb8 cap_user_header_t header cap_user_data_t dataptr - - - kernel/capability.c:161
185 sys_capset 0xb9 cap_user_header_t header const cap_user_data_t data - - - kernel/capability.c:235
186 sys_sigaltstack 0xba const stack_t __user * stack_t __user * struct pt_regs * - - arch/alpha/kernel/signal.c:199
187 sys_sendfile 0xbb int out_fd int in_fd off_t __user *offset size_t count - fs/read_write.c:897
188 not implemented 0xbc - - - - -  
189 not implemented 0xbd - - - - -  
190 sys_vfork 0xbe struct pt_regs * - - - - arch/alpha/kernel/entry.S:746
191 sys_getrlimit 0xbf unsigned int resource struct rlimit __user *rlim - - - kernel/sys.c:1237
192 sys_mmap_pgoff 0xc0 - - - - - mm/mmap.c:1091
193 sys_truncate64 0xc1 const char __user *path loff_t length - - - not found:
194 sys_ftruncate64 0xc2 unsigned int fd loff_t length - - - not found:
195 sys_stat64 0xc3 char __user *filename struct stat64 __user *statbuf - - - fs/stat.c:358
196 sys_lstat64 0xc4 char __user *filename struct stat64 __user *statbuf - - - fs/stat.c:369
197 sys_fstat64 0xc5 unsigned long fd struct stat64 __user *statbuf - - - fs/stat.c:380
198 sys_lchown 0xc6 const char __user *filename uid_t user gid_t group - - fs/open.c:583
199 sys_getuid 0xc7 - - - - - kernel/timer.c:1359
200 sys_getgid 0xc8 - - - - - kernel/timer.c:1371
201 sys_geteuid 0xc9 - - - - - kernel/timer.c:1365
202 sys_getegid 0xca - - - - - kernel/timer.c:1377
203 sys_setreuid 0xcb uid_t ruid uid_t euid - - - kernel/sys.c:594
204 sys_setregid 0xcc gid_t rgid gid_t egid - - - kernel/sys.c:484
205 sys_getgroups 0xcd int gidsetsize gid_t __user *grouplist - - - kernel/groups.c:203
206 sys_setgroups 0xce int gidsetsize gid_t __user *grouplist - - - kernel/groups.c:232
207 sys_fchown 0xcf unsigned int fd uid_t user gid_t group - - fs/open.c:602
208 sys_setresuid 0xd0 uid_t ruid uid_t euid uid_t suid - - kernel/sys.c:696
209 sys_getresuid 0xd1 uid_t __user *ruid uid_t __user *euid uid_t __user *suid - - kernel/sys.c:746
210 sys_setresgid 0xd2 gid_t rgid gid_t egid gid_t sgid - - kernel/sys.c:761
211 sys_getresgid 0xd3 gid_t __user *rgid gid_t __user *egid gid_t __user *sgid - - kernel/sys.c:800
212 sys_chown 0xd4 const char __user *filename uid_t user gid_t group - - fs/open.c:539
213 sys_setuid 0xd5 uid_t uid - - - - kernel/sys.c:655
214 sys_setgid 0xd6 gid_t gid - - - - kernel/sys.c:531
215 sys_setfsuid 0xd7 uid_t uid - - - - kernel/sys.c:819
216 sys_setfsgid 0xd8 gid_t gid - - - - kernel/sys.c:852
217 sys_pivot_root 0xd9 const char __user *new_root const char __user *put_old - - - fs/namespace.c:2184
218 sys_mincore 0xda unsigned long start size_t len unsigned char __user * vec - - mm/mincore.c:256
219 sys_madvise 0xdb unsigned long start size_t len int behavior - - mm/madvise.c:335
220 sys_getdents64 0xdc unsigned int fd struct linux_dirent64 __user *dirent unsigned int count - - fs/readdir.c:273
221 sys_fcntl64 0xdd unsigned int fd unsigned int cmd unsigned long arg - - fs/fcntl.c:452
222 not implemented 0xde - - - - -  
223 not implemented 0xdf - - - - -  
224 sys_gettid 0xe0 - - - - - kernel/timer.c:1493
225 sys_readahead 0xe1 int fd loff_t offset size_t count - - not found:
226 sys_setxattr 0xe2 const char __user *path const char __user *name const void __user *value size_t size int flags fs/xattr.c:279
227 sys_lsetxattr 0xe3 const char __user *path const char __user *name const void __user *value size_t size int flags fs/xattr.c:298
228 sys_fsetxattr 0xe4 int fd const char __user *name const void __user *value size_t size int flags fs/xattr.c:317
229 sys_getxattr 0xe5 const char __user *path const char __user *name void __user *value size_t size - fs/xattr.c:376
230 sys_lgetxattr 0xe6 const char __user *path const char __user *name void __user *value size_t size - fs/xattr.c:390
231 sys_fgetxattr 0xe7 int fd const char __user *name void __user *value size_t size - fs/xattr.c:404
232 sys_listxattr 0xe8 const char __user *path char __user *list size_t size - - fs/xattr.c:449
233 sys_llistxattr 0xe9 const char __user *path char __user *list size_t size - - fs/xattr.c:463
234 sys_flistxattr 0xea int fd char __user *list size_t size - - fs/xattr.c:477
235 sys_removexattr 0xeb const char __user *path const char __user *name - - - fs/xattr.c:509
236 sys_lremovexattr 0xec const char __user *path const char __user *name - - - fs/xattr.c:527
237 sys_fremovexattr 0xed int fd const char __user *name - - - fs/xattr.c:545
238 sys_tkill 0xee int pid int sig - - - kernel/signal.c:2395
239 sys_sendfile64 0xef int out_fd int in_fd loff_t __user *offset size_t count - fs/read_write.c:916
240 sys_futex 0xf0 - - - - - kernel/futex.c:2605
241 sys_sched_setaffinity 0xf1 pid_t pid unsigned int len unsigned long __user *user_mask_ptr - - kernel/sched.c:4765
242 sys_sched_getaffinity 0xf2 pid_t pid unsigned int len unsigned long __user *user_mask_ptr - - kernel/sched.c:4817
243 sys_set_thread_area 0xf3 struct user_desc __user * - - - - arch/mips/kernel/syscall.c:222
244 sys_get_thread_area 0xf4 struct user_desc __user * - - - - not found:
245 sys_io_setup 0xf5 unsigned nr_reqs aio_context_t __user *ctx - - - fs/aio.c:1245
246 sys_io_destroy 0xf6 aio_context_t ctx - - - - fs/aio.c:1283
247 sys_io_getevents 0xf7 aio_context_t ctx_id long min_nr long nr struct io_event __user *events struct timespec __user *timeout fs/aio.c:1808
248 sys_io_submit 0xf8 aio_context_t long struct iocb user * user * - - fs/aio.c:1711
249 sys_io_cancel 0xf9 aio_context_t ctx_id struct iocb __user *iocb struct io_event __user *result - - fs/aio.c:1746
250 sys_fadvise64 0xfa int fd loff_t offset size_t len int advice - not found:
251 not implemented 0xfb - - - - -  
252 sys_exit_group 0xfc int error_code - - - - kernel/exit.c:1087
253 sys_lookup_dcookie 0xfd u64 cookie64 char __user *buf size_t len - - not found:
254 sys_epoll_create 0xfe int size - - - - fs/eventpoll.c:1215
255 sys_epoll_ctl 0xff int epfd int op int fd struct epoll_event __user *event - fs/eventpoll.c:1228
256 sys_epoll_wait 0x100 int epfd struct epoll_event __user *events int maxevents int timeout - fs/eventpoll.c:1320
257 sys_remap_file_pages 0x101 unsigned long start unsigned long size unsigned long prot unsigned long pgoff unsigned long flags mm/fremap.c:123
258 sys_set_tid_address 0x102 int __user *tidptr - - - - kernel/fork.c:920
259 sys_timer_create 0x103 clockid_t which_clock struct sigevent __user *timer_event_spec timer_t __user * created_timer_id - - kernel/posix-timers.c:522
260 sys_timer_settime 0x104 timer_t timer_id int flags const struct itimerspec __user *new_setting struct itimerspec __user *old_setting - kernel/posix-timers.c:800
261 sys_timer_gettime 0x105 timer_t timer_id struct itimerspec __user *setting - - - kernel/posix-timers.c:702
262 sys_timer_getoverrun 0x106 timer_t timer_id - - - - kernel/posix-timers.c:732
263 sys_timer_delete 0x107 timer_t timer_id - - - - kernel/posix-timers.c:855
264 sys_clock_settime 0x108 clockid_t which_clock const struct timespec __user *tp - - - kernel/posix-timers.c:941
265 sys_clock_gettime 0x109 clockid_t which_clock struct timespec __user *tp - - - kernel/posix-timers.c:954
266 sys_clock_getres 0x10a clockid_t which_clock struct timespec __user *tp - - - kernel/posix-timers.c:971
267 sys_clock_nanosleep 0x10b clockid_t which_clock int flags const struct timespec __user *rqtp struct timespec __user *rmtp - kernel/posix-timers.c:1001
268 sys_statfs64 0x10c const char __user *path size_t sz struct statfs64 __user *buf - - fs/statfs.c:118
269 sys_fstatfs64 0x10d unsigned int fd size_t sz struct statfs64 __user *buf - - fs/statfs.c:154
270 sys_tgkill 0x10e int tgid int pid int sig - - kernel/signal.c:2383
271 sys_utimes 0x10f char __user *filename struct timeval __user *utimes - - - fs/utimes.c:219
272 sys_fadvise64_64 0x110 int fd loff_t offset loff_t len int advice - not found:
273 not implemented 0x111 - - - - -  
274 sys_mbind 0x112 - - - - - mm/mempolicy.c:1232
275 sys_get_mempolicy 0x113 int __user *policy unsigned long __user *nmask unsigned long maxnode unsigned long addr unsigned long flags mm/mempolicy.c:1348
276 sys_set_mempolicy 0x114 int mode unsigned long __user *nmask unsigned long maxnode - - mm/mempolicy.c:1254
277 sys_mq_open 0x115 const char __user *name int oflag mode_t mode struct mq_attr __user *attr - ipc/mqueue.c:673
278 sys_mq_unlink 0x116 const char __user *name - - - - ipc/mqueue.c:746
279 sys_mq_timedsend 0x117 mqd_t mqdes const char __user *msg_ptr size_t msg_len unsigned int msg_prio const struct timespec __user *abs_timeout ipc/mqueue.c:840
280 sys_mq_timedreceive 0x118 mqd_t mqdes char __user *msg_ptr size_t msg_len unsigned int __user *msg_prio const struct timespec __user *abs_timeout ipc/mqueue.c:934
281 sys_mq_notify 0x119 mqd_t mqdes const struct sigevent __user *notification - - - ipc/mqueue.c:1023
282 sys_mq_getsetattr 0x11a mqd_t mqdes const struct mq_attr __user *mqstat struct mq_attr __user *omqstat - - ipc/mqueue.c:1154
283 sys_kexec_load 0x11b unsigned long entry unsigned long nr_segments struct kexec_segment __user *segments unsigned long flags - kernel/kexec.c:939
284 sys_waitid 0x11c int which pid_t pid struct siginfo __user *infop int options struct rusage __user *ru kernel/exit.c:1655
285 not implemented 0x11d - - - - -  
286 sys_add_key 0x11e const char __user *_type const char __user *_description const void __user *_payload size_t plen key_serial_t destringid security/keys/keyctl.c:57
287 sys_request_key 0x11f const char __user *_type const char __user *_description const char __user *_callout_info key_serial_t destringid - security/keys/keyctl.c:149
288 sys_keyctl 0x120 int cmd unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5 security/keys/keyctl.c:1338
289 sys_ioprio_set 0x121 int which int who int ioprio - - fs/ioprio.c:76
290 sys_ioprio_get 0x122 int which int who - - - fs/ioprio.c:192
291 sys_inotify_init 0x123 - - - - - fs/notify/inotify/inotify_user.c:680
292 sys_inotify_add_watch 0x124 int fd const char __user *path u32 mask - - fs/notify/inotify/inotify_user.c:685
293 sys_inotify_rm_watch 0x125 int fd __s32 wd - - - fs/notify/inotify/inotify_user.c:726
294 sys_migrate_pages 0x126 pid_t pid unsigned long maxnode const unsigned long __user *from const unsigned long __user *to - mm/mempolicy.c:1273
295 sys_openat 0x127 int dfd const char __user *filename int flags int mode - fs/open.c:913
296 sys_mkdirat 0x128 int dfd const char __user * pathname int mode - - fs/namei.c:2093
297 sys_mknodat 0x129 int dfd const char __user * filename int mode unsigned dev - fs/namei.c:2012
298 sys_fchownat 0x12a int dfd const char __user *filename uid_t user gid_t group int flag fs/open.c:558
299 sys_futimesat 0x12b int dfd char __user *filename struct timeval __user *utimes - - fs/utimes.c:191
300 sys_fstatat64 0x12c int dfd char __user *filename struct stat64 __user *statbuf int flag - fs/stat.c:391
301 sys_unlinkat 0x12d int dfd const char __user * pathname int flag - - fs/namei.c:2341
302 sys_renameat 0x12e int olddfd const char __user * oldname int newdfd const char __user * newname - fs/namei.c:2671
303 sys_linkat 0x12f int olddfd const char __user *oldname int newdfd const char __user *newname int flags fs/namei.c:2470
304 sys_symlinkat 0x130 const char __user * oldname int newdfd const char __user * newname - - fs/namei.c:2377
305 sys_readlinkat 0x131 int dfd const char __user *path char __user *buf int bufsiz - fs/stat.c:284
306 sys_fchmodat 0x132 int dfd const char __user * filename mode_t mode - - fs/open.c:474
307 sys_faccessat 0x133 int dfd const char __user *filename int mode - - fs/open.c:286
308 sys_pselect6 0x134 - - - - - fs/select.c:675
309 sys_ppoll 0x135 struct pollfd __user *ufds unsigned int nfds struct timespec __user *tsp const sigset_t __user *sigmask size_t sigsetsize fs/select.c:950
310 sys_unshare 0x136 unsigned long unshare_flags - - - - kernel/fork.c:1624
311 sys_set_robust_list 0x137 struct robust_list_head __user *head size_t len - - - kernel/futex.c:2351
312 sys_get_robust_list 0x138 int pid struct robust_list_head user * user *head_ptr size_t __user *len_ptr - - kernel/futex.c:2373
313 sys_splice 0x139 - - - - - fs/splice.c:1718
314 sys_sync_file_range 0x13a int fd loff_t offset loff_t nbytes unsigned int flags - not found:
315 sys_tee 0x13b int fdin int fdout size_t len unsigned int flags - fs/splice.c:2061
316 sys_vmsplice 0x13c int fd const struct iovec __user *iov unsigned long nr_segs unsigned int flags - fs/splice.c:1692
317 sys_move_pages 0x13d - - - - - mm/migrate.c:1075
318 sys_getcpu 0x13e unsigned __user *cpu unsigned __user *node struct getcpu_cache __user *cache - - kernel/sys.c:1621
319 sys_epoll_pwait 0x13f - - - - - fs/eventpoll.c:1373
320 sys_utimensat 0x140 int dfd char __user *filename struct timespec __user *utimes int flags - fs/utimes.c:173
321 sys_signalfd 0x141 int ufd sigset_t __user *user_mask size_t sizemask - - fs/signalfd.c:265
322 sys_timerfd_create 0x142 int clockid int flags - - - fs/timerfd.c:164
323 sys_eventfd 0x143 unsigned int count - - - - fs/eventfd.c:434
324 sys_fallocate 0x144 int fd int mode loff_t offset loff_t len - not found:
325 sys_timerfd_settime 0x145 int ufd int flags const struct itimerspec __user *utmr struct itimerspec __user *otmr - fs/timerfd.c:194
326 sys_timerfd_gettime 0x146 int ufd struct itimerspec __user *otmr - - - fs/timerfd.c:252
327 sys_signalfd4 0x147 int ufd sigset_t __user *user_mask size_t sizemask int flags - fs/signalfd.c:211
328 sys_eventfd2 0x148 unsigned int count int flags - - - fs/eventfd.c:409
329 sys_epoll_create1 0x149 int flags - - - - fs/eventpoll.c:1187
330 sys_dup3 0x14a unsigned int oldfd unsigned int newfd int flags - - fs/fcntl.c:53
331 sys_pipe2 0x14b int __user *fildes int flags - - - fs/pipe.c:1101
332 sys_inotify_init1 0x14c int flags - - - - fs/notify/inotify/inotify_user.c:640
333 sys_preadv 0x14d unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h fs/read_write.c:759
334 sys_pwritev 0x14e unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h fs/read_write.c:784
335 sys_rt_tgsigqueueinfo 0x14f pid_t tgid pid_t pid int sig siginfo_t __user *uinfo - kernel/signal.c:2437
336 sys_perf_event_open 0x150 struct perf_event_attr __user *attr_uptr pid_t pid int cpu int group_fd unsigned long flags kernel/perf_event.c:5065
337 sys_recvmmsg 0x151 int fd struct mmsghdr __user *msg unsigned int vlen unsigned flags struct timespec __user *timeout net/socket.c:2168

6.2 64系统调用表

%rax System call %rdi %rsi %rdx %r10 %r8 %r9
0 sys_read unsigned int fd char *buf size_t count      
1 sys_write unsigned int fd const char *buf size_t count      
2 sys_open const char *filename int flags int mode      
3 sys_close unsigned int fd          
4 sys_stat const char *filename struct stat *statbuf        
5 sys_fstat unsigned int fd struct stat *statbuf        
6 sys_lstat fconst char *filename struct stat *statbuf        
7 sys_poll struct poll_fd *ufds unsigned int nfds long timeout_msecs      
8 sys_lseek unsigned int fd off_t offset unsigned int origin      
9 sys_mmap unsigned long addr unsigned long len unsigned long prot unsigned long flags unsigned long fd unsigned long off
10 sys_mprotect unsigned long start size_t len unsigned long prot      
11 sys_munmap unsigned long addr size_t len        
12 sys_brk unsigned long brk          
13 sys_rt_sigaction int sig const struct sigaction *act struct sigaction *oact size_t sigsetsize    
14 sys_rt_sigprocmask int how sigset_t *nset sigset_t *oset size_t sigsetsize    
15 sys_rt_sigreturn unsigned long __unused          
16 sys_ioctl unsigned int fd unsigned int cmd unsigned long arg      
17 sys_pread64 unsigned long fd char *buf size_t count loff_t pos    
18 sys_pwrite64 unsigned int fd const char *buf size_t count loff_t pos    
19 sys_readv unsigned long fd const struct iovec *vec unsigned long vlen      
20 sys_writev unsigned long fd const struct iovec *vec unsigned long vlen      
21 sys_access const char *filename int mode        
22 sys_pipe int *filedes          
23 sys_select int n fd_set *inp fd_set *outp fd_set*exp struct timeval *tvp  
24 sys_sched_yield            
25 sys_mremap unsigned long addr unsigned long old_len unsigned long new_len unsigned long flags unsigned long new_addr  
26 sys_msync unsigned long start size_t len int flags      
27 sys_mincore unsigned long start size_t len unsigned char *vec      
28 sys_madvise unsigned long start size_t len_in int behavior      
29 sys_shmget key_t key size_t size int shmflg      
30 sys_shmat int shmid char *shmaddr int shmflg      
31 sys_shmctl int shmid int cmd struct shmid_ds *buf      
32 sys_dup unsigned int fildes          
33 sys_dup2 unsigned int oldfd unsigned int newfd        
34 sys_pause            
35 sys_nanosleep struct timespec *rqtp struct timespec *rmtp        
36 sys_getitimer int which struct itimerval *value        
37 sys_alarm unsigned int seconds          
38 sys_setitimer int which struct itimerval *value struct itimerval *ovalue      
39 sys_getpid            
40 sys_sendfile int out_fd int in_fd off_t *offset size_t count    
41 sys_socket int family int type int protocol      
42 sys_connect int fd struct sockaddr *uservaddr int addrlen      
43 sys_accept int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen      
44 sys_sendto int fd void *buff size_t len unsigned flags struct sockaddr *addr int addr_len
45 sys_recvfrom int fd void *ubuf size_t size unsigned flags struct sockaddr *addr int *addr_len
46 sys_sendmsg int fd struct msghdr *msg unsigned flags      
47 sys_recvmsg int fd struct msghdr *msg unsigned int flags      
48 sys_shutdown int fd int how        
49 sys_bind int fd struct sokaddr *umyaddr int addrlen      
50 sys_listen int fd int backlog        
51 sys_getsockname int fd struct sockaddr *usockaddr int *usockaddr_len      
52 sys_getpeername int fd struct sockaddr *usockaddr int *usockaddr_len      
53 sys_socketpair int family int type int protocol int *usockvec    
54 sys_setsockopt int fd int level int optname char *optval int optlen  
55 sys_getsockopt int fd int level int optname char *optval int *optlen  
56 sys_clone unsigned long clone_flags unsigned long newsp void *parent_tid void *child_tid    
57 sys_fork            
58 sys_vfork            
59 sys_execve const char *filename const char *const argv[] const char *const envp[]      
60 sys_exit int error_code          
61 sys_wait4 pid_t upid int *stat_addr int options struct rusage *ru    
62 sys_kill pid_t pid int sig        
63 sys_uname struct old_utsname *name          
64 sys_semget key_t key int nsems int semflg      
65 sys_semop int semid struct sembuf *tsops unsigned nsops      
66 sys_semctl int semid int semnum int cmd union semun arg    
67 sys_shmdt char *shmaddr          
68 sys_msgget key_t key int msgflg        
69 sys_msgsnd int msqid struct msgbuf *msgp size_t msgsz int msgflg    
70 sys_msgrcv int msqid struct msgbuf *msgp size_t msgsz long msgtyp int msgflg  
71 sys_msgctl int msqid int cmd struct msqid_ds *buf      
72 sys_fcntl unsigned int fd unsigned int cmd unsigned long arg      
73 sys_flock unsigned int fd unsigned int cmd        
74 sys_fsync unsigned int fd          
75 sys_fdatasync unsigned int fd          
76 sys_truncate const char *path long length        
77 sys_ftruncate unsigned int fd unsigned long length        
78 sys_getdents unsigned int fd struct linux_dirent *dirent unsigned int count      
79 sys_getcwd char *buf unsigned long size        
80 sys_chdir const char *filename          
81 sys_fchdir unsigned int fd          
82 sys_rename const char *oldname const char *newname        
83 sys_mkdir const char *pathname int mode        
84 sys_rmdir const char *pathname          
85 sys_creat const char *pathname int mode        
86 sys_link const char *oldname const char *newname        
87 sys_unlink const char *pathname          
88 sys_symlink const char *oldname const char *newname        
89 sys_readlink const char *path char *buf int bufsiz      
90 sys_chmod const char *filename mode_t mode        
91 sys_fchmod unsigned int fd mode_t mode        
92 sys_chown const char *filename uid_t user gid_t group      
93 sys_fchown unsigned int fd uid_t user gid_t group      
94 sys_lchown const char *filename uid_t user gid_t group      
95 sys_umask int mask          
96 sys_gettimeofday struct timeval *tv struct timezone *tz        
97 sys_getrlimit unsigned int resource struct rlimit *rlim        
98 sys_getrusage int who struct rusage *ru        
99 sys_sysinfo struct sysinfo *info          
100 sys_times struct sysinfo *info          
101 sys_ptrace long request long pid unsigned long addr unsigned long data    
102 sys_getuid            
103 sys_syslog int type char *buf int len      
104 sys_getgid            
105 sys_setuid uid_t uid          
106 sys_setgid gid_t gid          
107 sys_geteuid            
108 sys_getegid            
109 sys_setpgid pid_t pid pid_t pgid        
110 sys_getppid            
111 sys_getpgrp            
112 sys_setsid            
113 sys_setreuid uid_t ruid uid_t euid        
114 sys_setregid gid_t rgid gid_t egid        
115 sys_getgroups int gidsetsize gid_t *grouplist        
116 sys_setgroups int gidsetsize gid_t *grouplist        
117 sys_setresuid uid_t *ruid uid_t *euid uid_t *suid      
118 sys_getresuid uid_t *ruid uid_t *euid uid_t *suid      
119 sys_setresgid gid_t rgid gid_t egid gid_t sgid      
120 sys_getresgid gid_t *rgid gid_t *egid gid_t *sgid      
121 sys_getpgid pid_t pid          
122 sys_setfsuid uid_t uid          
123 sys_setfsgid gid_t gid          
124 sys_getsid pid_t pid          
125 sys_capget cap_user_header_t header cap_user_data_t dataptr        
126 sys_capset cap_user_header_t header const cap_user_data_t data        
127 sys_rt_sigpending sigset_t *set size_t sigsetsize        
128 sys_rt_sigtimedwait const sigset_t *uthese siginfo_t *uinfo const struct timespec *uts size_t sigsetsize    
129 sys_rt_sigqueueinfo pid_t pid int sig siginfo_t *uinfo      
130 sys_rt_sigsuspend sigset_t *unewset size_t sigsetsize        
131 sys_sigaltstack const stack_t *uss stack_t *uoss        
132 sys_utime char *filename struct utimbuf *times        
133 sys_mknod const char *filename umode_t mode unsigned dev      
134 sys_uselib NOT IMPLEMENTED          
135 sys_personality unsigned int personality          
136 sys_ustat unsigned dev struct ustat *ubuf        
137 sys_statfs const char *pathname struct statfs *buf        
138 sys_fstatfs unsigned int fd struct statfs *buf        
139 sys_sysfs int option unsigned long arg1 unsigned long arg2      
140 sys_getpriority int which int who        
141 sys_setpriority int which int who int niceval      
142 sys_sched_setparam pid_t pid struct sched_param *param        
143 sys_sched_getparam pid_t pid struct sched_param *param        
144 sys_sched_setscheduler pid_t pid int policy struct sched_param *param      
145 sys_sched_getscheduler pid_t pid          
146 sys_sched_get_priority_max int policy          
147 sys_sched_get_priority_min int policy          
148 sys_sched_rr_get_interval pid_t pid struct timespec *interval        
149 sys_mlock unsigned long start size_t len        
150 sys_munlock unsigned long start size_t len        
151 sys_mlockall int flags          
152 sys_munlockall            
153 sys_vhangup            
154 sys_modify_ldt int func void *ptr unsigned long bytecount      
155 sys_pivot_root const char *new_root const char *put_old        
156 sys__sysctl struct __sysctl_args *args          
157 sys_prctl int option unsigned long arg2 unsigned long arg3 unsigned long arg4   unsigned long arg5
158 sys_arch_prctl struct task_struct *task int code unsigned long *addr      
159 sys_adjtimex struct timex *txc_p          
160 sys_setrlimit unsigned int resource struct rlimit *rlim        
161 sys_chroot const char *filename          
162 sys_sync            
163 sys_acct const char *name          
164 sys_settimeofday struct timeval *tv struct timezone *tz        
165 sys_mount char *dev_name char *dir_name char *type unsigned long flags void *data  
166 sys_umount2 const char *target int flags        
167 sys_swapon const char *specialfile int swap_flags        
168 sys_swapoff const char *specialfile          
169 sys_reboot int magic1 int magic2 unsigned int cmd void *arg    
170 sys_sethostname char *name int len        
171 sys_setdomainname char *name int len        
172 sys_iopl unsigned int level struct pt_regs *regs        
173 sys_ioperm unsigned long from unsigned long num int turn_on      
174 sys_create_module REMOVED IN Linux 2.6          
175 sys_init_module void *umod unsigned long len const char *uargs      
176 sys_delete_module const chat *name_user unsigned int flags        
177 sys_get_kernel_syms REMOVED IN Linux 2.6          
178 sys_query_module REMOVED IN Linux 2.6          
179 sys_quotactl unsigned int cmd const char *special qid_t id void *addr    
180 sys_nfsservctl NOT IMPLEMENTED          
181 sys_getpmsg NOT IMPLEMENTED          
182 sys_putpmsg NOT IMPLEMENTED          
183 sys_afs_syscall NOT IMPLEMENTED          
184 sys_tuxcall NOT IMPLEMENTED          
185 sys_security NOT IMPLEMENTED          
186 sys_gettid            
187 sys_readahead int fd loff_t offset size_t count      
188 sys_setxattr const char *pathname const char *name const void *value size_t size int flags  
189 sys_lsetxattr const char *pathname const char *name const void *value size_t size int flags  
190 sys_fsetxattr int fd const char *name const void *value size_t size int flags  
191 sys_getxattr const char *pathname const char *name void *value size_t size    
192 sys_lgetxattr const char *pathname const char *name void *value size_t size    
193 sys_fgetxattr int fd const har *name void *value size_t size    
194 sys_listxattr const char *pathname char *list size_t size      
195 sys_llistxattr const char *pathname char *list size_t size      
196 sys_flistxattr int fd char *list size_t size      
197 sys_removexattr const char *pathname const char *name        
198 sys_lremovexattr const char *pathname const char *name        
199 sys_fremovexattr int fd const char *name        
200 sys_tkill pid_t pid ing sig        
201 sys_time time_t *tloc          
202 sys_futex u32 *uaddr int op u32 val struct timespec *utime u32 *uaddr2 u32 val3
203 sys_sched_setaffinity pid_t pid unsigned int len unsigned long *user_mask_ptr      
204 sys_sched_getaffinity pid_t pid unsigned int len unsigned long *user_mask_ptr      
205 sys_set_thread_area NOT IMPLEMENTED. Use arch_prctl          
206 sys_io_setup unsigned nr_events aio_context_t *ctxp        
207 sys_io_destroy aio_context_t ctx          
208 sys_io_getevents aio_context_t ctx_id long min_nr long nr struct io_event *events    
209 sys_io_submit aio_context_t ctx_id long nr struct iocb **iocbpp      
210 sys_io_cancel aio_context_t ctx_id struct iocb *iocb struct io_event *result      
211 sys_get_thread_area NOT IMPLEMENTED. Use arch_prctl          
212 sys_lookup_dcookie u64 cookie64 long buf long len      
213 sys_epoll_create int size          
214 sys_epoll_ctl_old NOT IMPLEMENTED          
215 sys_epoll_wait_old NOT IMPLEMENTED          
216 sys_remap_file_pages unsigned long start unsigned long size unsigned long prot unsigned long pgoff unsigned long flags  
217 sys_getdents64 unsigned int fd struct linux_dirent64 *dirent unsigned int count      
218 sys_set_tid_address int *tidptr          
219 sys_restart_syscall            
220 sys_semtimedop int semid struct sembuf *tsops unsigned nsops const struct timespec *timeout    
221 sys_fadvise64 int fd loff_t offset size_t len int advice    
222 sys_timer_create const clockid_t which_clock struct sigevent *timer_event_spec timer_t *created_timer_id      
223 sys_timer_settime timer_t timer_id int flags const struct itimerspec *new_setting struct itimerspec *old_setting    
224 sys_timer_gettime timer_t timer_id struct itimerspec *setting        
225 sys_timer_getoverrun timer_t timer_id          
226 sys_timer_delete timer_t timer_id          
227 sys_clock_settime const clockid_t which_clock const struct timespec *tp        
228 sys_clock_gettime const clockid_t which_clock struct timespec *tp        
229 sys_clock_getres const clockid_t which_clock struct timespec *tp        
230 sys_clock_nanosleep const clockid_t which_clock int flags const struct timespec *rqtp struct timespec *rmtp    
231 sys_exit_group int error_code          
232 sys_epoll_wait int epfd struct epoll_event *events int maxevents int timeout    
233 sys_epoll_ctl int epfd int op int fd struct epoll_event *event    
234 sys_tgkill pid_t tgid pid_t pid int sig      
235 sys_utimes char *filename struct timeval *utimes        
236 sys_vserver NOT IMPLEMENTED          
237 sys_mbind unsigned long start unsigned long len unsigned long mode unsigned long *nmask unsigned long maxnode unsigned flags
238 sys_set_mempolicy int mode unsigned long *nmask unsigned long maxnode      
239 sys_get_mempolicy int *policy unsigned long *nmask unsigned long maxnode unsigned long addr unsigned long flags  
240 sys_mq_open const char *u_name int oflag mode_t mode struct mq_attr *u_attr    
241 sys_mq_unlink const char *u_name          
242 sys_mq_timedsend mqd_t mqdes const char *u_msg_ptr size_t msg_len unsigned int msg_prio const stuct timespec *u_abs_timeout  
243 sys_mq_timedreceive mqd_t mqdes char *u_msg_ptr size_t msg_len unsigned int *u_msg_prio const struct timespec *u_abs_timeout  
244 sys_mq_notify mqd_t mqdes const struct sigevent *u_notification        
245 sys_mq_getsetattr mqd_t mqdes const struct mq_attr *u_mqstat struct mq_attr *u_omqstat      
246 sys_kexec_load unsigned long entry unsigned long nr_segments struct kexec_segment *segments unsigned long flags    
247 sys_waitid int which pid_t upid struct siginfo *infop int options struct rusage *ru  
248 sys_add_key const char *_type const char *_description const void *_payload size_t plen    
249 sys_request_key const char *_type const char *_description const char *_callout_info key_serial_t destringid    
250 sys_keyctl int option unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5  
251 sys_ioprio_set int which int who int ioprio      
252 sys_ioprio_get int which int who        
253 sys_inotify_init            
254 sys_inotify_add_watch int fd const char *pathname u32 mask      
255 sys_inotify_rm_watch int fd __s32 wd        
256 sys_migrate_pages pid_t pid unsigned long maxnode const unsigned long *old_nodes const unsigned long *new_nodes    
257 sys_openat int dfd const char *filename int flags int mode    
258 sys_mkdirat int dfd const char *pathname int mode      
259 sys_mknodat int dfd const char *filename int mode unsigned dev    
260 sys_fchownat int dfd const char *filename uid_t user gid_t group int flag  
261 sys_futimesat int dfd const char *filename struct timeval *utimes      
262 sys_newfstatat int dfd const char *filename struct stat *statbuf int flag    
263 sys_unlinkat int dfd const char *pathname int flag      
264 sys_renameat int oldfd const char *oldname int newfd const char *newname    
265 sys_linkat int oldfd const char *oldname int newfd const char *newname int flags  
266 sys_symlinkat const char *oldname int newfd const char *newname      
267 sys_readlinkat int dfd const char *pathname char *buf int bufsiz    
268 sys_fchmodat int dfd const char *filename mode_t mode      
269 sys_faccessat int dfd const char *filename int mode      
270 sys_pselect6 int n fd_set *inp fd_set *outp fd_set *exp struct timespec *tsp void *sig
271 sys_ppoll struct pollfd *ufds unsigned int nfds struct timespec *tsp const sigset_t *sigmask size_t sigsetsize  
272 sys_unshare unsigned long unshare_flags          
273 sys_set_robust_list struct robust_list_head *head size_t len        
274 sys_get_robust_list int pid struct robust_list_head **head_ptr size_t *len_ptr      
275 sys_splice int fd_in loff_t *off_in int fd_out loff_t *off_out size_t len unsigned int flags
276 sys_tee int fdin int fdout size_t len unsigned int flags    
277 sys_sync_file_range long fd loff_t offset loff_t bytes long flags    
278 sys_vmsplice int fd const struct iovec *iov unsigned long nr_segs unsigned int flags    
279 sys_move_pages pid_t pid unsigned long nr_pages const void **pages const int *nodes int *status int flags
280 sys_utimensat int dfd const char *filename struct timespec *utimes int flags    
281 sys_epoll_pwait int epfd struct epoll_event *events int maxevents int timeout const sigset_t *sigmask size_t sigsetsize
282 sys_signalfd int ufd sigset_t *user_mask size_t sizemask      
283 sys_timerfd_create int clockid int flags        
284 sys_eventfd unsigned int count          
285 sys_fallocate long fd long mode loff_t offset loff_t len    
286 sys_timerfd_settime int ufd int flags const struct itimerspec *utmr struct itimerspec *otmr    
287 sys_timerfd_gettime int ufd struct itimerspec *otmr        
288 sys_accept4 int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen int flags    
289 sys_signalfd4 int ufd sigset_t *user_mask size_t sizemask int flags    
290 sys_eventfd2 unsigned int count int flags        
291 sys_epoll_create1 int flags          
292 sys_dup3 unsigned int oldfd unsigned int newfd int flags      
293 sys_pipe2 int *filedes int flags        
294 sys_inotify_init1 int flags          
295 sys_preadv unsigned long fd const struct iovec *vec unsigned long vlen unsigned long pos_l unsigned long pos_h  
296 sys_pwritev unsigned long fd const struct iovec *vec unsigned long vlen unsigned long pos_l unsigned long pos_h  
297 sys_rt_tgsigqueueinfo pid_t tgid pid_t pid int sig siginfo_t *uinfo    
298 sys_perf_event_open struct perf_event_attr *attr_uptr pid_t pid int cpu int group_fd unsigned long flags  
299 sys_recvmmsg int fd struct msghdr *mmsg unsigned int vlen unsigned int flags struct timespec *timeout  
300 sys_fanotify_init unsigned int flags unsigned int event_f_flags        
301 sys_fanotify_mark long fanotify_fd long flags __u64 mask long dfd long pathname  
302 sys_prlimit64 pid_t pid unsigned int resource const struct rlimit64 *new_rlim struct rlimit64 *old_rlim    
303 sys_name_to_handle_at int dfd const char *name struct file_handle *handle int *mnt_id int flag  
304 sys_open_by_handle_at int dfd const char *name struct file_handle *handle int *mnt_id int flags  
305 sys_clock_adjtime clockid_t which_clock struct timex *tx        
306 sys_syncfs int fd          
307 sys_sendmmsg int fd struct mmsghdr *mmsg unsigned int vlen unsigned int flags    
308 sys_setns int fd int nstype        
309 sys_getcpu unsigned *cpup unsigned *nodep struct getcpu_cache *unused      
310 sys_process_vm_readv pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovec *rvec unsigned long riovcnt unsigned long flags
311 sys_process_vm_writev pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovcc *rvec unsigned long riovcnt unsigned long flags
312 sys_kcmp pid_t pid1 pid_t pid2 int type unsigned long idx1 unsigned long idx2  
313 sys_finit_module int fd const char __user *uargs int flags      
314 sys_sched_setattr pid_t pid struct sched_attr __user *attr unsigned int flags      
315 sys_sched_getattr pid_t pid struct sched_attr __user *attr unsigned int size unsigned int flags    
316 sys_renameat2 int olddfd const char __user *oldname int newdfd const char __user *newname unsigned int flags  
317 sys_seccomp unsigned int op unsigned int flags const char __user *uargs      
318 sys_getrandom char __user *buf size_t count unsigned int flags      
319 sys_memfd_create const char __user *uname_ptr unsigned int flags        
320 sys_kexec_file_load int kernel_fd int initrd_fd unsigned long cmdline_len const char __user *cmdline_ptr unsigned long flags  
321 sys_bpf int cmd union bpf_attr *attr unsigned int size      
322 stub_execveat int dfd const char __user *filename const char user *const user *argv const char user *const user *envp int flags  
323 userfaultfd int flags          
324 membarrier int cmd int flags        
325 mlock2 unsigned long start size_t len int flags      
326 copy_file_range int fd_in loff_t __user *off_in int fd_out loff_t __user * off_out size_t len unsigned int flags
327 preadv2 unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h int flags
328 pwritev2 unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h int flags

 

 

 

posted @ 2023-04-08 10:00  CharyGao  阅读(390)  评论(0编辑  收藏  举报