一、什么是nasm汇编

nasm使用在windows、linux等系统下的汇编。

二、语法介绍

2.1 nasm 是区分大小写

例如:符号 foo 与 FOO 是两个不同的标识符。

2.2 内存操作数表达式

2.2.1 在 nasm 语法里,对 memory 操作数需要加 [ ] 括号

下面的代码:

  1. foo equ 1
  2. bar dw 2
  3. bits 32
  4. mov eax, foo
  5. mov ebx, bar

第 2 指令的意图是:将bar 内的值赋给ebx 寄存器。但这样是错误的,nasm 只会把 bar 当作是 immediate 赋给 ebx

  1. 00000000 0200 ; bar 变量
  2. 00000002 B801000000 mov eax,0x1
  3. 00000007 BB00000000 mov ebx,0x0 ; bar 地址作为 immediate 赋给 ebx

因此,需要将 bar 用 [ ] 括起来

  1. mov eax, foo
  2. mov ebx, [bar]

nasm 就编译出正确的代码:

  1. 00000000 0200 ; bar
  2. 00000002 B801000000 mov eax,0x1
  3. 00000007 8B1D00000000 mov ebx,[dword 0x0] ; bar 的内容赋给 ebx

2.2.2 给 memory 操作数提供一个 displacement 值

下面代码展示了 [base + disp] 的寻址方式:

  1. section .bss
  2. buffer resb 10
  3. section .text
  4. bits 32
  5. mov byte [buffer + 0x01] , 'a'
  6. mov ebx, buffer
  7. movzx eax, byte [ebx + 0x01]

2.2.3 指明 memory 操作数的 operand size

下面展示了为 memory 操作数提供一个 size 情况:

  1. mov byte [buffer + 0x01] , 'a'
  2. mov ebx, buffer
  3. movzx eax, byte [ebx + 0x01]

代码中使用 byte 关键字对 memory 操作数进行了修饰,指明 memory 操作数的大小为 byte

在这方面,nasm 的语法与微软的 masm 的语法(Intel 语法)有些不同,masm 的语法是:

  1. mov byte ptr [buffer + 0x01] , 'a'
  2. mov ebx, buffer
  3. movzx eax, byte ptr [ebx + 0x01]

在 masm 语法中需配合 ptr 指示字。

2.2.4 提供一个 segment

大多数 指令/内存操作数 缺省的 segment 是 DS ,x86/x64 允许为 memory 操作数提供另一个 segment 进行 segment override

在 nasm 语法中,如下:

  1. mov byte [es: buffer + 0x01] , 'a'
  2. mov ebx, buffer
  3. movzx eax, byte [es: ebx + 0x01]

nasm 语法中,在 [ ] 括号内提供 segment ,不能在 [ ] 括号外提供 segment

而 masm 的语法中是在 [ ] 括号外提供 segment,如下:

  1. mov byte ptr es: [buffer + 0x01] , 'a'
  2. mov ebx, buffer
  3. movzx eax, byte ptr es: [ebx + 0x01]

3.2.5 指明 memory 操作数的 address size

有些情况下必须指明 memory 操作数的 address size ,否则编译结果可能不是你想要的结果。下面的例子说明,如何为 memory 操作数指明 address size

例 1:

  1. section .bss
  2. buffer resb 10
  3. section .text
  4. bits 64
  5. mov rax, [qword buffer] ; 指明 64 位的 address size(绝对地址)
  6. mov rax, [buffer] ; 使用 32 位的 address size(绝对地址)

在 nasm 中,对于 绝对地址 形式,缺省是 32 位的,因此,需要明确使用 qword 来指明 64 位的 address size

这段代码编译后为:

  1. 00000000 48A11400000000000000 mov rax,[qword 0x14] ; 64 位地址
  2. 0000000A 488B042514000000 mov rax,[0x14] ; 32 位地址

它们的区别就是一个使用了 64 位地址,一个使用了 32 位地址。

例 2:


  1. section .bss
  2. buffer resb 10
  3. section .text
  4. bits 16
  5. mov byte [es:dword buffer + 0x01], 'a' ; 指明为 32 位地址
  6. mov ebx, buffer
  7. movzx eax, byte [es:ebx+0x01]

上面代码是 16 位代码,使用了 dword 指明 memory 操作数是 32 位的地址。

它被编译为:

  1. 00000000 2667C6051D000000 mov byte [dword es:0x1d],0x61
  2. -61
  3. 00000009 66BB1C000000 mov ebx,0x1c
  4. 0000000F 2667660FB6830100 movzx eax,byte [es:ebx+0x1]
  5. -0000

16 位的 address size 被 override 为 32 位地址。

2.3 nasm 伪指令

伪指令不是 x86/x64 机器的真实指令,伪指令是用于给编译器指示如何进行编译。

2.3.1 nasm 定义的 7 种数据 size

  • byte : 8 位
  • word : 16 位
  • dword : 32 位
  • qword : 64 位
  • tword : 80 位
  • oword : 128 位
  • yword : 256 位

oword 可以对应 Microsoft MASM 的 xmmword 类型,yword 对应 Microsoft MASM 的 ymmword 类型。

tword, oword 以及 yword 使用在 非整型 数据,使用在 float 和 SSE 型数据。

2.3.2 定义初始化数据:db 家族

nasm 定义了用于初始化上面 7 种 size 的 db 家族,它们用于定义初化常量值。

  • db : define byte
  • dw :define word
  • dd :define doubleword
  • dq :define quadword
  • dt :define tword
  • do :define oword
  • dy :define yword

正如前面所说的:dt , do , dy 不接受整型数值常量,它们被使用在定义 float 或 SSE 数据常量。dt 可以定义 extended-precision float 数据,do 可以定义 quad-precision float,dy 可定义 ymm 数据。而 dq 可以定义 double-precision float 数据,dd 可以定义 single-precision float 数据。

