组装,x86:如何将标签推入堆栈?

tal*_*alz 0 x86 assembly att addressing-mode

鉴于数据:

.section data
data_set:
.long 2,5,33,54,2,76,4,37,43,223,98,70,255
Run Code Online (Sandbox Code Playgroud)

如何将数据的起始地址(而不是该地址中的值)推送到堆栈?

我试过这个:

pushl data_set
Run Code Online (Sandbox Code Playgroud)

最终(在尝试访问此地址中的数据之后)导致了段错误。

Pet*_*des 5

在 AT&T 语法中,要将地址用作指令的立即操作数,请使用$label.

你想要pushl $data_setpush imm32指令。,就像你一样push $123

pushl data_set将推送从地址加载的双字数据data_set,即
push m32


从概念上讲,AT&T 语法将所有内容都视为潜在地址。所以label是一个地址,所以是0x123。因此,add 0x123, %eax从地址的负载0x123,并add label, %eax从该地址的负载label。当您想要一个数字作为直接操作数时,您可以使用add $0x123, %eax. 使用符号地址作为立即操作数的工作原理相同。

无论哪种方式,地址都被编码到指令中,这只是在寻址模式下它是立即数还是位移的问题。这就是为什么您使用
add $(foo - bar), %eax添加两个符号之间的距离,而不是
add $foo-$bar, %eax(这将查找名为 的符号$bar,即$符号名称的一部分)。将$适用于整个操作数,而不是符号/标签名称。相关:更多关于 GAS 中的组装时间数学

在其他上下文中,例如作为.longor的操作数.quad,没有立即与内存操作数的问题,因此您只需写入dataptr: .long data_set以发出 4 个字节的数据来保存 的地址data_set,就像您从 C 中得到的一样static int *dataptr = data_set;


您可以通过查看 C 编译器输出来检查语法

void ext(int*);
static int data[] = {1,2,3};
void foo() {
    ext(data);
}
Run Code Online (Sandbox Code Playgroud)

让 C 编译器发出将符号地址传递给函数的代码。我把它放在Godbolt 编译器资源管理器上,它确实使用了pushl $data.

  • 从技术上讲,`label` 始终是一个地址。`$` 只是选择一个带有立即数的指令编码。例如,如果你想要立即计算两个标签 `foo` 和 `bar` 的差异,你会做 `$foo-bar` 而不是 `$foo-$bar`(它也碰巧没有错误地组装,但引用了符号`foo`和`$bar`)。 (3认同)