AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 77623129
Accepted
TryingToPassCompSci
TryingToPassCompSci
Asked: 2023-12-08 05:20:25 +0800 CST2023-12-08 05:20:25 +0800 CST 2023-12-08 05:20:25 +0800 CST

C 内联汇编写入系统调用仅打印 4 个字符,但仅在使用“=a”作为输出寄存器时[重复]

  • 772
这个问题在这里已经有了答案:
何时在扩展 GCC 内联汇编中使用 Earlyclobber 约束? (2 个回答)
3 分钟前关闭。

作为一项统一任务,我为 write 系统调用编写了一个非常简单的包装器。这是针对 i386 的。代码编译为:

gcc -ffreestanding -fno-stack-protector -nostdlib -nostdinc -static -m32 -Wall -g -O2

我想知道为什么注释掉的代码只打印出 4 个字符,无论 num_bytes 是多少。

int my_write(int fd, void *buf, unsigned num_bytes){
int ret;
asm volatile (
    "mov $4, %%eax;"
    "mov %1, %%ebx;"
    "mov %2, %%ecx;"
    "mov %3, %%edx;"
    "int $0x80;"
    : "=r"(ret)
    : "r"(fd), "r"(buf), "r"(num_bytes)
    : "eax", "ebx", "ecx", "edx", "memory"
);
/* Does not work
asm volatile (
    "mov $4, %%eax;"
    "mov %1, %%ebx;"
    "mov %2, %%ecx;"
    "mov %3, %%edx;"
    "int $0x80;"
    "mov %%eax, %0;"
    : "=a"(ret)
    : "r"(fd), "r"(buf), "r"(num_bytes)
    : "ebx", "ecx", "edx", "memory"
); 
*/
return ret;
}

我知道我可以使用:

    asm volatile (
    "int $0x80;"
    : "=a"(ret)
    : "a"(4), "b"(fd), "c"(buf), "d"(num_bytes)
    : "memory"
    );

这也很好用。但是我想知道上述两种方法之间有什么不同。=a我是否使用or并不重要,=r因为 i386 的调用约定规定 syscall 的返回值位于eax.

我尝试编写静态缓冲区并使用固定的 num_bytes,但问题仍然存在。我还尝试使用另一个优化标志进行编译-Og。使用除 for 之外的任何内容-O2都会导致代码在启动后立即以状态 1 退出,即使版本正常工作。但这可能是由代码中的某些其他函数引起的,因为my_write使用-O2和编译的部分的 objdump-Og没有显示差异。但是我没有在任何其他功能中发现错误,所以我希望有人有一个想法。感谢您的任何意见。编辑:请原谅与格式的斗争

c
  • 1 1 个回答
  • 56 Views

1 个回答

  • Voted
  1. Best Answer
    rodrigo
    2023-12-08T05:50:17+08:002023-12-08T05:50:17+08:00

    在我看来,您将寄存器的破坏列表视为保留寄存器的列表,您想为自己使用它,并认为编译器将远离它们。

    但事实并非如此。破坏列表的作用是通知编译器这些寄存器在执行过程中可能已经更改了值asm,并且之后的值将被视为未定义。

    但是,通过在代码中按名称使用寄存器并使用r约束,您可能会面临编译器为您在代码中使用的约束选择相同寄存器的风险。这很可能会失败。

    为了调试此类问题,您应该反汇编生成的代码并将其与您的意图进行比较。例如,您注释的代码:

    asm volatile (
        "mov $4, %%eax;"
        "mov %1, %%ebx;"
        "mov %2, %%ecx;"
        "mov %3, %%edx;"
        "int $0x80;"
        "mov %%eax, %0;"
        : "=a"(ret)
        : "r"(fd), "r"(buf), "r"(num_bytes)
        : "ebx", "ecx", "edx", "memory"
    );
    

    在我的机器中拆解为:

    mov    0x10(%esp),%eax  // load fd
    mov    0x14(%esp),%esi  // load buf
    mov    0x18(%esp),%edi  // load num_bytes
    mov    $0x4,%eax   // ooops!
    mov    %eax,%ebx
    mov    %esi,%ecx
    mov    %edi,%edx
    int    $0x80
    mov    %eax,%eax  // store ret
    

    正如您所看到的,选择的寄存器为%1is ,但您在使用它之前eax用 a 覆盖了它。$4

    未注释的代码只是偶然起作用,因为eax没有选择寄存器来执行任何操作。

    您有两种选择来解决这个问题。最简单的方法是使用特定的寄存器来进行约束,正如您在问题末尾所示的那样。这样就不会发生冲突。

    另一个解决方案是要格外小心,不要覆盖任何可能仍然需要的寄存器。像这样的东西:

    asm volatile (
        "push %1;"
        "push %2;"
        "push %3;"
        "pop %%edx;"
        "pop %%ecx;"
        "pop %%ebx;"
        "mov $4, %%eax;"
        "int $0x80;"
        : "=a"(ret)
        : "r"(fd), "r"(buf), "r"(num_bytes)
        : "ebx", "ecx", "edx", "memory"
    );
    

    这编译为:

    mov    0x10(%esp),%eax
    mov    0x14(%esp),%esi
    mov    0x18(%esp),%edi
    push   %eax
    push   %esi
    push   %edi
    pop    %edx
    pop    %ecx
    pop    %ebx
    mov    $0x4,%eax
    int    $0x80
    

    对于所有这些堆栈舞蹈来说,这看起来有点傻,但它应该可行。

    顺便说一句,如果你写"=a"(ret)为输出约束,那么它mov %%eax, %0是多余的。mov %eax, %eax这就是为什么你的代码最后会得到额外的结果。

    • -1

相关问题

  • 比 * 更快的乘法

  • 在 C 中的 scanf() 格式说明符中使用宏获取字符串长度

  • 如何将#define的数据类型设置为long double?

  • 不兼容的常量指针

  • OpenGL 中的非渐变颜色变化

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve