NASM-中文手册摘录笔记

第二章 运行 NASM

NASM 在碰到错误以前是不输出任何信息的, 所以除了出错信息你看不到其他信息.

所有可用的输出文件格式的列表可以通过:

1
$ nasm -hf

得到.

-l 选项会生成一个源文件的列表文件, 在里面, 地址和产生的代码列在左边, 实际的源代码 (包括宏扩展, 除了那些指定不需要在列表中扩展的宏) 列在右边. 如:

1
$ nasm -f elf myfile.asm -l myfile.lst

-F 为输出文件选择一个调试格式. -g 使调试信息有效.

-I 包含文件搜索路径.

NASMENV 环境变量的值会作为命令行的附加选项部分, 其值通过空格符分隔. 若不想用空格符分隔, 可将其值以非 - 开头作为新的分隔符, 如:

1
NASMENV='!-dNAME="my name"'

2.2.3 NASM 不存储变量类型

因此在使用时显示表明类型:

1
mov word [var],2

第三章 NASM 语言

每一行 NASM 源代码包含 (除非是一个宏, 一个预处理操作符, 或一个汇编器操作符) 下面四个部分的全部或某几个部分:

1
label:  instruction operands    ; comment

NASM 使用反斜线 \ 作为 续行符 .

labels 中的有效字符是:

  • 字母
  • 数字
  • ‘-‘, ‘$’, ‘#’, ‘@’, ‘~’, ‘.’, ‘?’

只有 ‘.’, ‘_‘, ‘?’ 可以作为标识符开头.

一个标识符还可以加上一个 ‘$’ 前缀, 以表明它作为一个标识符而不是保留字来处理.

3.2 伪指令

伪指令是一些并不是真正的 x86 机器指令, 但还是被用在了 instruction 域中的指令.

db, dd, dw 的区别是对齐规则不同.

RESB 等用来声明未初始化的存储空间:

1
buffer:     resb   64       ; reserve 64 bytes

EQU 的行为就是把给出的 label 的名字定义成它的操作数 (唯一) 的值, 定义是不可更改的:

1
msglen      equ     10

3.2.5 TIMES: 重复指令或数据

前缀 TIMES 导致指令被汇编多次.

3.3 有效地址

以下也有效:

1
2
mov eax,[ebx*2+ecx+offset]
mov ax,[bp+di+8]

3.4 常数

NASM 能理解四种不同类型的常数:

  • 数值
  • 字符
  • 字符串
  • 浮点数

3.5 表达式

jmp $ 可以简单表示无限循环.

% 符号被宏预处理器使用.

3.6 SEGWRT

SRG 操作符返回符号所在的首选段的段基址:

1
2
3
mov ax,seg symbol
mov es,ax
mov bx,symbol

3.9 本地 Labels

NASM 对于那些以一个句点开始的符号会作特殊处理, 一个以单个句点开始的 Label 会被处理成本地 label, 这意味着它会跟前面一个非本地 label 相关联. 感觉像是子 label:

1
2
3
4
5
6
7
8
9
10
11
12
label1  ; some code

.loop ;
; some code
jne .loop
ret

label2 ; some code

.loop ;
jne code
ret

第一个 .loop 实际上被定义为 label1.loop.

第二个 .loop 实际上被定义为 label2.loop

NASM 还能定义其他的特殊符号, 比如以两个句点开始的符号, 如 ..start 被用来指定 .obj 输出文件的执行入口.

第四章 NASM 预处理

预处理指令都是以一个 % 开头.

预处理器把所有反斜杠 \ 结尾的连续行合并为一行:

1
2
%define THIS_VERY_LONG_MACRO_NAME_IS_DEFINED_TO \
THIS_value

会像是单独一行那样正常工作.

4.1 单行的宏

4.1.1 最常用的方式: %define

和 C 类似:

1
2
%define ctrl 0x1F &
%define param(a,b) ((a)+(a)*(b))

%define 定义的宏是大小写敏感的.

使用 %idefine ( i 代表 “insensitive” )则是大小写不敏感.

可以 重载 单行宏, 预处理器能够通过参数的个数来区分:

1
2
%define foo(x) 1+x
%define foo(x,y) 1+x*y

foo(1) 会变成 1+1.

foo(1,1) 会变成 1+1*1

但是, 一个不带参数的宏定义不允许对它进行带有参数 的重定义.

命令行使用 -d 来预定义宏.

4.1.2 %define 的增强版: %xdefine

其大小写不敏感形式为 %xidefine

用于当一个宏之中有其他宏定义时.

让这个宏始终为内部宏嵌入时的值,而不是内部宏修改后的值.

4.1.3 连接单行宏的符号: %+

4.1.4 取消宏定义: %undef

命令行为 -u

4.1.5 预处理器变量: %assign

大小写不敏感形式为 %iassign.

值可以用表达式的形式指定:

1
%assign i i+1

assign 是赋值的含义, 这里也就好像赋值, i 展开的值是表达式计算的结果.

4.2 字符串处理宏 %strlen%substr

4.2.1 %strlen

%strlen 宏创建的数值是一个字符串的长度:

1
%strlen charcnt 'my string'

4.2.2 %substr

索引值不是 0 开始:

1
2
3
%substr mychar 'xyz' 1 ; x
%substr mychar 'xyz' 2 ; y
%substr mychar 'xyz' 3 ; z

4.3 多行宏: %macro

如:

1
2
3
4
5
%macro prologue 1
push ebp
mov ebp
sub esp,%1
%endmacro

调用:

1
myfunc: prologue 12

相当于:

1
2
3
myfunc: push    ebp
mov ebp
sub esp,%

宏名后面的数字定义了宏可以接收的参数的个数. %1 是调用第一个参数.

4.3.1 多行宏的重载

4.3.2 Macro-Local Labels

NASM 允许你在多行宏中定义 labels, 使它们对于每一个宏调用来讲是本地的.

通过在 label 名称前面加上 %% 来实现:

1
2
3
4
5
6
%macro retz 0
jnz %%skip
ret
%%skip:

%endmacro

4.3.3 不确定的宏参数个数

在 NASM 中是通过在宏的 %macro 一行上的参数个数后面加上 + 来实现.

4.3.5 %0: 宏参数个数计数器

%0 其表示有多少个参数传给了宏.

4.3.6 %rotate: 循环移动宏参数

%rotate 让宏参数循环左移. 其可以加一个参数指定左移的次数. 负数右移.

4.4 条件汇编

1
2
3
4
5
6
7
%if <condition1>
;
%elif <condition2>
;
%else
;
%endif

4.4.1 %ifdef 测试单行宏是否存在

1
2
3
%ifdef
;
%endif

4.4.2 %ifmacro 测试多行宏是否存在

4.4.7 %error 报告用户自定义错误

预处理操作符 %error 会让 NASM 报告一个在汇编时产生的错误.

1
2
3
4
5
6
7
%ifdef SOME_MACRO
;
%elifdef SOME_OTHER_MACRO
;
%else
%error Neither SOME_MACRO nor SOME_OTHER_MACRO was defined
%endif

4.5 预处理器循环: %rep

操作符 %rep%endref 可以用来包围一段代码, 然后这段代码可以被复制多次, 次数由预处理器指定.

1
2
3
4
5
%assign i 0
%rep 64
inc word [table+2*i]
%assign i i+1
%endrep

中途可使用 %exitrep 操作符来终止循环.

4.6 包含其他文件

通过 %include 实现:

1
%include "macros.mac"

可用 -I 参数增加搜索路径.

防止重复包含:

1
2
3
4
%ifndef MACROS_MAC
%define MACROS_MAC
;
%endif

4.7 上下文栈

4.8 标准宏

NASM 定义了一套标准宏.

4.8.5 STRUCENDSTRUC 声明一个结构体数据类型

4.8.6 ISTRUC, ATIEND 声明结构体的一个实例

第五章 汇编器指令


NASM-中文手册摘录笔记
http://example.com/2022/12/17/NASM-中文手册摘录笔记/
作者
Jie
发布于
2022年12月17日
许可协议