Opcode syntax

来自osdev
Zhang3讨论 | 贡献2022年2月6日 (日) 14:00的版本 (创建页面,内容为“AT&T语法 (如GAS所理解的,GNU汇编器) 是大多数非Intel平台上的标准语法,但在x86平台上仍然很少见。 但是,AT&T语法是GCC 内联汇编 的默认语法,这是objdump在调试内核时将为你提供的语法。 NASM和FASM使用Intel语法,Intel语法是 Bochs 调试器在调试你的内核时提供的。 == 重要细节 == AT&T语法和Intel语法之间存在一些实质性差异,打算使用GN…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索

AT&T语法 (如GAS所理解的,GNU汇编器) 是大多数非Intel平台上的标准语法,但在x86平台上仍然很少见。 但是,AT&T语法是GCC 内联汇编 的默认语法,这是objdump在调试内核时将为你提供的语法。

NASM和FASM使用Intel语法,Intel语法是 Bochs 调试器在调试你的内核时提供的。

重要细节

AT&T语法和Intel语法之间存在一些实质性差异,打算使用GNU工具的程序员应该意识到这一点。 转到AT&T语法时,需要寻找以下几个关键内容:

  • 区分大小写: MOVL与movl并不相同。
  • 数值基数: 用C语言风格表示: 1表示十进制,01表示八进制,0x01表示十六进制。(不支持英特尔postfix-h语法。)
  • 转义字符: 特殊字符被写成C风格的转义 (\n,\",\#, \\,...)。
  • 注释: C样式 (/* ... */) 或shell样式 (# ...)。
  • 指令语法: 指令以句点开始 (“.align 4” 指在32位边界上对齐,“.word 0x1234” 等同于 “DW 1234h”)。
  • 字符串: 使用特殊指令定义 .ascii (或.asciz用于零端字符串)。示例: msg: .ascii "Hello, World!\n"”
  • 当前位置地址: 用句点 (".",相当于Intel语法 "$") 表示。
  • 初始化内存: 使用.fill完成 (大致相当于Intel语法 'times db')。 例: .fill 0x1fe - (. - START) , 1, 0 (其中 '1' 是以字节为单位的大小填充掩码,START是标记代码入口点的标签) 等于Intel语法 times 1FEh - ($-$$) db 0。 (.skip和.space指令可以以类似的方式使用。)
  • 代码计数器 可以多次设置,使用.org指令 (如.org 0x1fe START,其中START是标记代码入口点的标签。 位置分配指令 '.=' 可以以相同的方式使用。
  • 16/32 bit code 可以通过 .code16 或 .code32 (分别相当于Intel语法 [BITS 16] 和 [BITS 32])来生成。
  • 目标CPU: 使用.arch指令设置。 即使您确定默认值为 “i386”,也可以设置它。
  • 标签声明: 总是以冒号结尾。
  • 新标识符 假定出现在行的开头,而不是以冒号结尾,是等价语句的一部分,并且必须后跟等号和赋值。示例: FOO = 0xF00
  • 指令结束: 用换行符或用分号指定; 后者主要在宏中看到,以允许多行代码。
  • 换行继续: 与C一样,用反斜杠 ('\') 作为一行中的最后一个字符。这也主要用于宏中。
  • 寄存器: 始终以百分号为前缀: %eax,%cs,%esp等。
  • 来源(Source),目的地(Destination): 移动(Move),加载(Load),存储(Store)和类似的操作始终具有 “源,目的地” 顺序的操作数,这与Intel语法非常不同。 因此,“movl %eax, %ebx” 将%eax的值移动到, %ebx。 这是似乎最使人困惑的部分,因为它几乎与Intel语法相反:
    Opcode    Register/Memory-being-modified, Data, Data
  • 操作数大小后缀: 后缀始终附加到指令 (x86上的ljmp、lcall和lret除外): movb表示 “move字节”,movw表示 “move字”,movl表示 “move长整形” 等。
  • 直接地址操作数: 直接地址操作数没有前缀。因此,"movl foo, %eax" 将内存位置 “foo” 的内容移动到 %eax。
  • 立即操作数: 立即操作数以美元符号 ($) 为前缀: “pushl $4” 将0x00000004推到堆栈上。这也适用于标签: "movl $foo, %eax" 将标签foo的值 (即变量foo的地址) 移动到 %eax。
  • 索引/间接操作数: 索引/间接操作数语法: displacement (base, index, scale),如: movl %eax, %ss:8(%ebp, 2, 3) (相当于Intel语法 mov dword [ss:ebp + 2 * 3 + 8], eax,也就是说,它将 %eax的值移动到段%ss中的偏移 (%ebp + (2 *3) + 8)。 间接地址的五个操作数中的任何一个都可以省略。
  • 相对寻址: 相对寻址默认情况下在所有跳转和调用指令中使用。要使用绝对寻址,操作数必须以星号 (*) 为前缀。
  • Far jumps / calls / returns: 这些使用特殊的操作码 “ljmp” 、 “lcall” 和 “lret”。

AT&T语法的宏格式:

.macro <name> <args>
<operations>
.endm

示例:

.macro write string
   movw string, %si
   call printstr
.endm

这将等同于NASM宏:

%macro write 1
   mov si, %1
   call printstr
%endmacro

此外,cpp和 M4 宏预处理器通常用于宏处理。

将小代码片段从Intel语法转换为AT&T

你可以使用以下脚本将代码的短片段 (一行) 从Intel语法转换为AT&T语法:

#!/bin/bash
set -e

# Usage:
#
# ./inteltoatt [16|32|64] "mov eax, eax \n xor ecx, edx"
#

case "$1" in
16|32|64)
	bits="$1"
	shift ;;
*)
	bits="32" ;;
esac
code="$1"

nasm="$(mktemp)"
obj="$(mktemp)"
objdump="$(mktemp)"

case "$bits" in
	16) m="i8086"       ;;
	32) m="i386"        ;;
	64) m="i386:x86-64" ;;
esac

echo -e "BITS $bits\n$code" > "$nasm"

nasm "$nasm" -o "$obj"
objdump -D -b binary -m $m -Maddr${bits},data${bits} "$obj" > "$objdump"

lineno="$(egrep -m 1 -n '<\.data>\:$' "$objdump" | cut -d':' -f1)"
lineno=$((lineno+1))

tail -n +$lineno "$objdump"

另见

外部链接