使用内联汇编时出现分段错误(核心转储)错误

2024-01-21

我在 GCC 中使用内联汇编。我想将变量内容向左旋转 2 位(我将变量移动到 rax 寄存器,然后将其旋转 2 次)。我编写了下面的代码,但遇到了分段错误(核心转储)错误。 如果您能帮助我,我将不胜感激。

uint64_t X = 13835058055282163712U;
 asm volatile(
            "movq %0 , %%rax\n"
            "rol %%rax\n"
            "rol %%rax\n"
            :"=r"(X)
            :"r"(X)
         );
printf("%" PRIu64 "\n" , X);

理解内联 asm 的关键是理解每个 asm 语句都有两部分:

  1. 实际汇编器内容的文本,编译器将在其中进行文本替换,但是不明白.

    这是汇编模板在文档中(直到第一个的所有内容: in the __asm__()).

  2. 汇编器的作用的描述,就编译器而言确实明白.

    这就是: OutputOperands : InputOperands : Clobbers in 文档 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html.

    这必须告诉编译器汇编器如何适应编译器围绕它生成的所有代码。代码生成忙于分配寄存器来保存值、决定执行操作的顺序、将操作移出循环、消除未使用的代码片段、丢弃不再需要的值等等。

    实际的汇编器是一个黑匣子,它接受此处描述的输入,产生所描述的输出,并且副作用可能会“破坏”一些寄存器和/或内存。这must是汇编器功能的完整描述...否则编译器在模板周围生成的汇编语言将与其发生冲突并依赖于错误的假设。

    有了这些信息compiler可以决定汇编器可以使用哪些寄存器,并且您应该让它这样做。

所以,你的片段:

 asm volatile(
            "movq %0 , %%rax\n"
            "rol %%rax\n"
            "rol %%rax\n"
            :"=r"(X)
            :"r"(X)
         );

有几个“问题”:

  • 你可能已经选择了%rax基于 asm() 就像一个函数,并且可能期望返回结果%rax——但事实并非如此。
  • 你继续使用%rax,编译器可能(很好)已经分配给其他东西......所以你实际上是“破坏”%rax但你没有告诉编译器这一点!
  • 你指定的=r(X) (输出操作数)它告诉编译器期望某个寄存器中的输出,并且该输出将是变量的新值X. The %0 in the 汇编模板将被为输出选择的寄存器替换。可悲的是,你的大会对待%0作为输入:-( 而输出实际上是%rax-- 如上所述,编译器不知道这一点。
  • 你还指定了r(X) (输入操作数) 告诉编译器安排变量的当前值X放置在某个寄存器中供汇编程序使用。这将是%1 in the 汇编模板。遗憾的是,您的程序集不使用此输入。

    即使输出和输入操作数都引用X,编译器可能不会使%0相同的寄存器%1。 (这允许它使用 asm 块作为非破坏性操作,使输入的原始值保持不变。如果这不是您的模板的工作方式,请不要那样编写。

  • 一般来说你不需要volatile当所有输入和输出都通过约束正确描述时。编译器要做的一件好事就是丢弃asm()如果(所有)输出未被使用...volatile告诉编译器不要这样做(并告诉它许多其他事情......请参阅手册)。

除此之外,一切都很棒。以下是安全的,并且避免了mov操作说明:

 asm("rol %0\n"
     "rol %0\n"   : "+r"(X));

where "+r"(X)表示需要一个组合的输入和输出寄存器,取旧值X并退回一件新的。

现在,如果您不想更换X,然后假设Y是结果,你可以:

 asm("mov %1, %0\n"
     "rol %0\n"
     "rol %0\n"   : "=r"(Y) : "r"(X));

但最好让编译器来决定是否需要mov或者是否可以让输入被销毁。


有几条规则输入操作数值得一提的是:

  • 汇编器must not覆盖任何输入操作数-- 编译器正在跟踪哪些值在哪些寄存器中,并且期望输入操作数予以保存。

  • 编译器期望所有输入操作数之前要读的any 输出操作数被写。当编译器知道给定的输入操作数后不再使用asm(),因此它可以分配输入操作数的注册到输出操作数。有一种东西叫早起的 (=&r(foo))来处理这个小皱纹。

在上面,如果你实际上没有使用X编译器可以再次分配%0 and %1到同一个寄存器!但是(多余的)mov仍然会被汇编——记住编译器真的不理解汇编模板。所以,一般来说,你最好在 C 中调整值,而不是在 C 中调整值。asm(). See https://gcc.gnu.org/wiki/DontUseInlineAsm https://gcc.gnu.org/wiki/DontUseInlineAsm and C++ 循环移位(旋转)操作的最佳实践 https://stackoverflow.com/q/776508


所以这里有一个主题的四种变体,以及生成的代码(gcc -O2):

// (1) uses both X and Y in the printf() -- does mov %1, %0 in asm()
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi   # address of format string
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi       # X = 99
  X = 99 ;                                     rol    %rsi            # 1st asm
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             mov    %rsi,%rdx       # 2nd asm, compiler using it as a copy-and-rotate
      ) ;                                      rol    %rdx
                                               rol    %rdx
  __asm__("\t mov  %1, %0\n"                   jmpq   0x4010a0 <printf@plt>  # tailcall printf
          "\t rol  %0\n"
          "\t rol  %0\n" : "=r"(Y) : "r"(X)
      ) ;

  printf("%lx %lx\n", X, Y) ;
}

