两类风格汇编语法对比

前言

  • 机器指令是用二进制代码表示的CPU能直接识别和执行的一种指令系统的集合,不同的CPU架构有不同的机器指令。汇编指令是机器指令便于记忆的书写格式,汇编指令编写完成后通过汇编器将其翻译成机器指令供CPU执行,因此,汇编器的功能是将汇编指令翻译成机器指令。
  • 同一条机器指令,可以用不同的汇编指令表达,只要汇编器最终能正确无误地翻译就可以。不同的汇编指令格式衍生出不同的汇编语法,针对每种汇编语法都有一个与之对应的汇编器。没有汇编器可以将所有类型的汇编语法都正确翻译成机器指令。因此,随着计算机的发展,不同厂家形成了自成一派的汇编语言,并有自己的汇编器。不同的汇编语言,实现相同的机器指令可能语法不一样。
  • 常见的汇编器有GNU Assembler (GAS),Microsoft Macro Assembler (MASM),Netwide Assembler (NASM),Flat Assembler (FASM)等,不同汇编器有不同的汇编语言,GAS汇编器使用AT&T汇编语法,MASM使用Intel汇编语法,NASM使用的汇编语法和Intel汇编语法类似,但比Intel的简单,每种汇编语法都有自己的手册可以查阅。GASAT&T语法可以查询GNU Tools ManualsNASM的语法可以查询NASM官网
  • 本文对比的是AT&T汇编语法和NASM汇编语法,除此之外,还介绍了内联汇编的基本语法

汇编(Assembly)

1.表达式

注释

ATT:# 开头
NASM:; 开头

立即数

ATT: 十六进制 $0xff
NASM:十六进制 0ffh

寄存器引用

ATT:引用寄存器需要加%
NASM:引用寄存器什么都不加

段寄存器引用

ATT:引用es:di中的值
%es:(%di)
NASM:引用es:di中的值
[es:di]

指定操作数长度

ATT:在指令后面加X字母
X可选值:
l - 32bit, w - 16bit, b - 8bit

NASM:在立即数或者其它不确定长度的内存地址前加单词X
X可选值:
dword - 32bit, word - 16bit, byte - 8bit

标号定义

定义一个16bit数据
ATT:
wSectorNo:
	.word   0	
NASM:
	wSectorNo       dw  0

取标号处的值

将标号wSectorNo处的值读取到ax中
ATT:
movw    wSectorNo, %ax	// 直接引用
NASM:
mov ax, [wSectorNo]

取标号处的地址

将标号2处的地址取出,保存到eax中
ATT:加'$'就是取地址
movl    $2f, %eax		//需要加$
2:
	xxxxxxx
NASM:标号本身就是地址
mov dword eax, .2
.2:
	xxxxxxx

2.数学指令

比较指令

比较标号bOdd处的1个字节是否为1
ATT:
movb    bOdd, %al
cmpb    $1, %al

NASM:
cmp byte [bOdd], 1

逻辑右移

ax右移4bit
ATT:
shrw 	$4, %ax	
NASM:
shr 	ax, 4

3.数据传输

mov指令格式

ATT:
mov src,  dst
NASM:
mov dst,  src

4.执行流控制

相对跳转

语法相同
jmp label

绝对跳转

跳转到9000:100地址处,BaseOfLoader=0x9000,OffsetOfLoader=0x100
ATT:
ljmp    $BaseOfLoader, $OffsetOfLoader
NASM:
jmp BaseOfLoader:OffsetOfLoader 

跳转到标号处

ATT:
向前跳转到标号1处,f表示forward
jmp 1f
1:
	xxxxxxx
向后跳转到标号2处,b表示back
2:
	xxxxxxx
jmp 2b
NASM:
没有前后之分
跳转到标号.0处
.0:
    xxxxxxx
    jmp .0

长跳转

