【问题标题】:Swapping two int pointers in assembly x86在汇编 x86 中交换两个 int 指针
【发布时间】:2017-04-22 18:24:29
【问题描述】:

我想交换两个 int 指针。

我可以像这样在 C 中做到这一点

void swap(int* a, int* b)
    int temp = *a;
    *a = *b;
    *b=temp;

现在我正在尝试在组装中进行,但请耐心等待,我不明白为什么这不起作用

push %ebp
mov %esp,%ebp   

mov 8(%ebp), %ecx       #a 
mov 12(%ebp), %ebx      #b

mov (%ecx), %eax        #move o a para temp
mov (%ebx), %ecx        #move o b para o a
mov (%eax), %ebx        #move o temp para o b

mov %ebp, %esp
pop %ebp
ret

谁能解释我做错了什么?

【问题讨论】:

  • 因为您需要编写 mov (%ebx), (%ecx) 来执行 *b = *a;,而 x86 不允许两个操作数都是间接的(movs 除外)。
  • C 代码不会“交换两个 int 指针”。它正在交换它们指向的数据。
  • 我很惊讶还没有人提到xchg
  • @DanielKamilKozar xchg 对此没有多大帮助,而且它比加载和存储要慢得多。
  • @DanielKamilKozar :您不想在这里将xchg 与内存操作数一起使用,因为这会将 LOCK 应用于指令。为避免这种情况,您必须将内存操作数移动到寄存器中并 xchg 寄存器,但是当您这样做时,您还不如使用 mov

标签: assembly x86 gnu-assembler att cdecl


【解决方案1】:

您的 C 代码正在尝试交换指针指向的值,但您的汇编代码似乎将值视为指向 segmentation faults 的指针。处理这个问题的一种方法是使用额外的寄存器来保存指针和它们指向的值。

32-bit CDECL 允许我们使用 EAXECXEDX,而无需担心保存它们的值。我们需要第四个寄存器,我们必须自己保存它。

我还假设您希望存在帧指针(可以在没有它的情况下编写例程):

push   %ebp
mov    %esp,%ebp
push   %ebx              # Preserve non-volatile EBX register
mov    8(%ebp),%eax      # EAX = Pointer to a (arg1)
mov    12(%ebp),%edx     # EDX = Pointer to b (arg2)
mov    (%eax),%ecx       # Temporarily save value at memory address a (*a) to ECX
mov    (%edx),%ebx       # Temporarily save value at memory address b (*b) to EBX
mov    %ebx,(%eax)       # Move value of b (EBX) to memory address of a (*a)
mov    %ecx,(%edx)       # Move value of a (ECX) to memory address of b (*b)
pop    %ebx              # Restore non-volatile EBX register
pop    %ebp
ret

理论上,您可以完全删除堆栈帧(可能会使调试器中的堆栈跟踪更难跟踪)。代码可以使用 ESP 而不是 EBP 来访问参数:

push   %ebx              # Preserve non-volatile EBX register
mov    8(%esp),%eax      # EAX = Pointer to a
mov    12(%esp),%edx     # EDX = Pointer to b
mov    (%eax),%ecx       # Temporarily save value at memory address a (*a) to ECX
mov    (%edx),%ebx       # Temporarily save value at memory address b (*b) to EBX
mov    %ebx,(%eax)       # Move value of b (EBX) to memory address of a (*a)
mov    %ecx,(%edx)       # Move value of a (ECX) to memory address of b (*b)
pop    %ebx              # Restore non-volatile EBX register
ret

注意:未能保留(按 32 位 CDECL 调用约定)非易失性寄存器,如 EBXEBP、ESIEDI 是一个错误。在简单的测试代码中它似乎可以工作,但在经过优化的更复杂的代码中,如果不严格遵守调用约定,您可能会遇到未定义的行为。

【讨论】:

  • 在功能上推送/弹出寄存器与使用临时堆栈有何不同?
  • @EOF:实际上,该注释适用于我没有保留 EBX 的代码的第一个化身。感谢您的关注。该评论不适用于我发布的代码,因此我已将其删除。
  • *Sigh*,在这里我希望能够脱离负载存储转发延迟和 OoO 执行的影响,并且不需要立即弹出值,可能是为了更好的带有 redzones 的 ABI...但是无论如何,省略帧指针可能会更好。
  • Redzone 不适用于 32 位 Linux 代码(我没有为这个答案烦恼),但它适用于 64 位代码。是的,在这种情况下,您可以简单地使用 RSP 下方的内存地址,而无需调整 RSP。 64 位 Linux 代码更容易,因为指针已经在寄存器中(RDI_and _RSI),因此您只需要几个额外的易失性寄存器来保存这些值。根本不需要临时堆栈空间。
  • 谢谢!很好的解释
【解决方案2】:

正如 Weather Vane 所说,您显示的 C 代码没有交换两个 int 指针。它正在交换由两个 int 指针指向的两个 int。

但在您的程序集中,您似乎试图交换由两个 int 指针指向的两个 int,因此您的代码并不是完全失败的原因。

研究这个以了解它的作用,然后尝试一下:

mov (%ecx), %eax
mov (%ebx), %edx
mov %edx, (%ecx)
mov %eax, (%ebx)

【讨论】:

  • 你可能想再看看你的答案。
  • @EOF 哎呀! C-:=
  • @EOF 该死的这种无意义的语法。
  • 别告诉我你更喜欢大喊大叫的MOV DWORD PTR ...
  • @EOF 不,我更喜欢mov eax, [ecx],因为我分配给eax,所以汇编程序会弄清楚我的意思是dword ptr
猜你喜欢
  • 1970-01-01
  • 2015-07-01
  • 2012-10-02
  • 2012-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
相关资源
最近更新 更多