X86處理器彙編技術系列5

第35部分-Linux x86 64位彙編 系統調用

如果對Linux內核有一定的瞭解,那麼理解起來會非常簡單。系統調用是用戶態程序陷入到內核態的API接口。

         每個系統調用都有一個名稱和調用號,調用號一旦確認就基本不會改變。現在Linux系統調用有三百多個。

     64位系統中可以在文件中找到:

arch/x86/entry/syscalls/syscall_64.tbl

文件定義的是

在Linux環境中可以通過man命令來獲取。例如

man 2 exit

其中2表示man頁的第二部分。

系統調用格式

我們已經知道EAX寄存器用於保存系統調用值。定義使用內核支持的系統調用中的哪個系統調用。

系統調用示例

 
  1. .section .data
  2. output:
  3. .ascii "This is a test message.\n"
  4. len:
  5. .int 24
  6. .section .text
  7. .globl _start
  8. _start:
  9. movq $1, %rax
  10. movq $1, %rdi
  11. movq $output, %rsi
  12. movq len, %rdx
  13. syscall
  14.  
  15. movq $60, %rax
  16. movq $0, %rdi
  17. syscall
 

as -g -o syscalltest.o syscalltest.s

ld -o syscalltest syscalltest.o

 

int 80h 和syscall

SYSCALL指令是INT 0X80的64位版本,syscall是在x86-64上進入內核模式的默認方式。

但是仍然可以在64位代碼中使用INT 0X80,但是強烈不建議使用了。

int 0x80是調用系統調用的傳統方法,應避免使用。

而且syscall具有比int 0x80更少的開銷。

int 0x80是選擇eax作爲系統調用編號,ebx,ecx,edx,esi,edi和ebp傳遞參數。

Syscall是使用rdi,rsi,rdx,rcx,r8,r9傳遞傳輸。

第36部分-Linux x86 64位彙編 sysinfo系統調用

Sysinfo可以返回關於系統配置的資源信息。

通過man 查看如下:

NAME

       sysinfo - return system information

 

SYNOPSIS

       #include <sys/sysinfo.h>

       int sysinfo(struct sysinfo *info);

結構體如下:

struct sysinfo {

         long uptime;             /* Seconds since boot */

         unsigned long loads[3];/* 1, 5, and 15 minute load averages*/

         unsigned long totalram;  /* Total usable main memory size */

         unsigned long freeram;   /* Available memory size */

         unsigned long sharedram; /* Amount of shared memory */

         unsigned long bufferram; /* Memory used by buffers */

         unsigned long totalswap; /* Total swap space size */

         unsigned long freeswap;  /* Swap space still available */

         unsigned short procs;    /* Number of current processes */

         char _f[22];             /* Pads structure to 64 bytes */

};

通過查看文件arch/x86/entry/syscalls/syscall_64.tbl

知道系統調用號是99.

示例

 
  1. .section .data
  2. result:
  3. uptime:
  4. .int 0,0
  5. load1:
  6. .int 0,0
  7. load5:
  8. .int 0,0
  9. load15:
  10. .int 0,0
  11. totalram:
  12. .int 0,0
  13. freeram:
  14. .int 0,0
  15. sharedram:
  16. .int 0,0
  17. bufferram:
  18. .int 0,0
  19. totalswap:
  20. .int 0,0
  21. freeswap:
  22. .int 0,0
  23. procs:
  24. .byte 0x00, 0x00
  25. .section .text
  26. .globl _start
  27. _start:
  28. nop
  29. movq $result, %rdi
  30. movq $99, %rax
  31. syscall
  32.  
  33. movq $0, %rbx
  34. movq $60, %rax
  35. syscall
 

as -g -o sysinfo.o sysinfo.s

ld -o sysinfo sysinfo.o

在執行過程中進行查看如下:

(gdb) x /d &load5

0x6000e1:   23584

(gdb) x /d &load15

0x6000e9:   10656

(gdb) x /d &totalram

0x6000f1:   -467349504

(gdb) x /ud &totalram

0x6000f1:   -467349504

(gdb) x /u &totalram

0x6000f1:   3827617792

(gdb) x /u &procs

0x600121:   1565