ATT:
把SEG32_MODE32_CS装入CS,0x12345装入EIP,并跳转到SEG32_MODE32_CS:$0x12345
ljmpl $SEG32_MODE32_CS, $0x12345
NASM:
把SelectorCode32装入CS,把0装入EIP,并跳转到 SelectorCode32:0  处
jmp dword SelectorCode32:0

5.宏定义

多行宏

ATT:
.macro name arg1, arg2, arg3...
宏定义内容
.endm
举例:段描述符
# 代码段/数据段描述符
# usage: Descriptor Base, Limit, Attr
#        Base:  .long   4字节长度
#        Limit: .long   4字节长度
#        Attr:  .short  2字节长度
.macro  Descriptor Base=0x0, Limit=0xffffffff, Attr
    .short  \Limit & 0xffff
    .short  \Base & 0xffff
    .byte   (\Base >> 16) & 0xff
    .short  ((\Limit >> 8) & 0xf00) | (\Attr & 0x0f0ff)
    .byte   (\Base >> 24) & 0xff
.endm
宏的参数可以设置默认值
宏的内部引用参数时,使用'\'表示引用的参数

NASM:
%macro name nargs
宏定义内容
%endmacro
举例:段描述符
; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
    dw  %2 & 0FFFFh             ; 段界限1
    dw  %1 & 0FFFFh             ; 段基址1
    db  (%1 >> 16) & 0FFh           ; 段基址2
    dw  ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2
    db  (%1 >> 24) & 0FFh           ; 段基址3
%endmacro ; 共 8 字节
宏的内部引用参数时,使用%n表示引用的参数

内联汇编(Inline Assembly in Linux C)

example

查看cpu信息

/* cpuid.c */
/* gcc -o cpuid cpuid.c */
#include <stdio.h>
#include <stdlib.h>

static inline void cpuid(unsigned int index,
                         unsigned int *eax,
                         unsigned int *ebx,
                         unsigned int *ecx,
                         unsigned int *edx)
{
    asm("cpuid"
        : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
        : "0" (index));
}

void main(void)
{
    unsigned int eax, ebx, ecx, edx;
    cpuid(0, &eax, &ebx, &ecx, &edx);
    printf("eax = %x\n", eax);
    printf("ebx = %x\n", ebx);
    printf("ecx = %x\n", ecx);
    printf("edx = %x\n", edx);
}

运行结果
在这里插入图片描述
结果分析参考intel手册volume 2,chapter 3,3.2章节中对CPUID指令的输出介绍

基本格式

 asm [ volatile ] (  
         assembler template
         [ : output operands ]                /* optional */
         [ : input operands  ]                /* optional */
         [ : list of clobbered registers ]    /* optional */
         );
  • asm
    gcc关键字,表示接下来要嵌入汇编代码。为避免keyword asm与程序中其它部分产生命名冲突,gcc还支持__asm__关键字,与asm的作用等价
  • volatile
    可选,表示不需要gcc对下面的汇编代码做任何优化。同样出于避免命名冲突的原因,__volatile__也是gcc支持的与volatile等效的关键字
  • output operands
    可选,指明输出操作数,典型格式:
    "=a" (output)
    其中,"=a"指定output operand的应遵守的约束(constraint),output为存放指令结果的变量,通常是个C语言变量。"="是输出操作特有约束,表示操作数是只写的(write-only),表达式的意思是先将命令执行结果输入到eax寄存器中,然后再由寄存器eax更新位于内存中的output
  • input operands
    可选,指明输出操作数,典型格式:
    "constraint" (input)
    其中,constraint指定input operand的应遵守的约束(constraint),input为存放输入的变量,通常是个C语言变量。比如:
    asm("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (index));
    表达式的 input operands是"0" (index),"0"是关联约束,在有些情况下,输入和输入需要用到同一个寄存器,就可以指定以matching constraint方式分配寄存器,此时,input 和 output共用一个中转寄存器。代码中指定输入匹配第0个输出,则用eax作为中转寄存器,那么index作为输入,在cpuid指令运行前,需要将其值写入eax中。

常用约束

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