x86 您所在的位置:网站首页 gas汇编语法 x86

x86

2024-07-10 00:12| 来源: 网络整理| 查看: 265

x86-64汇编基础(x64)

Last Updated: 2023-11-23 15:21:37 Thursday

-- TOC --

汇编语法 Cast Operator(Intel) 寻址 汇编器指令(GAS) 操作数(Operands) Conditional Codes Intel和AT&T语法中,不同名称的相同指令

本文汇总一些个人学习x86-64(x64或AMD64)汇编过程中,遇到的一些基础知识。内容略显零乱,但都是干货!

学习x86-64寄存器,这部分内容,其实直接阅读AMD64手册是最好的! 汇编语法

一个烦人的现实,x86-64汇编代码没有统一的语法格式!

AT&T语法,寄存器前加%号,立即数前加$号,指令后面还有指示操作数宽度的后缀,比如movl,movq等。src在左,dst在右,例如:

movq src, dst

Intel语法,寄存器和立即数前没有prefix,指令后也没有suffix,但地址前都有说明按多少字节访问地址的信息(cast operator),比如byte ptr,word ptr,或dword ptr等等。src在右,dst在左,例如:

mov DWORD PTR [dst], src

如果用gas编译intel语法的汇编代码文件,需要在源文件中使用指令.intel_syntax noprefix,见后文。

objdump -d默认输出AT&T语法,使用-M intel参数就可以切换到Intel语法。(详解objdump命令)

Intel语法用[]表示地址,ATT语法用()表示。

In Intel syntax the base register is enclosed in [ and ] where as in AT&T they change to ( and ). Additionally, in Intel syntax an indirect memory reference is like section:[base+index*scale+disp], which changes to section:disp(base,index,scale) in AT&T。(这是最一般的格式,scale只能取1,2,4或8)

One point to bear in mind is that, when a constant is used for disp/scale, $ shouldn’t be prefixed。(disp或scale使用立即数时,不需要$前缀)

个人倾向使用Intel语法,因为Intel语法更符合C语言风格。可这个世界很复杂,除了AT&T语法,还有nasm语法...

Cast Operator(Intel) PTR,表示指针,pointer的简写。 BYTE: 字节,8位。计算机的最小存储单元。 WORD: 字,2bytes,16位(由于Intel的x86系列芯片起源与8086,那时的word只有16bit,因此word在x86的世界里,就是16bits,一直沿用至今) DWORD: 双字,4bytes,32位 QWORD: 8bytes,64位 XMMWORD:16bytes,128位 寻址

movl (addr), %eax,将addr所在位置的4字节存入%eax。label都是addr!

movl data_items(,%edi,4), %eax,地址为%edi*4+dataitems。

例如movl 4(%eax), %ebx,用于访问结构体成员比较方便,例如一个结构体的基地址保存在eax寄存器中,其中一个成员在结构体内偏移量是4字节,要把这个成员读上来就可以用这条指令。

或者movl (%eax), %ebx,把eax寄存器的值看作地址,把这个地址处的32位数传送到ebx寄存器。有人说这叫间接寻址。

举例:

# 把立即数4存入rbp-4这个地址,movl,后缀l表示long,4bytes的DWORD movl $4, -4(%rbp) +------------------------------+------------------------------------+ | Intel Code | AT&T Code | +------------------------------+------------------------------------+ | mov eax,1 | movl $1,%eax | | mov ebx,0ffh | movl $0xff,%ebx | | int 80h | int $0x80 | | mov ebx, eax | movl %eax, %ebx | | mov eax,[ecx] | movl (%ecx),%eax | | mov eax,[ebx+3] | movl 3(%ebx),%eax | | mov eax,[ebx+20h] | movl 0x20(%ebx),%eax | | add eax,[ebx+ecx*2h] | addl (%ebx,%ecx,0x2),%eax | | lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax | | sub eax,[ebx+ecx*4h-20h] | subl -0x20(%ebx,%ecx,0x4),%eax | 将一个汇编代码中label的地址赋给register: | mov rax, OFFSET msg | mov $msg, rax | +------------------------------+------------------------------------+ 汇编器指令(GAS)

汇编程序中以.开头的名称不是指令,不会被翻译成机器码,它们是给汇编器一些特殊的指示,称为汇编器指令。

.data,定义data section,保存程序的全局和静态数据,可读可写。

.text,定义text section,保存代码,只读和可执行。

.section .rodata,定义只读数据区(必须带上.section前缀)。

定义section的一般格式如下:

.section section_name [,"flags" [,Type]]

.text,.data,.rodata这三个名称的section,都默认的flags和type,因此无需显示指定。查看object或executable文件的sections,请参考readelf命令,有对应的type和flags信息。

.globl指示告诉汇编器这个符号是个全局符号。也可以使用.global,这个更好!

.extern,指定外部符号。

.section .data data_items: .long 3,67,34,222,45,75,54,34,44,33,22,248,66,0 .quad,64位 .long,声明32位数据,一个或一组。 .word,16位 .byte,8位

上例中,数组开头有个symbol叫做data_items,汇编器会把数组的首地址作为data_items符号所代表的地址,data_items类似于C中的数组名。data_items这个符号没有.global声明,因为它只在这个汇编程序内部使用,链接器不需要知道这个名字的存在。

.type,用来定义符号的类型。

The ".type" directive is used in assembly language to declare the type of a symbol or label. It is typically used in conjunction with the symbol's name to provide additional information to the assembler or linker about the symbol's properties.

比如,申明一个全局函数接口:

.global funcname .type funcname, @function

定义一个全局变量:

