【问题标题】:x86 add and addl operands are adding wrong?x86 add 和 addl 操作数添加错误?
【发布时间】:2016-03-10 17:24:26
【问题描述】:

我使用的是 xv6,它在 x86 机器上实现了原始 UNIX。我在 C 程序中编写了非常简单的内联汇编:

register int ecx asm ("%ecx");
printf(1, "%d\n", ecx);
__asm__("movl 16(%esp), %ecx\t\n");
printf(1, "%d\n", ecx);
__asm__("add $0, %ecx\t\n");
printf(1, "%d\n", ecx);
__asm__("movl %ecx, 16(%esp)\t\n");

我通常会得到类似 434 的第二个打印语句打印的值。但是,在添加命令之后它会打印 2。如果我改用 addl 命令,它也会打印 2。我使用的是 xv6 的最新稳定版本。所以,我真的不怀疑这是问题所在。有没有其他方法可以在内联汇编中添加两个数字?

基本上我需要将 16(%esp) 增加 4。


将代码编辑为:

__asm__("addl $8, 16(%esp)\t\n");

【问题讨论】:

  • @MichaelWalz 具体表示printf 确实修改esp。但它很可能会修改ecx
  • 请注意,at&t 中没有add,只是汇编器有时可以从操作数中推断出大小。如果你想将 16(%esp) 增加 4,然后执行 addl $4, 16(%esp) 并完成它。
  • 你怎么知道它不起作用?从外观上看,16(%esp) 是返回地址,覆盖它是一个非常糟糕的主意,如果你遇到可能意味着它正在工作的崩溃。这闻起来像 XY 问题,你想做什么?请注意,处理程序的返回地址不是源,以防您想返回到其他地方,这不是这样做的方法。
  • @Jester 基本上我正在实现信号。在信号处理程序中,我正在计算 500 个信号所花费的时间。因此,当信号发生在 main 中时,我会增加信号的数量。如果该数字达到 500,那么我会增加堆栈中的返回地址,这样我就不会继续执行异常创建代码(除以 0)。然后,它从 main 中退出。
  • 我不知道它在xv6 上是如何工作的,但在普通的 unix 系统中,处理程序的返回地址不是异常的来源。要返回别处,您不会弄乱返回地址,而是在上下文中更改实际的重新启动地址。

标签: c assembly x86 gdb xv6


【解决方案1】:

1) 在您的示例中,您不是将 ecx 增加 4,而是将其增加 0。

__asm__("addl $4, %ecx");

2) 您应该能够将多个命令链接到一个 asm 调用中

__asm__("movl 16(%esp), %ecx\n\t"
        "addl $4, %ecx\n\t"
        "movl %ecx, 16(%esp)");

3) register 关键字是一个hint,编译器可能会决定将你的变量放在它想要的任何位置。阅读 GCC 页面上的文档也会警告某些函数如何破坏各种寄存器。作为 C 函数的 printf() 可以很好地使用 ecx 寄存器而不保留其值。它可以保存它,但它可能不会;编译器可能会将该寄存器用于该调用中的各种优化。它是80x86上的通用寄存器,经常用于各种参数传递和返回值。

未经测试的更正:

int reg; // By leaving this out, we give GCC the ability to pick the best available register.

/*
 * volatile indicates to GCC that this inline assembly might do odd side
 * effects and should disable any optimizations around it.
 */
asm volatile ("movl 16(%esp), %0\n\t"
              "addl $4, %0\n\t"
              "movl %0, 16(%esp)" 
              : "r" (reg)); // The "r" indicates we want to use a register

printf("Result: %d\n", reg);

GCC manage page 有更多详细信息。

【讨论】:

  • asm ("%ecx"); 不是提示,gcc 保证使用 ecx if 您在约束中引用该操作数。见Local Register Variables。要添加 4,您只需使用 addl $4, 16(%esp) 即可,无需额外的注册和移动。
  • 同一页面强烈反对使用 register 关键字。即使在使用扩展 ASM 调用的上下文中,它仍然说优化器可以破坏它或者其他东西可以使用它。因此,将该变量声明为 ecx 并不意味着编译器没有使用 ecx else were in the function。 (例如,对 printf() 的调用进行参数传递优化)
  • Sys-V 调用约定说 ECX 是一个暂存寄存器。在 OP 代码中的函数调用之间不做任何努力来保存它。
  • @AnttiHaapala 确实,这就是我关于printf() 和 ecx 寄存器的全部观点。不知道该调用对寄存器做了什么。正如您所指出的,没有受让人愿意努力保护它。因此,在内联 asm 之间交叉调用 printf() 可能是导致此错误的原因。
  • @Kelmar 您的代码无法编译。但是,我根据 Jester 的建议编辑了代码。我也删除了 printfs,但仍然无法正常工作。
猜你喜欢
  • 2012-04-25
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-20
  • 2011-05-13
  • 2018-09-20
相关资源
最近更新 更多