下面是 NASM Manual 上的例子:

  1. db 0x55 ; just the byte 0x55
  2. db 0x55,0x56,0x57 ; three bytes in succession
  3. db 'a',0x55 ; character constants are OK
  4. db 'hello',13,10,'$' ; so are string constants
  5. dw 0x1234 ; 0x34 0x12
  6. dw 'a' ; 0x61 0x00 (it's just a number)
  7. dw 'ab' ; 0x61 0x62 (character constant)
  8. dw 'abc' ; 0x61 0x62 0x63 0x00 (string)
  9. dd 0x12345678 ; 0x78 0x56 0x34 0x12
  10. dd 1.234567e20 ; floating-point constant
  11. dq 0x123456789abcdef0 ; eight byte constant
  12. dq 1.234567e20 ; double-precision float
  13. dt 1.234567e20 ; extended-precision float

2.3.3 定义非初始化数据:resb 家族

程序中使用到的非初始化数据通常放在 bss section 里,bss 代表 uninitialized storage space

nasm 使用了 resb (reserve byte) 家族来定义非初始化数据。

  • resb :reserve byte
  • resw :reserve word
  • resd :reserve doubword
  • resq :reserve quadword
  • rest :reserve tword
  • reso :reserve oword
  • resy :reserve yword

resb 相当于 Microsoft MASM 语法中的 db ?

下面是 NASM Manual 的例子:

  1. buffer: resb 64 ; reserve 64 bytes
  2. wordvar: resw 1 ; reserve a word
  3. realarray resq 10 ; array of ten reals
  4. ymmval: resy 1 ; one YMM register

3.3.4 包含 binary 文件

nasm 提供了一种包含 binary(二进制)文件的方法:使用 incbin 伪指令。incbin 伪指令包含的 binary 文件将直将写入输出文件中。此伪指令的作用是包含 graphics 以及 sound 这类数据文件。 

··· 

incbin “file.dat” ; include the whole file 

incbin “file.dat”,1024 ; skip the first 1024 bytes 

incbin “file.dat”,1024,512 ; skip the first 1024, and 

; actually include at most 512 

···

2.3.5 使用 equ 定义常量

equ 用来为标识符定义一个 整型 常量,它的作用类似 C 语言中的 #define

  1. a equ 0 ; OK
  2. b equ 'abcd' ; OK! b = 0x64636261
  3. c equ 'abcdefghi' ; warning! c = 0x6867666564636261
  4. d equ 1.2 ; error!
  5. section .data
  6. string db 'hello,word',0
  7. len equ $-string ; OK! len = 0x0b
  8. section .text
  9. textlen equ _end - entry ; OK! textlen = 0x05
  10. _entry:
  11. mov ecx, textlen
  12. _end:

例子中: b 定义为常量 ‘abcd’ 它将是字符串的 ASCII 码序列,‘abcdefghi’ 常量将会被截断,整型常量最长为 quadword(8 bytes),而 d 企图被定义为一个 float 常量,这产生会错误。len 和 textlen 被定义为编译期确定的数值。

2.3.6 使用 times 重复写数据或指令

times 是一个比较实用伪指令,用来重复定义数据或指令。

下面是一个经典的使用例子:

  1. times 510-($-$$) db 0
  2. dw 0xaa55

这段代码经常出现在 boot 磁盘 MBR 引导代码中,目的是除了最后 2 个字节和 code 代码外的区域全部写 0 值。

times 还可以使用在重复写某一条指令,如下:

  1. times 10 nop

这段代码结果是重复填入了 10 条 nop 指令:

  1. 00000000 90 nop
  2. 00000001 90 nop
  3. 00000002 90 nop
  4. 00000003 90 nop
  5. 00000004 90 nop
  6. 00000005 90 nop
  7. 00000006 90 nop
  8. 00000007 90 nop
  9. 00000008 90 nop
  10. 00000009 90 nop

2.4 常量值

nasm 下可以接受 4 种常量:整型常量 ,字符常量 ,字符串常量 以及浮点常量

2.4.1 整型常量

在 nasm 中,常用的整型进制有 4 种:

  • decimal :十进制数
  • hex :十六进制
  • binary :二进制数
  • octal :八进制数

每一种进制都有前缀 和后缀 表示法。当数值无前缀和后缀时,它是十进制数。因为缺省是十进制。

2.4.1.1 十进制数表示方法

看一看,下面的例子:

  1. mov ax,200 ; decimal
  2. mov ax,0200 ; still decimal
  3. mov ax,0200d ; explicitly decimal
  4. mov ax,0d200 ; also decimal

十进制的前缀是:0d , 后缀是:d

2.4.1.2 十六进制数表示方法

十六进制使用 0123456789ABCDEF 来表示 16 个数值。类似地,它的前缀是:0h 或 0x (c/c++ 风格)以及 $0 (pascal 风格),后缀是:h

  1. mov ax, 0c8h ; hex
  2. mov ax, 8h ; hex

上面例子中的 hex 数,表明:当以 h 后缀结尾时,如果含有字母 ,必须要以 0 开头。

  1. mov ax,$0c8 ; hex again: the 0 is required

上面是 pascal 风格的十六进制表示法,使用前缀 $0 (0 是必须的)

  1. mov ax,0xc8 ; hex yet again
  2. mov ax,0hc8 ; still hex

上面是使用 0h 前缀和 C/C++ 风格的 0x 表示十六进制数。

2.4.1.3 八进制数表示方法

八进制的前缀可以是:0o 或 0q 后缀可以是:o 或 q

  1. mov ax,310q ; octal
  2. mov ax,310o ; octal again
  3. mov ax,0o310 ; octal yet again
  4. mov ax,0q310 ; octal yet again

2.4.1.4 二进制数表示方法

类似地,二进制的前缀是:0b 后缀是:b

  1. mov ax,11001000b ; binary
  2. mov ax,1100_1000b ; same binary constant
  3. mov ax,0b1100_1000 ; same binary constant yet again

2.4.2 字符常量

在 nasm 中,可以使用 3 种引号来提供字符

  • ’ …’ (单引号)
  • ” …” (双引号)
  • ` …` (反引号)

如下示例,它们的结果是一样的:

  1. db 'abcd'
  2. db "abcd"
  3. db `abcd`

2.4.2.1 提供字符常量

下面看看如何提供字符量:

  1. mov eax, 'a' ; eax = 0x61
  2. mov eax, 'abcd' ; eax = 0x64636261
  3. mov eax, 'abcdefghi' ; eax = 0x64636261
  4. mov eax, `/x61/x62/x63/x64' ; eax = 0x64636261

第 3 条企图赋超过 4 bytes 的字符常量给 eax, 编译器会截断为 4bytes 再赋给 eax, 而第 4 条是另一种字符常量表示法,使用转义字符表示。

可见:字符常量是以 little-endian 排列

2.4.3 nasm 中的转义字符

在 nasm 中使用 c 风格的转义字符,在 / (反斜杠符)后面跟 转义码 :/ escape-code

转义码(escape-code)包括:字符转义码 , 八进制转义码 , 十六进制转义码

注意:nasm 中的转义符必须要用 (反引号)来引用

  1. db `/x61` ; right! 'a'
  2. db '/x61' ; wrong! '/' , 'x' , '6', '1'

第 1 个用反引号来包含转义符是正确的。而第 2 个用单引号来包含转义符,nasm 却视它为一般的字符串对待

2.4.3.1 字符转义码

下面是一些例子:


  1. /' single quote (')
  2. /" double quote (")
  3. /` backquote (`)
  4. // backslash (/)
  5. /? question mark (?)
  6. /a BEL (ASCII 7)
  7. /b BS (ASCII 8)
  8. /t TAB (ASCII 9)
  9. /n LF (ASCII 10)
  10. /v VT (ASCII 11)
  11. /f FF (ASCII 12)
  12. /r CR (ASCII 13)
  13. /e ESC (ASCII 27)

2.4.3.2 八进制转义码

/ 后面最多可以跟着 3 位数字,构成八进制转义码,如下所示:

  1. /377 Up to 3 octal digits - literal byte
  2. /004 ; EOT
  3. /006 ; ACK
  4. /025 ; NAK
  5. /0 ; NULL

2.4.3.3 十六进制转义码

十六进制转义码以 x 或 X 开头,如下所示:

  1. db `/x61` ; 'a'
  2. db `/x61/x62/x63/x64` ; 'abcd'

上面所示:十六进制转义码可以连串提供。

2.4.4 字符串常量

字符串是逐个逐个提供字符,看以下例子:

  1. db 'hello' ; string constant
  2. db 'h','e','l','l','o' ; equivalent character constants

提供字符串常量,是从左到右依次写入到内存。

这里要注意的是:字符常量 与字符串常量 的区别 ,看一看下面的例子

  1. buffer db 'hello' ; 字符串常量
  2. ... ...
  3. mov eax, 'hello' ; 字符常量

字符常量与字符串常量最大区别就是:字符常以 littlen-endian 存储,而字符串常量是从左到度

2.4.5 Unicode 字符串

nasm 定义了两个操作数符来定义 Unicode 字符串:

  • utf16
  • utf32

下面是 nasm 里的例子:

  1. %define u(x) __utf16__(x)
  2. %define w(x) __utf32__(x)
  3. dw u('C:/WINDOWS'), 0 ; Pathname in UTF-16
  4. dd w(`A + B = /u206a`), 0 ; String in UTF-32

2.4.6 浮点数常量

浮点数变量 可以使用 DB , DW , DD , DQ , DT 以及 DO ,浮点数常量 使用 float8 , float16 , float32 , float64 , float80m , float80e , float128l 以及 float128h 来定义。

下面是 nasm 提供的例子:

  1. db -0.2 ; "Quarter precision"
  2. dw -0.5 ; IEEE 754r/SSE5 half precision
  3. dd 1.2 ; an easy one
  4. dd 1.222_222_222 ; underscores are permitted
  5. dd 0x1p+2 ; 1.0x2^2 = 4.0
  6. dq 0x1p+32 ; 1.0x2^32 = 4 294 967 296.0
  7. dq 1.e10 ; 10 000 000 000.0
  8. dq 1.e+10 ; synonymous with 1.e10
  9. dq 1.e-10 ; 0.000 000 000 1
  10. dt 3.141592653589793238462 ; pi
  11. do 1.e+4000 ; IEEE 754r quad precision

2.5 表达式

在 nasm 里的表达式很像 C 表达式,对于熟悉 C 表达式的人来说几乎可以马上上手。

2.5.1 与$ 标号

标号表示nasm编译后当前指令位置标号表示nasm编译后当前指令位置$ 标号表示当前 section 起始位置

看看下面的例子:

  1. bits 64
  2. section .rdata
  3. dq 0
  4. section .text
  5. mov rax, 0
  6. mov rax, $-$$

它的编译结果是:

  1. 00000000 48B8000000000000 mov rax,0x0
  2. -0000
  3. 0000000A 48B80A0000000000 mov rax,0xa
  4. -0000
  5. 00000014 0000 add [rax],al
  6. 00000016 0000 add [rax],al
  7. 00000018 0000 add [rax],al
  8. 0000001A 0000 add [rax],al

2.5.2 位运算符

与 C 一样,包括下面:

位运算符 描述
& 位与: AND
| 位或:OR
~ 位非: NOT
^ 位异或: XOR
<< 左移
>> 右移

2.5.3 算术运算符

下面是 nasm 所支持的算术运算符

算术运算符 描述
+
-
*
/ 除(无符号数)
// 除(符号数)
% 取模(无符号数)
%% 取模(符号数)

资料来源: 
https://www.cnblogs.com/jiu0821/p/4422464.html

nasm汇编讲解的更多相关文章

  1. Ubuntu 16.04安装NASM汇编IDE-SASM

    在Linux下,尤其是Ubuntu,SASM工具应该是用来开发汇编最好用的IDE,小巧且支持调试.支持的编译器有:NASM, MASM, GAS, FASM. 安装步骤: 下载: http://dow ...

  2. ubuntu 上安装 NASM 汇编开发工具

    一般系统自带NASM可通过 输入 nasm -version 检查,若是没有 可用下述指令安装: sudo apt-get install nasm 安装过程执行完毕后 再次输入 : nasm -ve ...

  3. nasm汇编一些需要注意的地方

    经常用msam或tasm的童鞋一下转换到nasm下可能觉得不怎么适应,它们应该先去晓习一下gas的语法,然后就适应了-that's only a joke! :) section .data v101 ...

  4. Mac nasm 汇编入门

    下载 brew install nasm code SECTION .data msg: db "hello world", 0x0a len: equ $-msg SECTION ...

  5. 一个问题:关于类型转换Type Cast(汇编讲解 as 语法)

    问题如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34   ...

  6. ASM:《X86汇编语言-从实模式到保护模式》5-7章:汇编基础

    第5-7章感觉是这一本书中比较奇怪的章节,可能是作者考虑到读者人群水平的差异,故意由浅入深地讲如何在屏幕上显示字符和使用mov,jmp指令等等,但是这样讲的东西有点重复,而且看了第六,第七章以后,感觉 ...

  7. 推荐一篇讲arm架构gcc内联汇编的文章

    这是来自ethernut网站的一篇文章,原文链接: http://www.ethernut.de/en/documents/arm-inline-asm.html 另外,据说nut/os是个不错的开源 ...

  8. 用户手册是Yasm汇编

    本文档的用户手册是Yasm汇编. 它是介绍和通用所有Yasm用户参考. 英文的参考:http://www.cnblogs.com/coryxie/p/3959888.html 1 .介绍 Yasm b ...

  9. NASM在Ubuntu上的安装与简单使用

    一 .安装NASM 1. 下载安装文件 地址是:http://www.nasm.us/pub/nasm/releasebuilds/2.11.08/ 2.解压(具体命令要根据压缩包的类型来选用) 3. ...

随机推荐

  1. Luogu5285 [十二省联考2019] 骗分过样例

    题目分析: 观察前3个点,$361=19*19$,所以可以发现实际上就是快速幂,然后模数猜测是$998244353$,因为功能编号里面有这个数字,用费马小定理处理一下. $pts:12$ 观察第4个点 ...

  2. python 实现 websocket

    一.websocket概要: websocket是基于TCP传输层协议实现的一种标准协议(关于网络协议,可以看看文末的图片),用于在客户端和服务端双向传输数据 传统的客户端想要知道服务端处理进度有两个 ...

  3. intel ipp6.0安装过程

    由于最近看到一个代码中使用了intel ipp6.0库,了解到,ipp6.0是一个很强大的图像处理库,将其与opencv联合使用,还能够加速opencv的处理,在图像处理的过程中,是一个很重要的工具. ...

  4. UI5-技术篇-Hybrid App-1-Barcode扫描

    参考资料: https://www.w3cschool.cn/cordova/cordova_overview.html https://blogs.sap.com/2017/01/03/sapui5 ...

  5. cocos动画没有cc.Sprite.spriteFrame属性

    对于新人来说总是有那么多的坑等着你. 新建动画节点的时候千万别[新建空节点]!!! 上面这个就是新建了空的节点,导致没有cc.Sprite.spriteFrame属性. 正确姿势: 粗略试了一下除了空 ...

  6. 报错:ipython 6.5.0 has requirement prompt-toolkit<2.0.0,>=1.0.15, but you'll have prompt-toolkit 2.0.15 which is incompatible.

    pip install imagededup 时,报错:ipython 6.5.0 has requirement prompt-toolkit<2.0.0,>=1.0.15, but y ...

  7. k8s的日志

    日志   • K8S系统的组件日志 • K8SCluster里面部署的应用程序日志   方案一:Node上部署一个日志收集程序 • DaemonSet方式部署日志收集程序 • 对本节点/var/log ...

  8. evpp下载1个文件

    上传一个文件无错误 如何下载一个文件?? 集群?? http pdf 直接 redbuf

  9. SpringBoot项目的异常

    1.问题描述 创建springcloud 项目时候,main报错: Spring Boot Application in default package less… (Ctrl+F1)Inspecti ...

  10. 1204 中间件以及cookie,session

    目录 一 .cookie与session原理 1.cookie 操作 1.1 设置cookie set_cookie 1.2 获取cookie request.COOKIES.get('k1') 1. ...