【问题标题】:Should we store long strings on stack in assembly?我们应该在汇编中将长字符串存储在堆栈中吗?
【发布时间】:2021-03-10 18:17:14
【问题描述】:

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

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

【问题讨论】:

  • bss 部分通常用于在运行时进行零初始化。初始化数据可以进入数据部分或文本部分。或者也可能有只读数据的特定部分。您的问题中的小错误:使用单个 dword 写入一次只能初始化 4 个字节,如果您想要包含零终止符字节,这是一个最多 3 个字节的字符串。

标签: assembly x86 nasm


【解决方案1】:

mov dword [rsp] 'foo' 组装成C70424666F6F00,编码 4 个有效载荷字符需要 7 个字节。
与标准的静态方式DB 'foo',0相比,在代码段中将字符串定义为立即操作数增加了75%的代码量。

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

【讨论】:

  • mov rcx, imm64 + push rcx 是 10+1 = 11 个字节,8 个字节的有效负载 = 73% 的效率,而 4/7 = 57%。 (从 RSP 偏移会更糟,但是为了节省代码大小,您可以使用 RBP+imm8 而不是 RSP+imm8,但这仍然是每 7 个字节 4 个字节。您可以将 push imm32mov dword [rsp+4], imm32 混合使用,但这也是不好。)
  • 编译器有时会使用立即数来初始化一个必须在堆栈上的本地结构或数组(即他们必须将一个非常量指针传递给另一个函数);他们切换到从 .rodata 复制(使用 movdqa xmm 加载/存储)的阈值高于 8 个字节。但是当你只想打印字符串时,你只需要传递一个指向 .rodata 的指针,根本不需要将它复制到堆栈中,因此使用堆栈空间的阈值要低得多。可能是 8 个字节,可能是 16 个字节,可能只有 4 个字节,具体取决于程序中的 I-cache 与 D-cache 压力。
  • 当然,对于 4 个字节,您可以使用 push 'foo' = 5 个字节 = 80% 的效率。使用mov rdi, rsp 将指针放入寄存器比lea rdi, [rel msg_foo] 小4 个字节,因此总体上是总大小的胜利(除非函数对齐的填充将其向上或隐藏它)。但无论如何,与 最佳 选项而不是最差选项进行比较可能是您回答的好主意。
【解决方案2】:

但是对于较长的字符串,必须使用多个指令拆分和存储字符串。所以这会增加可执行文件的大小(我是这么认为的)。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-10
    • 2012-04-17
    • 2017-11-16
    • 1970-01-01
    • 2022-11-06
    • 2016-01-12
    • 2020-04-07
    • 2022-01-02
    相关资源
    最近更新 更多