我们应该在汇编中将长字符串存储在堆栈上吗?

Should we store long strings on stack in assembly?

在 NASM 中存储字符串的一般方法是像 msg: db 'hello world', 0xA 一样使用 db。我认为这会将字符串存储在 bss 部分。所以字符串会占据整个程序的存储空间。相反,如果我们将它存储在堆栈中,它只会在本地帧期间处于活动状态。对于小字符串(小于 8 个字节),可以使用 mov dword [rsp] 'foo' 来完成。但是对于较长的字符串,必须使用多条指令拆分和存储字符串。所以这会增加可执行文件的大小(我是这么认为的)。

那么现在,在有多个字符串的大型程序中哪个更好?我上面所做的任何假设都是错误的吗?

mov dword [rsp] 'foo'汇编为C70424666F6F00,编码4个payload字符需要7个字节
与标准静态方式相比DB 'foo',0在代码段中将字符串定义为直接操作数增加了75%的代码大小。

只有当您可以完全消除 .rodata.data 部分时,您的动态方法才可能有利可图(这种情况很少见)大型程序)。由于其文件对齐(在 PE 格式中为 512 字节),每个附加部分在可执行程序中比其 netto 内容占用更多 space。 即使您的程序在数据部分除了长字符串之外没有其他静态数据,您也可以在 .text (代码)部分使用静态声明节省更多 space。

But for longer strings string has to be split and be stored using multiple instructions. So this would increase the executable size (I thought so).

是的,在几乎所有情况下,这些指令使用的字节数将超过将字符串正常存储在内存中所需的字节数。 (该指令包括立即数的所有字节,除了零和符号扩展等少数例外,以及用于编码操作码、目标地址等的额外字节)。当然,代码也会在整个程序运行期间占用(虚拟)内存。天下没有免费的午餐。

因此,您应该直接 assemble 使用 db 将字符串直接存入内存,就像您所做的那样。但是请尝试将它们安排在只读部分中,例如 .text.rodata,具体取决于您的系统支持的内容。如果存在内存压力,现代操作系统将能够从物理内存中丢弃这些页面,知道如果需要再次可以从可执行文件中重新加载它们。这对您的程序是完全透明的。如果同时需要多个字符串,您可以通过尝试将它们安排在程序内存中相邻来进行一些优化(将它们一起定义在一个 asm 文件中就足够了)。

您还可以设计一些东西,在运行时将字符串从外部文件读取到动态分配的内存中,然后在完成后释放它。这是物理内存有限且不支持虚拟内存(想想 MS-DOS)的古代操作系统上程序的常用技术。