【问题标题】:how to access and change char array in c x86 AT&T inline assembly如何在 c x86 AT&T 内联汇编中访问和更改 char 数组
【发布时间】:2018-10-29 19:23:36
【问题描述】:

我在解决学校练习时遇到问题,我应该使用内联汇编更改 c 中的 char 数组。在这种情况下,将“ahoy”更改为“aXoy”,但我遇到了分段错误。这是我的代码:

 #include <stdio.h>

    int main() {
        char string[] = "ahoy";
        __asm__ volatile (
                "mov %0, %%eax;"
                "movb $'X', 1(%%eax);"
                : "=m"(string) : "0"(string) : "memory", "eax");
        printf("%s\n", string);
        return 0
    }

用这个:"mov %0, %%eax;" 我正在尝试将数组的地址存储在寄存器中

然后用这个:"movb $'X', 1(%%eax);" 我想将字节“X”存储在 (%%eax) 偏移 1 个字节(char) 所指向的位置,

我有字符串作为输出和输入,以及 clobber 中的“memory”、“eax”,因为我正在修改两者。我的代码有什么问题?

【问题讨论】:

  • 如果您想使用string 作为输入和输出,请将其标记为"+m" 而不是"=m",然后完全删除"0"(string)。这行mov %0, %%eax; 会将内存地址处的 32 位值移动到 EAX,因为 %0 是内存操作数。你想把 %0 的地址放在它出现的 EAX 中。使用lea %0, %%eax; 而不是mov %0, %%eax;
  • 其次:你是编译成 64 位还是 32 位代码?内联汇编使用 32 位寄存器和基于堆栈的地址 (string) 将出错,因为堆栈地址无法以 32 位值正确表示。确保使用 -m32 编译它或修改代码以使用 64 位寄存器作为地址(RAX 而不是 EAX)
  • __asm__ volatile ( "lea %0, %%eax;" "movb $'X', 1(%%eax);" : "+m"(string) :: "memory", "eax");
  • + 修饰符几乎永远存在。它们都记录在这里:gcc.gnu.org/onlinedocs/gcc/Modifiers.html#Modifiers
  • 从技术上讲,在这种情况下使用+m,您不需要使用memory clobber,因为编译器将首先在内存中实现所有string,因为您使用的是@987654339 @ 约束。或者,您可以完成 __asm__ volatile ( "movb $'X', 1(%0);" :: "r"(string) : "memory"); 。需要内存破坏器,因为在这种情况下,您通过寄存器而不是内存操作数传递 string 的地址。

标签: c assembly x86 inline-assembly cpu-registers


【解决方案1】:

使用gcc -S 而不是-c 来查看编译器的汇编输出,您应该会看到问题所在。 "m" 约束生成用于访问与其关联的对象的内存引用表达式,而不是包含其地址的寄存器。所以它会扩展成(%ecx),而不是%ecx,第一个mov会从string加载4个字节,而不是string的地址,到eax

解决此问题的一种方法是改用寄存器约束"r"(string)

或者,您可以将mov 替换为lealea %0, %%eax

您的代码也存在其他问题,例如无用的临时/clobber 寄存器 eax,但它们不应使其无法正常工作。

【讨论】:

  • 我真的很难理解这个......所以你说我唯一需要改变的是:“=m”(string)到:“=r”(string) ?因为在那种情况下,编译器告诉我“asm”中有一个“不可能的约束”
  • 抱歉,如果您使用"r",它只需要一个输入,并且您需要一个内存破坏器(您已经拥有)。那是因为表达式string 不是可修改的左值;它现在被用于衰减为指针的上下文中。
猜你喜欢
  • 1970-01-01
  • 2017-10-30
  • 1970-01-01
  • 1970-01-01
  • 2018-03-22
  • 2023-03-10
  • 1970-01-01
  • 2012-10-28
  • 2015-09-01
相关资源
最近更新 更多