// (2) uses both X and Y in the printf() -- does Y = X in 'C'
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi
  X = 99 ;                                     rol    %rsi       # 1st asm
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             mov    %rsi,%rdx  # compiler-generated mov
      ) ;                                      rol    %rdx       # 2nd asm
                                               rol    %rdx
  Y = X ;                                      jmpq   0x4010a0 <printf@plt>
  __asm__("\t rol  %0\n"
          "\t rol  %0\n" : "+r"(Y)
      ) ;

  printf("%lx %lx\n", X, Y) ;
}

// (3) uses only Y in the printf() -- does mov %1, %0 in asm()
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi
  X = 99 ;                                     rol    %rsi
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             mov    %rsi,%rsi   # redundant instruction because of mov in the asm template
      ) ;                                      rol    %rsi
                                               rol    %rsi
  __asm__("\t mov  %1, %0\n"                   jmpq   0x4010a0 <printf@plt>
          "\t rol  %0\n"
          "\t rol  %0\n" : "=r"(Y) : "r"(X)
      ) ;

  printf("%lx\n", Y) ;
}

// (4) uses only Y in the printf() -- does Y = X in 'C'
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi
  X = 99 ;                                     rol    %rsi
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             rol    %rsi    # no wasted mov, compiler picked %0=%1=%rsi
      ) ;                                      rol    %rsi
                                               jmpq   0x4010a0 <printf@plt>
  Y = X ;
  __asm__("\t rol  %0\n"
          "\t rol  %0\n" : "+r"(Y)
      ) ;

  printf("%lx\n", Y) ;
}

希望这能演示编译器忙于将值分配给寄存器,跟踪需要保留的值,最小化寄存器/寄存器移动,并且通常很聪明。

所以诀窍是与编译器一起工作,了解:输出操作数:输入操作数:Clobbers是您描述汇编器正在做什么的地方。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用内联汇编时出现分段错误(核心转储)错误 的相关文章

随机推荐

  • 如何暂停/恢复视频录制

    我想在录制视频时实现暂停 恢复功能 MediaRecorder 没有任何暂停 恢复方法 本机相机应用程序具有暂停 恢复功能 可以实施吗 请指导我 任何帮助或指导将不胜感激 None
  • Azure Pipeline 使用 YAML 触发 Pipeline

    当使用 YAML 完成另一个管道时尝试触发 Azure 管道 有文档 https github com microsoft azure pipelines yaml blob master design pipeline resources
  • Android ListView 有两个按钮设置可见性问题

    I have Drag Sort Listview https github com bauerca drag sort listview与以下项目 1 文本视图2 两个按钮 ON和OFF 一次只有一个按钮可见 关闭状态的图像 http p
  • 是否可以在自定义组件中使用骆驼组件?

    我最近开始使用 Apache Camel 我们正在考虑创建自定义组件来抽象大量逻辑并简化路由 但其中一些逻辑涉及 http 请求和其他部分 这些部分具有我们想要利用的现有 Camel 组件 是否可以从我们的自定义组件的生产者中调用其他组件
  • 使用ajax调用Struts 2动作,直接向响应写入字符串,不返回字符串

    在 struts2 应用程序中 我调用 Ajax 请求并将字符串直接写入响应 如下所示并返回null在操作的执行方法中 ServeletActionContext getResponse getOutputStream print samp
  • 如何将逻辑运算符应用于Python列表中的所有元素

    我有一个 python 中的布尔值列表 我想对它们进行 与 或 或 或 非 并得到结果 下面的代码可以工作 但不太Pythonic def apply and alist if len alist gt 1 return alist 0 a
  • JSP 技巧让模板制作变得更容易?

    在工作中我的任务是转动一堆HTML文件转化为简单的JSP项目 它实际上都是静态的 没有可编程的服务器端逻辑 我应该提到我对 Java 完全陌生 JSP 文件似乎可以轻松地使用常见的包含和变量 就像PHP 但我想知道一种简单的方法来获得模板继
  • 是否可以通过 GitHub API 查明问题是否已通过拉取请求关闭

    I m using github script https github com marketplace actions github script for GitHub actions which allows you to easily
  • 如何在网络浏览器中以适当的比例显示 5 毫米网格?

    我正在创建一个用于显示心电图的 Web 应用程序 心电图通常是在 5 毫米方形网格上以固定比例 10 毫米 毫伏和 25 毫米 秒 绘制的 使用正确的尺寸非常重要 因为读者会将屏幕上看到的内容与可能仅存在于纸上的其他心电图进行比较 最推荐的
  • 生成 Markdown 表?

    有没有办法从对象 Python Ruby Java C 生成表 我想以编程方式创建一个简单的表 我有一些对象 我想将一些属性映射到标题 将集合映射到行 为什么是 Markdown 因为我想稍后手动编辑该文档 现在 整个过程是这样的 报告引擎
  • 如何使用 smack(java) 创建、发送和接收 iq 数据包

    我已连接到服务器 Xmpp 但无法在我的 psi 客户端发送和接收数据包 这是我的代码片段 POSClientIQ posclientiq new POSClientIQ posclientiq connectXMPPServer posc
  • 如何将我的 Google Analytics Web 应用程序转换为移动应用程序?

    我有一个移动应用程序 我应用了我的谷歌分析跟踪 ID 但是当我创建谷歌分析应用程序配置文件时 我最初选择网络应用程序而不是移动应用程序 在探索设置时 我找不到将其转换为移动应用程序的开关 我有超过 10 个具有不同谷歌分析跟踪 ID 的应用
  • 如何在 Angular 6 中导入 sass 文件

    我用 sass 创建了新的角度项目 并创建了名为 sass 的文件夹 其中包含一个名为 variables scss 的文件 在应用程序组件中 我尝试导入这样的变量 import variables 当我运行 ngserve 时出现以下错误
  • NHibernate QueryOver 带有子查询和别名

    我正在努力将以下 简化的 HQL 转换为 QueryOver select subscription from Subscription as subscription where not exists from Shipment as s
  • 在复制数据中丢失从源到接收器的数据

    在我的 MS Azure 数据工厂中 我有一个到嵌套 JSON 数据集的 REST API 连接 源 预览数据 显示所有数据 网上商店7个订单 在 活动复制数据 中 是菜单选项卡 映射 我在其中将 JSON 字段与接收器 SQL 表列进行映
  • 将代码嵌入到 docker 容器中还是将其挂载为卷?

    我是 docker 的新用户 即将在生产环境中从虚拟机迁移到容器 但后来 我突然意识到 最适合我的开发和质量保证环境的东西对于生产来说并不理想 在我的 dev 和 qa 中 我将版本化项目文件夹安装到 python php 命名它 容器中
  • 在 UIWebView 中显示图像

    我想使用在 WebView 中显示图像 img src imageInBundle png 上图在我的包里 我将它与字符串中的其他 HTML 一起加载 如下所示 myWebView loadHTMLString htmlString bas
  • Firebase Android 长按时删除 ListView 中的节点键

    尝试了很多搜索 但两天后我仍然陷入这个问题 我的 Firebase 中有以下数据库 该数据库填充在我的ListView 对于中的每一行ListView显示data1和data2 test 3db7e users W9KkXAidmHgyOp
  • 将 OpenID Connect CallbackPath 设置为 HTTPS

    我有一个 Net Core 3 1 应用程序 托管在 AWS 中的 https 负载均衡器后面 对于外界来说 它是一个 https 站点 但对于 AWS 内部来说 它在平衡器后面的 http 上运行 因此 OpenID Connect 中间
  • 使用内联汇编时出现分段错误(核心转储)错误

    我在 GCC 中使用内联汇编 我想将变量内容向左旋转 2 位 我将变量移动到 rax 寄存器 然后将其旋转 2 次 我编写了下面的代码 但遇到了分段错误 核心转储 错误 如果您能帮助我 我将不胜感激 uint64 t X 138350580