gas學習 by Jian Lee
- 利用 cpuid 取得 intel 集容cpu的廠商信息。
.section .data
output1:
.ascii "當前CPU廠商ID是:"
output2:
.ascii "xxxxxxxxxxxx\n"
.section .text
.global _start
_start:
movl $0,%eax
cpuid
movl $output2,%edi
movl %ebx,(%edi)
movl %edx,4(%edi)
movl %ecx,8(%edi)
movl $4,%eax
movl $1,%ebx
movl $output1,%ecx
movl $(output2-output1+14),%edx
int $0x80
movl $1,%eax
movl $0,%ebx
int $0x80
編譯和鏈接上面的源代碼並運行:
as -o cpuid.o cpuid.s # 彙編源代碼為目標文件
ld -o cpuid cpuid.o # 鏈接目標文件為linux可執行的文件
./cpuid # 執行
1.1 基本gas程序模板
gas程序分為幾個部分,通常有data段和text段。data段存放數據,其 內容會被放在最終的可執行程序中。text段是彙編指令。通常還有 bss段,這裡相當於緩存,臨時創建。
.section .data
...
.section .text
...
1.2 gas彙編程序偽指令
一般彙編程序本身會提供一些方便的"指令",但是這些"指令"不是 cpu指令,它們只是方便彙編程序開發者,解釋權歸彙編程序本身。
gas中的“偽指令”都是以 "." 開頭,如上面示例中的一些偽指令:
.section 定義一個段
.data 同.section一起定義數據段
.ascii 定義一個字符串
.text 同.section一起定義程序段
.global 定義全局標籤
1.3 gas的標籤
在gas中的標籤等價於一個內存地址,相當於C語言中的指針。不過C語 言的指針還有數據類型屬性,這裡的數據類型需要自己指定了。
gas中的標籤是以冒號結尾的字符串。如示例中的:
output1:
output2:
既然gas中的標籤是內存地址,這是一個數值。那麼,我們可以在gas 程序中對標籤進行算術運算,且對於“立即數”能出現的地方,標籤也 都可以出現:
movl $(output2-output1+14),%
1.4 gas的開始標籤
如果用 ld 手動鏈接程序,則需要在彙編程序中設置一個為 _start: 的開始標籤,相當於C程序中的main函數。如果用gcc編譯彙編程序, 那麼要將"_start:" 換成 "main:"
如果沒有指定 "_start" 標籤,可以在運新 ld 命令的時候指定開始標籤:
ld -e
1.5 gas中的系統調用
Linux下的彙編程序可以使用系統調用,這樣可以避免很多複雜的工作。 在gas裡使用系統調用就是在相關寄存器存入系統調用號、調用函數的 參數,再執行 "int $0x80" 命令就可以了。
上例使用了兩個系統調用函數: write 和 exit
1.6 基本 AT&T 彙編風格
- 立即數 表示立即數要用"$"符號
- 寄存器 表示寄存器要用"%"符號
- 內存尋址 內存地址要放在寄存器裡,才能被尋址,這是intel的cpu指令集規定的。
movl %ebx,(%edi) # 將ebx裡面的內容放到edi指向的內存地址單元處。 movl %edx,4(%edi) # 將edx裡面的內容放到比edi指向的內存地址高4個值的內存單元處。
2. 使用標準C庫函數
- 使用標準C庫函數的 cpuid2.s 程序:
.section .data
output:
.asciz "當前CPU廠商信息是:%s \n"
.section .bss
.lcomm buffer,12
.section .text
.global _start
_start:
movl $0,%eax
cpuid
movl $buffer,%edi
movl %ebx,(%edi)
movl %edx,4(%edi)
movl %ecx,8(%edi)
pushl $buffer
pushl $output
call printf
addl $8,%esp
pushl $0
call exit
彙編、鏈接並運行:
as -o cpuid2.o cpuid2.s
ld -dynamic-linker /lib/ld-linux.so.2 -o cpuid2 -lc cpuid2.o
參數說明:
-lc 鏈接C庫/lib/libc.so;如果是-lx,默認鏈接/lib/libx.so。
-dynamic-linker /lib/ld-linux.so.2 使用/lib/ld-linux.so.2加載共享庫。
. 定義數據元素
3.1 定義數據元素的命令
.ascii 文本字符串
.asciz 帶零結束符的文本字符串
.byte 字節值
.double 雙精度值
.float 單精度值
.int 32位整數
.long 同.int
.octa 16字節長度整數
.quad 8字節長整數
.short 16位整數
.single 同.float
有幾種數據段:
.section .data 定義通常的數據段,數據被包含在最終程序中
.section .rodata 同.data,但是這裡定義的數據的值不可修改
.section .bss 相當於緩衝
數據定義可以一個標籤一個命令一個數據的定義:
output:
.asciz "這是一個.asciz定義的字符串"
value:
.int 100
也可以,一個標籤一個命令定義一堆數據:
values:
.int 15,20,25,30,35,40,45,50,55,60
無論是哪種形式定義的,數據在內存中都是一個挨著一個的存放的。 這樣我們可以用索引來引用它們。
3.2 賦值命令
gas中也可以用一個符號代表一個值,但是符號只不可修改:
.equ factor,3
.equ LINUX_SYS_CALL,0x80
3.3 bss段
這個段無須聲明特定的類型,只要聲明大小:
.comm 聲明未初始化數據的通用內存區域
.lcomm 聲明未初始化數據的"本地"通用內存區域
例如,聲明一個1000字節的緩衝區,通過buffer引用這個區域的基址:
.section .bss
.lcomm buffer,1000
3.4 .fill 命令
這個命令讓彙編器自動創建一段內存區域,並用0填充:
.section .data
buffer:
.fill 1000
4. MOV命令
gas中把mov命令加了不同後綴,每個後綴表示操作不同的數據大小:
movl 32位
movw 16
movb 8
下面一個例子把一個值移到ecx寄存器,然後調用linux系統的exit正 常退出。用gdb可以看見寄存器的變化。
# 程序 movetest1.s
.section .data
value:
.int 1
.section .text
.global _start
_start:
nop
movl value,%ecx
movl $1,%eax
movl $0,%ebx
int $0x80
用-gtabs參數彙編程序並鏈接:
as -gtabs -o movetest1.o movetest1.s
ld -o movetest1 movetest1.o
使用gdb調試程序:
root@jianlee:~/lab/asm# gdb -q movetest1
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file movetest1.s, line 8.
(gdb) run
Starting program: /root/lab/asm/movetest1
Breakpoint 1, _start () at movetest1.s:8
8 movl value,%ecx
Current language: auto; currently asm
(gdb) print/x %ecx
A syntax error in expression, near `%ecx'.
(gdb) print/x $ecx
$1 = 0x0
(gdb) next
_start () at movetest1.s:9
9 movl $1,%eax
(gdb) print/x $ecx
$2 = 0x1
(gdb) s
_start () at movetest1.s:10
10 movl $0,%ebx
(gdb) cont
Continuing.
Program exited normally.
(gdb)
把寄存器的值傳到內存裡:
# movetest2.s 把寄存器的值傳到內存中
.section .data
value:
.int 1
.section .text
.global _start
_start:
nop
movl $100,%eax
movl %eax,value
movl $1,%eax
movl $0,%ebx
int $0x80
調試程序,查看程序執行過程:
root@jianlee:~/lab/asm# as -gtabs -o movetest2.o movetest2.s
movetest2.s: Assembler messages:
movetest2.s:0: Warning: end of file not at end of a line; newline inserted
root@jianlee:~/lab/asm# ld -o movetest2 movetest2.o
root@jianlee:~/lab/asm# gdb -q movetest2
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file movetest2.s, line 9.
(gdb) run
Starting program: /root/lab/asm/movetest2
Breakpoint 1, _start () at movetest2.s:9
9 movl $100,%eax
Current language: auto; currently asm
(gdb) print/x $eax
$1 = 0x0
(gdb) x/d &value
0x804908c <value>: 1
(gdb) s
_start () at movetest2.s:10
10 movl %eax,value
(gdb) print/x $eax
$2 = 0x64
(gdb) x/d &value
0x804908c <value>: 1
(gdb) s
_start () at movetest2.s:12
12 movl $1,%eax
(gdb) x/d &value
0x804908c <value>: 100
(gdb) cont
Continuing.
Program exited normally.
(gdb)