第35部分-Linux x86 64位彙編 系統調用
如果對Linux內核有一定的瞭解,那麼理解起來會非常簡單。系統調用是用戶態程序陷入到內核態的API接口。
每個系統調用都有一個名稱和調用號,調用號一旦確認就基本不會改變。現在Linux系統調用有三百多個。
64位系統中可以在文件中找到:
arch/x86/entry/syscalls/syscall_64.tbl
文件定義的是
在Linux環境中可以通過man命令來獲取。例如
man 2 exit
其中2表示man頁的第二部分。
系統調用格式
我們已經知道EAX寄存器用於保存系統調用值。定義使用內核支持的系統調用中的哪個系統調用。
系統調用示例
-
.section .data
-
output:
-
.ascii "This is a test message.\n"
-
len:
-
.int 24
-
.section .text
-
.globl _start
-
_start:
-
movq $1, %rax
-
movq $1, %rdi
-
movq $output, %rsi
-
movq len, %rdx
-
syscall
-
-
movq $60, %rax
-
movq $0, %rdi
-
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.
示例
-
.section .data
-
result:
-
uptime:
-
.int 0,0
-
load1:
-
.int 0,0
-
load5:
-
.int 0,0
-
load15:
-
.int 0,0
-
totalram:
-
.int 0,0
-
freeram:
-
.int 0,0
-
sharedram:
-
.int 0,0
-
bufferram:
-
.int 0,0
-
totalswap:
-
.int 0,0
-
freeswap:
-
.int 0,0
-
procs:
-
.byte 0x00, 0x00
-
.section .text
-
.globl _start
-
_start:
-
nop
-
movq $result, %rdi
-
movq $99, %rax
-
syscall
-
-
movq $0, %rbx
-
movq $60, %rax
-
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 */
};
代碼如下:
-
.section .data
-
timespec:;//定義了timespec結構·,5表示秒,0表示0納秒。需要16個字節。
-
.int 5, 0,0,0
-
output:
-
.ascii "This is a test\n"
-
output_end:
-
.equ len, 15
-
temp:
-
.int 0
-
-
.section .bss;// 需要16個字節。
-
.lcomm rem,16
-
.section .text
-
.globl _start
-
_start:
-
nop
-
movq $10, %rcx
-
loop1:
-
movq %rcx, temp
-
movq $1, %rax
-
movq $1, %rdi
-
movq $output,%rsi
-
movq $len, %rdx
-
syscall
-
-
movq $35, %rax
-
movq $timespec, %rdi
-
movq $rem, %rsi
-
syscall
-
-
movq temp,%rcx
-
loop loop1
-
-
movq $60, %rax
-
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函數示例
-
.section .data
-
output:
-
.asciz "This is a test\n"
-
temp:
-
.int 0
-
.section .text
-
.globl _start
-
_start:
-
movq $10, %rcx
-
loop1:
-
movq %rcx, temp
-
movq $output,%rdi
-
call printf
-
movq $5, %rdi
-
call sleep
-
pop %rcx
-
movq temp,%rcx
-
loop loop1
-
movq $0,%rdi
-
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指令進行比較判斷。
-
.section .data
-
gotmmx:
-
.asciz "Supports MMX"
-
gotsse:
-
.asciz "Supports SSE"
-
gotsse2:
-
.asciz "Supports SSE2"
-
gotsse3:
-
.asciz "Supports SSE3"
-
output:
-
.asciz "%s\n"
-
.section .bss
-
.lcomm ecxdata, 4
-
.lcomm edxdata, 4
-
.section .text
-
.globl _start
-
_start:
-
nop
-
movl $1, %eax;//CPUID的1號功能
-
cpuid
-
movl %ecx, ecxdata
-
movl %edx, edxdata
-
-
test $0x00800000, %edx
-
jz done
-
-
movq $output,%rdi
-
movq $gotmmx,%rsi
-
-
call printf
-
-
movl edxdata, %edx
-
test $0x02000000, %edx
-
jz done
-
movq $output,%rdi
-
movq $gotsse,%rsi
-
call printf
-
-
movl edxdata, %edx
-
test $0x04000000, %edx
-
jz done
-
movq $output,%rdi
-
movq $gotsse2,%rsi
-
call printf
-
-
movl ecxdata, %ecx
-
test $0x00000001, %ecx
-
jz done
-
movq $output,%rdi
-
movq $gotsse3,%rsi
-
-
call printf
-
-
done:
-
movq $60,%rax
-
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架構需要一下步驟
- 從整數值創建打包整數值
- 把打包整數值加載到MMX寄存器中
- 對打包整數值執行MMX數學操作。
- 從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中的。
-
- MMX乘法示例
乘法比較困難,因爲乘法生產的結果可能會比輸入操作數大得多。乘法允許使用兩條指令完成乘法操作。
PMULL把每對打包字整數值相乘,結果的低16位存放到目標寄存器。
PMULH把每對打包字整數值相乘,結果的高16位存放到目標寄存器。
帶符號的是PMULLW和PMULHW,無符號的是PMULLUW和PMULHUW。
乘法系列中還有一個附加指令是PMADDWD指令。
MMX邏輯和移位
MMX中可用的布爾邏輯指令如下圖:
SOURCE可以是MMX寄存器或者64位的內存位置,目標必須是MMX寄存器。
MMX比較
兩個值比較指令如下圖:
比較結果存放到目標打包整數值中。
-
-
- 示例
-
比較的示例如下,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示例
-
.section .data
-
.align 16
-
value1:
-
.float 12.34, 2345., -93.2, 10.44
-
value2:
-
.float 39.234, 21.4, 100.94, 10.56
-
.section .bss
-
.lcomm result, 16
-
.section .text
-
.globl _start
-
_start:
-
nop
-
movaps value1, %xmm0
-
movaps value2, %xmm1
-
-
addps %xmm1, %xmm0
-
sqrtps %xmm0, %xmm0
-
maxps %xmm1, %xmm0
-
movaps %xmm0, result
-
-
movl $60, %eax
-
movl $0, %ebx
-
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操作數的僞指令,如下:
比較示例
-
.section .data
-
.align 16
-
value1:
-
.float 12.34, 2345., -93.2, 10.44
-
value2:
-
.float 12.34, 21.4, -93.2, 10.45
-
.section .bss
-
.lcomm result, 16
-
.section .text
-
.globl _start
-
_start:
-
nop
-
movaps value1, %xmm0
-
movaps value2, %xmm1
-
-
cmpeqps %xmm1, %xmm0
-
movaps %xmm0, result
-
-
movl $60, %eax
-
movl $0, %ebx
-
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加法指令:
示例
-
.section .data
-
.align 16
-
value1:
-
.double 10.42, -5.330
-
value2:
-
.double 4.25, 2.10
-
value3:
-
.int 10, 20, 30, 40
-
value4:
-
.int 5, 15, 25, 35
-
.section .bss
-
.lcomm result1, 16
-
.lcomm result2, 16
-
.section .text
-
.globl _start
-
_start:
-
nop
-
movapd value1, %xmm0
-
movapd value2, %xmm1
-
movdqa value3, %xmm2
-
movdqa value4, %xmm3
-
-
mulpd %xmm1, %xmm0
-
paddd %xmm3, %xmm2
-
-
movapd %xmm0, result1
-
movdqa %xmm2, result2
-
-
movl $1, %eax
-
movl $0, %ebx
-
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沒有提供任何新的數據類型,僅僅添加了幾條指令。