汇编语言学习笔记(十一)-预编译宏

在c语言中,宏作为一种预编译手段,可以在编译之前进行替换,汇编中的宏只是语法与c有所差别,在语言中所起到的作用非常相近。

先来看MASM中宏的语法规范:

macroname macro parameter1, parameter2, ...
	statement-list
endm

; 调用
macroname parameter1, parameter2, ...

看上去跟结构体的声明差不多,只是endm时不需要宏名,这里的参数是在调用点传递,预处理器只做替换,与c的宏有相同的含义,不过这里调用点的参数个数可以不与宏定义的参数个数相同,如果调用点个数多余定义,汇编器会产生一个警告,如果少于,则未传递的参数为空。

接下来举一个简单的例子进行说明:

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		kernel32.inc
includelib	kernel32.lib
includelib	msvcrt.lib

.data
outstring	db "result is %d", 0ah, 0dh, 0
result dd 0

.code
printf proto c s:dword, i:dword

addTwo macro a, b, result
	mov eax, a
	add eax, b
	mov result, eax
endm

start:
	addTwo 12, 14, result
	invoke printf, addr outstring, result
	addTwo result, 14, result
	invoke printf, addr outstring, result
    invoke ExitProcess, NULL
end start

通过反汇编此程序,可以看到宏被扩展了两份,与函数调用不同。

masm中宏的使用还有其他一些特性,对于参数,有时必须要调用者指定,可以在参数后加:req,由此后缀的参数必须传递。比如上例中的a,可以写做 a:req。 另外如果需要宏在汇编时输出信息,可使用echo伪指令,比如在addTwo宏内任意一行添加echo test data,则在编译时会输出两句test data.

接下来介绍MASM的条件汇编伪指令,类似于c语言的#if-#else-#endif条件编译宏。

先来看条件汇编伪指令的一般格式:

if condition
	statements
[else
	statements]
endif

可以看到,其用法与c基本相同,唯一需要说明一下的是condition的使用,因为次伪指令是在编译期间决定是否需要编译,所以条件表达式必须为常量,能够在编译期间识别,另外可使用一些关系运算符进行比较:

LT 	小于
GT 	大于
EQ 	等于
NE 	不等于
LE 	小于等于
GE 	大于等于

if宏还有几种变型,现列举如下:

伪指令 说明
ifb < param > 如果参数为空则允许汇编,参数名必须用<>括起
ifnb < param > 如果参数不为空则允许汇编,参数名必须用<>括起
ifidn < param1 >,< param2 > 如果两个参数相同则允许汇编,区分大小写
ifidni < param1 >,< param2 > 如果两个参数相同则允许汇编,不分区大小写
ifdif < param1 >,< param2 > 如果两个参数不同则允许汇编,区分大小写
ifdifi < param1 >,< param2 > 如果两个参数不同则允许汇编,不分区大小写
ifdef name 如果名字定义则允许汇编
ifndef name 如果名字没有定义则允许汇编

MASM的重复定义块: MASM中有些用于生成重复定义块的循环伪指令,while, repeat, for 和 forc,这些伪指令可在汇编期间生成代码,一般定义格式如下:

while constExpression
	statements
endm

repeat constExpression
	statements
endm

; 第一次循环时,参数取值为arg1
; 第二次循环时,参数取值为arg2
; 如此反复值最后一个取值
for parameter, 
	statements
endm

; 与for类似,第一次循环parameter取string的第一个字母
; 依次取遍所有字符,注意<>这两个字符前需加!标识
forc parameter, 
	statements
endm

这里的语法还是比较清楚,先略去实例的说明。

以上是MASM语法中的宏,在AT&T语法中,也有类似机制,现对几个常用的宏进行说明:

.macro
与MASM的macro类似,只是前面需要加点,语法规则如下:

.macro macro_name param[=value], ...
	statements
.endm

这里param可以赋默认值,宏中使用参数时需要使用\,\param引用参数param。举一个与MASM宏相同的例子:

.section .data
	result: .int 0
	outstring: .asciz "result is %d\n"

.section .text

.macro addTwo a, b, result
	mov \a, %eax
	add \b, %eax
	mov %eax, \result
.endm

.globl _main
_main:
	addTwo $12, $13, result
	pushl result
	pushl $outstring
	call _printf

	addTwo $123, $456, result
	pushl result
	pushl $outstring
	call _printf

	pushl $0
	call _exit

另外还有.if宏,其用法与MASM和c相同,这里仅仅做一个简单的列举:

说明
.ifdef symbol symbol 定义则编译
.ifeq expression 如果表达式为0则编译
.ifndef symbol 如果符号未定义则编译
.endif 结尾
.else 同MASM

当然,选择编译宏的种类十分丰富,这里只是列举常用宏,全部信息可由as使用手册获得。

最后还需要说明一下AT&T的.fill宏,这个宏在编译期间填充字符,可以用来控制可执行文件的大小,语法如下:

.fill repeat, size, value

这里暂且不举例,如果有机会说明一下系统引导程序编程,再说明这个指令吧。

Built with Hugo
主题 StackJimmy 设计