(gdb) x /u &totalswap

0x600111:   999288832

(gdb) x /u &freeswap

0x600119:   999288832

(gdb)

第37部分-Linux x86 64位彙編 系統調用跟蹤

我們可以使用strace進行系統調用跟蹤。

本章我們通過彙編來實現一個後臺持續的進程,然後strace通過-p參數來跟蹤該進程的系統調用。

示例

調用nanosleep系統調用來進行等待,系統調用號位35.

NAME

       nanosleep - high-resolution sleep

 

SYNOPSIS

       #include <time.h>

 

       int nanosleep(const struct timespec *req, struct timespec *rem);

其中結構體timespec是:

struct timespec {

               time_t tv_sec;        /* seconds */

               long   tv_nsec;       /* nanoseconds */

};

代碼如下:

 
  1. .section .data
  2. timespec:;//定義了timespec結構·,5表示秒,0表示0納秒。需要16個字節。
  3. .int 5, 0,0,0
  4. output:
  5. .ascii "This is a test\n"
  6. output_end:
  7. .equ len, 15
  8. temp:
  9. .int 0
  10.  
  11. .section .bss;// 需要16個字節。
  12. .lcomm rem,16
  13. .section .text
  14. .globl _start
  15. _start:
  16. nop
  17. movq $10, %rcx
  18. loop1:
  19. movq %rcx, temp
  20. movq $1, %rax
  21. movq $1, %rdi
  22. movq $output,%rsi
  23. movq $len, %rdx
  24. syscall
  25.  
  26. movq $35, %rax
  27. movq $timespec, %rdi
  28. movq $rem, %rsi
  29. syscall
  30.  
  31. movq temp,%rcx
  32. loop loop1
  33.  
  34. movq $60, %rax
  35. syscall
 

as -g -o nanotest.o nanotest.s

ld -o nanotest nanotest.o

然後通過strace進行進程的系統調用。

# strace -p 87720

strace: Process 87720 attached

restart_syscall(<... resuming interrupted nanosleep ...>) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0},

0x600130) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x600130) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x600130) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x600130) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x600130) = 0

exit(6291720)                           = ?

+++ exited with 8 +++

第38部分-Linux x86 64位彙編 系統調用和C庫

C庫提供了豐富的函數,可以在彙編語言中利用他們。

C庫函數包含在libc庫中,必須連接到彙編語言程序中。C庫包含在man頁的第3部分。系統調用包含在man頁的第2部分。

跟蹤C函數示例

 
  1. .section .data
  2. output:
  3. .asciz "This is a test\n"
  4. temp:
  5. .int 0
  6. .section .text
  7. .globl _start
  8. _start:
  9. movq $10, %rcx
  10. loop1:
  11. movq %rcx, temp
  12. movq $output,%rdi
  13. call printf
  14. movq $5, %rdi
  15. call sleep
  16. pop %rcx
  17. movq temp,%rcx
  18. loop loop1
  19. movq $0,%rdi
  20. call exit
 

as -g -o cfunctest.o cfunctest.s

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

運行cfunctest後使用strace進行跟蹤

strace: Process 88728 attached

restart_syscall(<... resuming interrupted nanosleep ...>) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039be70) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039be78) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039be80) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039be88) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039be90) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039be98) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffe2039bea0) = 0

write(1, "This is a test\n", 15)        = 15

