PAYATHON 2026

NASM 中如何在函数结束时批量取消 %define 定义?

数据阿明

可以用 NASM 的 context stack 管理由一个宏定义的一批临时变量:在函数对应的源码区域开始处执行 %push,将变量定义为 %$var1 这样的上下文局部宏,再在源码区域结束处用一次 %pop 清理整个上下文。

这需要调整变量的定义和引用方式。普通的 %define var1 ... 不会在函数结束或执行 %pop 时自动消失。

为什么原来的 clear_vars 不生效?

NASM 在调用多行宏之前,会先展开调用行中的单行宏。例如:

%define var1 10
clear_vars var1

传给 clear_vars 的参数已经变成了 10,宏体里的 %undef %1 无法再取得原始名称 var1。即使增加 %rep 或调整 %rotate,也无法恢复原始名称。这个顺序可参见 NASM 预处理展开规则。

加引号可以保留字符串内容,但 %undef 需要宏标识符。直接传入 "var1" 不会自动将字符串转换为标识符。

用上下文局部宏统一清理

如果可以修改负责定义变量的宏,可以这样组织:

bits 64
section .text

%macro define_vars 0
    %define %$var1 10
    %define %$var2 20
    %define %$var3 30
%endmacro

%push function_vars

example_function:
    define_vars

    mov eax, %$var1
    add eax, %$var2
    add eax, %$var3
    ret

%pop

%push 创建上下文,define_vars 在当前上下文中定义三个单行宏,%pop 则销毁这个上下文及其局部定义。这样就不必维护清理名称列表或逐项编写 %undef。NASM context stack 文档说明了 %push、%pop 和 %$ 的用法。

使用时需要注意什么?

  • 定义和引用都要使用 %$var1。只在普通 %define var1 ... 外面加 %push、%pop,不会使它变成局部定义。
  • %push 和 %pop 必须配对。存在嵌套上下文时,%$var1 指向栈顶上下文,访问上一层需要使用 %$$var1。
  • 这是预处理阶段的源码范围管理。ret 不会触发清理,清理发生在预处理器读到 %pop 时。
  • 如果必须保留现有的全局宏名称,直接写出相应的 %undef 仍是明确可控的方案。不要用不带参数的 %clear 代替局部清理,它会清除其他宏定义,包括 NASM 的预定义宏。

备注:内容仅供参考。