.global my_variable .type my_variable, @object my_variable: .word 42

.ascii,例: .ascii "Hello World",声明了11个数,取值为相应字符的ASCII码,末尾是没有'\0'字符。

.data msg: .ascii "Hello world, hello AT&T asm!\n" len = . - msg

len符号代表一个常量,它的值由当前地址(.)减去符号msg所代表的地址得到,就是字符串的长度。用$len表示len的值,Intel语法就直接使用len。

.asciz,自动在末尾增加\0字符。

.asciz "JNZ" # 插入4个字节: 0x4A 0x4E 0x5A 0x00`

.balign指令(Intel)用来对齐地址,后面跟2的幂次,比如:

.balign 8 # 8字节对齐,Intel语法

对应的AT&T语法是:

.align 8 # 8字节对齐,AT&T语法

下面是三种定义常量的语法,这些常量在汇编代码中,就是简单替换:

.set AA, 100 .equ BB, 200 CC = 300

.size,说明一个symbol的size大小。貌似手搓的汇编用不上这条指令。

The ".size" directive is used in assembly language to specify the size of a symbol or label. It is typically used to provide information to the assembler or linker about the size of a data object, function, or other symbol.

funcname: ... ret .size funcname, .-funcname # 用减法计算size

用as编译intel语法代码

为什么我比较喜欢Intel格式的汇编,是因为Intel很强大吗?不仅如此,Intel格式语法看着更符合C代码,比如目的在左,源在右。

示例代码:

.intel_syntax noprefix .LC2: .string "%d %d %.2f %0.2f %c %d\n" .section .text .global main main: sub rsp, 8 mov ecx, 101 mov edx, 1 xor esi, esi mov r8d, 2 mov edi, OFFSET FLAT:.LC2 mov eax, 2 movsd xmm1, QWORD PTR .LC0[rip] movsd xmm0, QWORD PTR .LC1[rip] call printf mov eax, 7 add rsp, 8 ret .LC0: .long 0 .long 1074921472 .LC1: .long 1610612736 .long 1073899110

确保这段代码可以被as正常编译,就是.intel_syntax noprefix这行代码,它指示as采用intel语法进行汇编!而且,noprefix是必须的,如果没有它,就表示需要指定各种prefix,即%指定寄存器,$表达立即数。

编译:

$ gcc test.s -o test

可以先用as编译成.o文件,再用ld链接,但手动调用ld时,设置参数会比较麻烦。

$ as test.s -o test.o $ ld ...

就算是在gcc命令行使用-masm=intel,也需要申明.intel_syntax noprefix这行代码,否则你就需要写带上prefix的Intel语法的汇编(貌似dst和src的位置还是ATT风格的)!但是我在测试使用这个命令行参数编译inline asm的时候,确可以正常编译。嗨.....as没有-masm=intel这个参数!

as同时还有一个.att_syntax指令,切换回默认的ATT风格。

操作数(Operands)

汇编指令操作数是必须的,要么显式指定,要么隐式的被指令强行指定。

Most x86-32 instructions use operands, which designate the specific values that an instruction will act upon. Nearly all instructions require one or more source operands along with a single destination operand. Most instructions also require the programmer to explicitly specify the source and destination operands. There are, however, a number of instructions where the operands are either implicitly specified or forced by the instruction.

有三种操作数类型:

立即数,嵌入指令的数据,仅用于source operand 寄存器数,数据存放在某个通用寄存器中 内存数,数据存放在内存的某个地址中

指令支持源或目的operand为memory,但是源或目的不能同时为memory。

There are three basic types of operands: immediate, register, and memory. An immediate operand is a constant value that is encoded as part of the instruction. These are typically used to specify constant arithmetic, logical, or offset values. Only source operands can be used as immediate operands. Register operands are contained in a general-purpose register. A memory operand specifies a location in memory, which can contain any of the data types described earlier in this chapter. An instruction can specify either the source or destination operand as a memory operand, but not both.

Conditional Codes

很多汇编指令中,都包含conditional codes,比如jcc,cmovcc,setcc。

助记符 含义 A Above E Equal,当PE时,Even B Below L Less N 如果只有两个字母,表示Not,如果是3个字母,Neight Z Zero G Greater S Sign C Carry O Overflow,当PO时,Odd P Parity

A和B用于unsigned integer,G和L用于signed integer。

Condition-codes containing the words above and below are employed for unsigned-integer operands, while the words greater and less are used for signed-integer operands.

cmovcc指令的出现,是为了在某些时候干掉branch,加快CPU执行指令的速度。

Intel和AT&T语法中,不同名称的相同指令

绝大多数面向80386的AT&T汇编指令与Intel格式的汇编指令都是相同的,带符号扩展指令和补零扩展指令则是仅有的不同格式指令。

intel att(nosuffix) comments movsx movs signed extension movzx movz zero extension cbw cbtw Sign-extend AL into AX cwde cwtl Sign-extend AX into EAX cdqe cltq Sign-extend EAX into RAX cwd cwtd Sign-extend AX into DX:AX cdq cltd Sign-extend EAX into EDX:EAX cqo cqto Sign-extend RAX into RDX:RAX

本文链接:https://cs.pynote.net/hd/asm/202203071/

-- EOF --

-- MORE --

学习x86-64寄存器(x64 Register Set)x64汇编学习(5)-- Global & Static Variablesx64汇编学习(10)-- vtablelock指令(x64)x64汇编学习(8)-- class type & newx64汇编学习(4)-- For Loopx64汇编学习(2)-- volatilex64汇编学习(1)-- call printf(逐行解释)

CS笔记 Since 2021.9Powered by Python & Markdown



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有