nanosleep({tv_sec=5, tv_nsec=0},

我們發現C庫和系統調用基本一致。

另外,我們在執行的時候,使用strace -c ./cfunctest

# strace -c ./cfunctest

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

% time     seconds  usecs/call     calls    errors syscall

------ ----------- ----------- --------- --------- ----------------

  0.00    0.000000           0         1           read

  0.00    0.000000           0        10           write

  0.00    0.000000           0         2           close

  0.00    0.000000           0         3           fstat

  0.00    0.000000           0         5           mmap

  0.00    0.000000           0         4           mprotect

  0.00    0.000000           0         1           munmap

  0.00    0.000000           0         3           brk

  0.00    0.000000           0         3         3 access

  0.00    0.000000           0        10           nanosleep

  0.00    0.000000           0         1           execve

  0.00    0.000000           0         1           arch_prctl

  0.00    0.000000           0         2           openat

------ ----------- ----------- --------- --------- ----------------

100.00    0.000000                    46         3 total

 

# strace -c ./nanotest

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

This is a test

% time     seconds  usecs/call     calls    errors syscall

------ ----------- ----------- --------- --------- ----------------

  0.00    0.000000           0        10           write

  0.00    0.000000           0        10           nanosleep

  0.00    0.000000           0         1           execve

------ ----------- ----------- --------- --------- ----------------

100.00    0.000000                    21           total

使用C庫使用了46次系統調用,而nanotest只使用了21次系統調用。

C庫和系統調用小結

從而我們可以得出結論,系統調用的好處是:

  • 代碼長度短,不需要把外部庫連接到程序中
  • 速度快,不需要外部庫連接到程序中。
  • 連接後可執行文件獨立於外部代碼,只依賴內核系統調用。

使用C庫的好處是:

  • 包含函數多,而彙編去實現比較費力。
  • C庫在操作系統之間可移植。
  • C庫函數在程序間利用共享,減少內存需求。

第39部分-Linux x86 64位彙編 跳轉/調用/中斷/循環

跳轉

JMP指令把指令指針的值改變爲JMP指令中指定的內存位置。

單一彙編跳轉指令被彙編爲跳轉操作碼的3種不同類型之一。

短跳轉

近跳轉

遠跳轉

由當前指令的內存位置和目的點的內存位置之間的距離決定的。

依據跳過的字節數目決定使用哪種跳轉類型。

當偏移量小於128字節時候,用短跳轉。

在分段內存模式下,當跳轉到另一個段中的指令時使用元跳轉。

近跳轉用於所有其他跳轉。

使用匯編語言助記符指令時,不需要擔心跳轉的長度,單一跳轉指令用於跳轉到程序代碼中的任務位置。

 

條件跳轉指令不支持分段內存模式下的遠跳轉。

調用

調用是具有在需要的時候返回這個位置的能力。

調用指令有兩個部分,第一個部分是實際的call指令,需要單一操作數——跳轉到的位置地址。

         緊跟call指針後面的是RET。

         調用CALL指令後,會把EIP寄存器放到堆棧中,然後修改EIP寄存器指向被調用的函數地址。函數調用完成後,從堆棧獲得過去的EIP寄存器值,返回控制權。

中斷

中斷是處理器中斷當前指令碼路徑並且切換到不同路徑的方式。

中斷有軟件中斷/硬件中斷。

硬件中斷是硬件層發生的事件。

軟件中斷是操作系統提供的。在Linux中,0x80中斷用於提供低級內核函數。

 

循環

使用Loop命令。

 

未來防止loop限制,不要將ecs置0後開始循環。

Loop操作是先將ecs減1.

第40部分-Linux x86 64位彙編 Intel向量指令歷史

SIMD主要是四種技術:

多媒體擴展(MMX, Multimedia Extension)

流化SIMD擴展(SSE,Streaming SIMD Extension)

流化SIMD擴展第二實現(SSE2, Streaming SIMD Extension Second Implementation)

流化SIMD擴展第三年實現(SSE3,Streaming SIMD Extension Third Implement)

SIMD的主要好處是使用單一指令執行並行數據操作的能力。

MMX和SSE架構可以保存打包數據的附加寄存器。

 

發展歷史如下:

 

 

MMX

MMX提供3種整數值類型:

  • 64位打包字節整數(8個單字節整數值)/64位打包整數(4個字整數值)/64位打包雙字整數(2個雙字整數值)

MMX整數數據類型需要使用FPU寄存器來保存執行數學操作。MMX寄存器被命名爲MM0到MM7。

MMX寄存器直接映射到FPU寄存器。但是不能當做堆棧使用。直接映射到FPU的R0到R7寄存器。

所以,FPU寄存器既用於保存MMX數據,也保存FPU數據,兩者容易混亂。

MMX模式下使用這些寄存器處理MMX數據,在FPU模式下使用這些寄存器處理一般的FPU擴展雙精度浮點數據。

     在MMX模式下使用FPU寄存器時,FPU的標記寄存器會被破壞。最後的辦法是將FPU寄存器的指令和MMX寄存器的指令分開。

 

SSE

SSE主要是對浮點數據執行SIMD操作。新的數據類型:128位打包的單精度浮點數據類型。

SSE2

SSE2擴展了SSE核心架構。

128位打包的雙精度浮點值(2個雙精度值)

128位打包字節整數值(16個單字節整數值)

128位打包字整數值(8個字整數值)

128位打包雙字整數值(4個雙字整數值)

128位打包四字整數值(2個四字整數值)

SSE3沒有添加新的數據類型,增加了數據高級處理指令。

 

向量指令支持檢測

通過調用cpuid值,將eax寄存器賦值爲1.

可以獲取處理器簽名信息。

使用test指令進行比較判斷。

 
  1. .section .data
  2. gotmmx:
  3. .asciz "Supports MMX"
  4. gotsse:
  5. .asciz "Supports SSE"
  6. gotsse2:
  7. .asciz "Supports SSE2"
  8. gotsse3:
  9. .asciz "Supports SSE3"
  10. output:
  11. .asciz "%s\n"
  12. .section .bss
  13. .lcomm ecxdata, 4
  14. .lcomm edxdata, 4
  15. .section .text
  16. .globl _start
  17. _start:
  18. nop
  19. movl $1, %eax;//CPUID的1號功能
  20. cpuid
  21. movl %ecx, ecxdata
  22. movl %edx, edxdata
  23.  
  24. test $0x00800000, %edx
  25. jz done
  26.  
  27. movq $output,%rdi
  28. movq $gotmmx,%rsi
  29.  
  30. call printf
  31.  
  32. movl edxdata, %edx
  33. test $0x02000000, %edx
  34. jz done
  35. movq $output,%rdi
  36. movq $gotsse,%rsi
  37. call printf
  38.  
  39. movl edxdata, %edx
  40. test $0x04000000, %edx
  41. jz done
  42. movq $output,%rdi
  43. movq $gotsse2,%rsi
  44. call printf
  45.  
  46. movl ecxdata, %ecx
  47. test $0x00000001, %ecx
  48. jz done
  49. movq $output,%rdi
  50. movq $gotsse3,%rsi
  51.  
  52. call printf
  53.  
  54. done:
  55. movq $60,%rax
  56. call exit
 

as -g -o features.o features.s

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

第41部分-Linux x86 64位彙編MMX使用

使用MMX架構需要一下步驟

  1. 從整數值創建打包整數值
  2. 把打包整數值加載到MMX寄存器中
  3. 對打包整數值執行MMX數學操作。
  4. 從MMX寄存器獲得結果放到內存位置中。

加載和獲得打包的整數值,使用movq指令把整數值傳送進/出MMX寄存器。

對比打包整數值操作,同時計算多個結果值,單一一組標誌不能表示操作的結果。可以從執行數學操作的3種溢出方法中選擇: 環繞/帶符號飽和/無符號飽和。

帶符號和無符號飽和運算方法把溢出情況的結果設置爲預先設置的值。

正溢出就設置爲最大值,負溢出就設置爲最小值,數學角度沒有意義。主要是應用於執行圖像計算顯示圖片,正溢出就是最大值爲白色,負溢出就是最小值爲黑色。

操作的指令有如下:

MMX加法示例

兩個long類型整數值存儲到單一內存位置,創建打包雙字整數值。移動到MMX寄存器,然後通過PADD指令相加,存放到MM0中,最後複製到result內存位置。

.section .data
value1:
   .int 10, 20
value2:
   .int 30, 40
.section .bss
   .lcomm result, 8
.section .text
.globl _start
_start:
   nop
   movq value1, %mm0
   movq value2, %mm1
   paddd %mm1, %mm0
   movq %mm0, result
 
   movl $60, %eax
   movl $0, %ebx
   syscall

編譯:

as -g -o mmxadd.o mmxadd.s

ld -o mmxadd mmxadd.o

使用gdb調試,在退出前斷點進行查看結果。

(gdb) x /2d &value1

0x6000d8:   10  20

(gdb) x /2d &value2

0x6000e0:   30  40

(gdb) x /2x &result

0x6000e8 <result>: 0x00000028  0x0000003c

gdb>info all

st0            <invalid float value>   (raw 0xffff0000003c00000028)

可以在st0寄存器中看到兩個結果。MM0就是存在st0中的。

 

    1. MMX乘法示例

乘法比較困難,因爲乘法生產的結果可能會比輸入操作數大得多。乘法允許使用兩條指令完成乘法操作。

PMULL把每對打包字整數值相乘,結果的低16位存放到目標寄存器。

PMULH把每對打包字整數值相乘,結果的高16位存放到目標寄存器。

帶符號的是PMULLW和PMULHW,無符號的是PMULLUW和PMULHUW。

     乘法系列中還有一個附加指令是PMADDWD指令。

MMX邏輯和移位

MMX中可用的布爾邏輯指令如下圖:

SOURCE可以是MMX寄存器或者64位的內存位置,目標必須是MMX寄存器。

 

MMX比較

兩個值比較指令如下圖:

比較結果存放到目標打包整數值中。

      1. 示例

比較的示例如下,value1和value2被設置爲保存4個short類型的整數值。加載到MMX寄存器中,然後使用PCMPEQW指令比較打包整數值的4個字值。結果存放到MM0寄存器,然後傳送到result中。

.section .data
value1:
   .short 10, 20, -30, 40
value2:
   .short 10, 40, -30, 45
.section .bss
   .lcomm result, 8
.section .text
.globl _start
_start:
   nop
   movq value1, %mm0
   movq value2, %mm1
   pcmpeqw %mm1, %mm0
   movq %mm0, result
 
   movl $60, %eax
   movl $0, %ebx
   syscall

as -g -o mmxcomp.o mmxcomp.s

ld -g -o mmxcomp mmxcomp.o

使用gdb進行調試,開始時如下;

(gdb) x /x &value1

0x6000d8:   0x0014000a

(gdb) x /x &value2

0x6000e0:   0x0028000a

(gdb) x /x &result

0x6000e8 <result>: 0x00000000

執行移動到MM0/MM1後,ST0/ST1的寄存器如下:

st0 <invalid float value>  (raw 0xffff0028ffe20014000a)

st1 <invalid float value>  (raw 0xffff002dffe20028000a)

執行pcmpeqw後st0寄存器如下:

st0 <invalid float value>  (raw 0xffff0000ffff0000ffff)

(gdb) x /8x & result

0x6000e8 <result>: 0x0000ffff  0x0000ffff

我們發現結果如下:

相等的打包整數值,結果相等等於FFFF,不相等的等於0000。

第42部分-Linux x86 64位彙編SSE指令

SSE架構提供對打包單精度浮點值的SIMD支持。數據傳送到XMM寄存器中。

SSE指令有兩個版本,後綴PS和後綴SS。PS是對打包單精度浮點值執行類型的運算操作,每個值都參與。SS結尾的,只對打包值中的低位雙字執行。

傳送數據

傳送單精度浮點值很大程度上依賴於值是否在內存中對準了。

MOVAPS指令要求數據在內存中對準16字節邊界。如果不對準會出現分段錯誤。

gas彙編器使用.align命令來把數據對準特定的內存邊界。

處理數據

運算指令如下;

這些指令都是用兩個操作數,源操作數可以是128位內存或者XMM寄存器,目標操作數必須是XMM寄存器。

sse示例

 
  1. .section .data
  2. .align 16
  3. value1:
  4. .float 12.34, 2345., -93.2, 10.44
  5. value2:
  6. .float 39.234, 21.4, 100.94, 10.56
  7. .section .bss
  8. .lcomm result, 16
  9. .section .text
  10. .globl _start
  11. _start:
  12. nop
  13. movaps value1, %xmm0
  14. movaps value2, %xmm1
  15.  
  16. addps %xmm1, %xmm0
  17. sqrtps %xmm0, %xmm0
  18. maxps %xmm1, %xmm0
  19. movaps %xmm0, result
  20.  
  21. movl $60, %eax
  22. movl $0, %ebx
  23. syscall
 

把單精度浮點值加載到XMM寄存器中。並執行基本運算操作,XMM0結果被傳送回內存中使用標籤result標記的位置中。

as -g -o ssemath.o ssemath.s

ld -o ssemath ssemath.o

使用gdb進行過程中的查看:

(gdb) print $xmm0

$3 = {v4_float = {12.3400002, 2345, -93.1999969, 10.4399996}, v2_double = {5.6101725574714474e+24, 754974.88032836909}, v16_int8 = {-92, 112, 69, 65, 0, -112, 18, 69, 102, 102, -70,

    -62, 61, 10, 39, 65}, v8_int16 = {28836, 16709, -28672, 17682, 26214, -15686, 2621, 16679}, v4_int32 = {1095069860, 1158844416, -1027971482, 1093077565}, v2_int64 = {

    4977198868967288996, 4694732396933310054}, uint128 = 86602527020781775578804592404012036260}

(gdb) print $xmm1

$4 = {v4_float = {39.2340012, 21.3999996, 100.940002, 10.5600004}, v2_double = {228170145.05651563, 817889.63044647221}, v16_int8 = {-98, -17, 28, 66, 51, 51, -85, 65, 72, -31, -55,

    66, -61, -11, 40, 65}, v8_int16 = {-4194, 16924, 13107, 16811, -7864, 17097, -2621, 16680}, v4_int32 = {1109192606, 1101738803, 1120526664, 1093203395}, v2_int64 = {

    4731932128728379294, 4695272830521696584}, uint128 = 86612496260875578387995842694967259038}

相加addps後:

(gdb) s

18     sqrtps %xmm0, %xmm0

(gdb) print $xmm0

$5 = {v4_float = {51.5740013, 2366.3999, 7.74000549, 21}, v2_double = {6.014405302368266e+24, 201326624.48375034}, v16_int8 = {-57, 75, 78, 66, 102, -26, 19, 69, 32, -82, -9, 64, 0,

    0, -88, 65}, v8_int16 = {19399, 16974, -6554, 17683, -20960, 16631, 0, 16808}, v4_int32 = {1112427463, 1158932070, 1089973792, 1101529088}, v2_int64 = {4977575340048010183,

    4731031409642679840}, uint128 = 87272125618359850373392885123090631623}

sqrtps後,求平方:

(gdb) s

19     maxps %xmm1, %xmm0

(gdb) print $xmm0

$6 = {v4_float = {7.18150425, 48.6456566, 2.78208661, 4.5825758}, v2_double = {159623578059.61627, 1193.1154792614809}, v16_int8 = {-30, -50, -27, 64, 39, -107, 66, 66, -75, 13, 50,

    64, 118, -92, -110, 64}, v8_int16 = {-12574, 16613, -27353, 16962, 3509, 16434, -23434, 16530}, v4_int32 = {1088802530, 1111659815, 1077022133, 1083352182}, v2_int64 = {

    4774542550791212770, 4652962192817262005}, uint128 = 85832002755546427910696780764134362850}

maxps後,計算兩個打包值中最大值:

(gdb) s

20     movaps %xmm0, result

(gdb) print $xmm0

$7 = {v4_float = {39.2340012, 48.6456566, 100.940002, 10.5600004}, v2_double = {159623578681.87201, 817889.63044647221}, v16_int8 = {-98, -17, 28, 66, 39, -107, 66, 66, 72, -31,

    -55, 66, -61, -11, 40, 65}, v8_int16 = {-4194, 16924, -27353, 16962, -7864, 17097, -2621, 16680}, v4_int32 = {1109192606, 1111659815, 1120526664, 1093203395}, v2_int64 = {

    4774542550811602846, 4695272830521696584}, uint128 = 86612496260875578388038453117050482590}

movaps後,result得到結果:

(gdb) x /4f &result

0x600100 <result>: 39.2340012  48.6456566  100.940002  10.5600004

 

比較指令

SSE的比較指令同MMX比較指令類似,單獨比較128位打包單精度浮點值的每個元素。

其中CMPSS有3個操作數

CMPPS imp, source,destination

imp可以有如下:

結果是位掩碼,存放在寄存器XMM0中。

Gas彙編器提供了替代imp操作數的僞指令,如下:

比較示例

 
  1. .section .data
  2. .align 16
  3. value1:
  4. .float 12.34, 2345., -93.2, 10.44
  5. value2:
  6. .float 12.34, 21.4, -93.2, 10.45
  7. .section .bss
  8. .lcomm result, 16
  9. .section .text
  10. .globl _start
  11. _start:
  12. nop
  13. movaps value1, %xmm0
  14. movaps value2, %xmm1
  15.  
  16. cmpeqps %xmm1, %xmm0
  17. movaps %xmm0, result
  18.  
  19. movl $60, %eax
  20. movl $0, %ebx
  21. syscall
 

as -g -o ssecomp.o ssecomp.s

ld -o ssecomp ssecomp.o

比較前:

(gdb) print $xmm0

$1 = {v4_float = {12.3400002, 2345, -93.1999969, 10.4399996}, v2_double = {5.6101725574714474e+24, 754974.88032836909}, v16_int8 = {-92, 112, 69, 65, 0, -112, 18, 69, 102, 102, -70,

    -62, 61, 10, 39, 65}, v8_int16 = {28836, 16709, -28672, 17682, 26214, -15686, 2621, 16679}, v4_int32 = {1095069860, 1158844416, -1027971482, 1093077565}, v2_int64 = {

    4977198868967288996, 4694732396933310054}, uint128 = 86602527020781775578804592404012036260}

(gdb) print $xmm1

$2 = {v4_float = {12.3400002, 21.3999996, -93.1999969, 10.4499998}, v2_double = {228170144.635625, 760217.88032836909}, v16_int8 = {-92, 112, 69, 65, 51, 51, -85, 65, 102, 102, -70,

    -62, 51, 51, 39, 65}, v8_int16 = {28836, 16709, 13107, 16811, 26214, -15686, 13107, 16679}, v4_int32 = {1095069860, 1101738803, -1027971482, 1093088051}, v2_int64 = {

    4731932128714256548, 4694777433960375910}, uint128 = 86603357807293900154403331565622227108}

比較後:

(gdb) print $xmm0

$3 = {v4_float = {-nan(0x7fffff), 0, -nan(0x7fffff), 0}, v2_double = {2.1219957904712067e-314, 2.1219957904712067e-314}, v16_int8 = {-1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0,

    0, 0, 0}, v8_int16 = {-1, -1, 0, 0, -1, -1, 0, 0}, v4_int32 = {-1, 0, -1, 0}, v2_int64 = {4294967295, 4294967295}, uint128 = 79228162495817593524129366015}

 (gdb) x /4x &result

0x600100 <result>: 0xffffffff  0x00000000  0xffffffff  0x00000000

表示第一個和第三個整數相等。

sse整數指令

sse提供處理64位打包整數值的一些擴展特性。擴展了MMX提供的功能。如下:

第43部分-Linux x86 64位彙編SSE2指令

SS2架構擴展了SSE指令。

SSE2使用128位XMM寄存器保存2個雙精度,4個雙字整數值或者2個四字整數值。

傳送數據

提供了5條指令

處理數據

SSE2加法指令:

示例

 
  1. .section .data
  2. .align 16
  3. value1:
  4. .double 10.42, -5.330
  5. value2:
  6. .double 4.25, 2.10
  7. value3:
  8. .int 10, 20, 30, 40
  9. value4:
  10. .int 5, 15, 25, 35
  11. .section .bss
  12. .lcomm result1, 16
  13. .lcomm result2, 16
  14. .section .text
  15. .globl _start
  16. _start:
  17. nop
  18. movapd value1, %xmm0
  19. movapd value2, %xmm1
  20. movdqa value3, %xmm2
  21. movdqa value4, %xmm3
  22.  
  23. mulpd %xmm1, %xmm0
  24. paddd %xmm3, %xmm2
  25.  
  26. movapd %xmm0, result1
  27. movdqa %xmm2, result2
  28.  
  29. movl $1, %eax
  30. movl $0, %ebx
  31. int $0x80
 

as -g -o sse2math.o sse2math.s

ld -o sse2math sse2math.o

MOVAPD和MOVDQA指令加載XMM寄存器必須對準16字節邊界。

(gdb) print $xmm0

$2 = {v4_float = {0.0587499999, 2.57562494, -7.46297859e-36, -2.33312488}, v2_double = {10.42, -5.3300000000000001}, v16_int8 = {-41, -93, 112, 61, 10, -41, 36, 64, 82, -72, 30,

    -123, -21, 81, 21, -64}, v8_int16 = {-23593, 15728, -10486, 16420, -18350, -31458, 20971, -16363}, v4_int32 = {1030792151, 1076156170, -2061584302, -1072344597}, v2_int64 = {

    4622055556569408471, -4605684971923916718}, uint128 = 255322474959727810128669020838031303639}

(gdb) print $xmm1

$3 = {v4_float = {0, 2.265625, -107374184, 2.01249981}, v2_double = {4.25, 2.1000000000000001}, v16_int8 = {0, 0, 0, 0, 0, 0, 17, 64, -51, -52, -52, -52, -52, -52, 0, 64},

  v8_int16 = {0, 0, 0, 16401, -13107, -13108, -13108, 16384}, v4_int32 = {0, 1074855936, -858993459, 1073794252}, v2_int64 = {4616471093031469056, 4611911198408756429},

  uint128 = 85074745567721443736252296162778808320}

(gdb) print $xmm2

$4 = {v4_float = {1.40129846e-44, 2.80259693e-44, 4.20389539e-44, 5.60519386e-44}, v2_double = {4.2439915824246103e-313, 8.4879831653432862e-313}, v16_int8 = {10, 0, 0, 0, 20, 0, 0,

    0, 30, 0, 0, 0, 40, 0, 0, 0}, v8_int16 = {10, 0, 20, 0, 30, 0, 40, 0}, v4_int32 = {10, 20, 30, 40}, v2_int64 = {85899345930, 171798691870},

  uint128 = 3169126501123975826038943907850}

(gdb) print $xmm3

$5 = {v4_float = {7.00649232e-45, 2.1019477e-44, 3.50324616e-44, 4.90454463e-44}, v2_double = {3.1829936866949413e-313, 7.4269852696136172e-313}, v16_int8 = {5, 0, 0, 0, 15, 0, 0,

    0, 25, 0, 0, 0, 35, 0, 0, 0}, v8_int16 = {5, 0, 15, 0, 25, 0, 35, 0}, v4_int32 = {5, 15, 25, 35}, v2_int64 = {64424509445, 150323855385},

  uint128 = 2772985688460420417681201561605}

調用mulpd後,2個雙精度浮點值的乘法操作,xmm0寄存器如下:

(gdb) print $xmm0

$6 = {v4_float = {-2.30215358e+20, 3.09597635, -6.61886922e+22, -2.59978104}, v2_double = {44.284999999999997, -11.193000000000001}, v16_int8 = {20, -82, 71, -31, 122, 36, 70, 64,

    -118, 65, 96, -27, -48, 98, 38, -64}, v8_int16 = {-20972, -7865, 9338, 16454, 16778, -6816, 25296, -16346}, v4_int32 = {-515396076, 1078338682, -446676598, -1071226160},

  v2_int64 = {4631429376981315092, -4600881319971372662}, uint128 = 255411086697915565509973403936689204756}

執行paddd後,四個雙字整數加法

(gdb) print $xmm2

$7 = {v4_float = {2.1019477e-44, 4.90454463e-44, 7.70714155e-44, 1.05097385e-43}, v2_double = {7.4269852691195516e-313, 1.5914968434956903e-312}, v16_int8 = {15, 0, 0, 0, 35, 0, 0,

    0, 55, 0, 0, 0, 75, 0, 0, 0}, v8_int16 = {15, 0, 35, 0, 55, 0, 75, 0}, v4_int32 = {15, 35, 55, 75}, v2_int64 = {150323855375, 322122547255},

  uint128 = 5942112189584396243720145469455}

最後結果:

(gdb) x /4wd &result2

0x600150 <result2>:    15   35   55   75

(gdb) x /2gf &result1

0x600140 <result1>:    44.284999999999997 -11.193000000000001

 

SSE3指令

SSE3相比SSE2沒有提供任何新的數據類型,僅僅添加了幾條指令。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章