在 GNU C 内联汇编中使用 LDRD?使用什么限制?

2023-12-11

TL;DR我在玩 easm 并烧伤了我的手指。我的限制有意义吗?

当我玩弄内存时,我想测试在 ARM CPU(皮质 A9)上手动读取一些内存

(免责声明:这里的学习目的,当然我同意依赖优化器在 99.999% 的情况下都是正确的做法,但我真的很想理解为什么这里的一切都会爆炸)。

在相关硬件上:

  • 总线 CPU - 内存 64 位宽,所以我尝试使用ldrd一次加载两个 32b 字的指令。
  • 内存中的数据是 128 位对齐的,因此,我们使用两倍ldrd操作说明。

我的问题是,生成的汇编器/生成尝试没有意义,这独立于:

  • 编译器(使用 GCC 和 clang 进行测试)
  • 优化级别(使用-O0 -Og -O2 -O3 测试)
  • 跨/本机(使用arm-linux-gnueabihf-gcc和本机gcc进行测试)

这是一个演示该问题的最小示例:

#include <stdint.h>


// custom structure: represent 128 bits
typedef struct __attribute__ ((packed)) u128
{
  uint32_t a;
  uint32_t b;
  uint32_t c;
  uint32_t d;
} u128;



int main(void)
{
  uint32_t *ptr = (uint32_t*) 0xdeadbeef; // For test purpose, just a random location in memory
  u128 words;

  // 1st read: 64 bits
  asm volatile inline (
    "ldrd %[high_32b], %[low_32b], [%[addr]], #8"
    : [high_32b] "=X" (words.a), [low_32b] "=X" (words.b)
    : [addr] "r" (ptr));

  // 2nd read: 64 bits
  asm volatile inline (
    "ldrd %[high_32b], %[low_32b], [%[addr]], #8"
    : [high_32b] "=X" (words.c), [low_32b] "=X" (words.d)
    : [addr] "r" (ptr));

  return 0;
}

GCC

arm-linux-gnueabihf-gcc -Wall -Wextra -O3 -g -ggdbbroken_asm.c -obroken_asm /tmp/ccIaxiTz.s:汇编器消息: /tmp/ccIaxiTz.s:51:警告:基址寄存器写回,并与传输寄存器之一重叠

拆解(radare2 -A -c 's sym.main; pdf' broken_asm)

│ 0x000003da f3e80221 ldrd r2, r1, [r3], 8
| 0x000003de f3e80232 ldrd r3, r2, [r3], 8 ; broken_asm.c:27 asm 易失性内联(

所以,确实,这个警告是有道理的:ldrd r3, r2, [r3], 8似乎坏了

(预期:来源!=目的地。例如:ldrd r3, r2, [r4], 8)

Clang

clang -mtune=cortex-a9 --target=arm-linux-gnueabihf -isystem /usr/arm-linux-gnueabihf/include -Wall -Wextra -O3 -g -ggdb broken_asm.c -o broken_asm

broken_asm.c:22:5: 错误:Rt 必须是偶数 “ldrd %[high_32b]、%[low_32b]、[%[addr]]、#8” ^ :1:11:注意:在此处实例化为程序集 ldrd r1, r2, [r0], #8 ^broken_asm.c:28:5:错误:基址寄存器需要与目标寄存器不同 “ldrd %[high_32b]、%[low_32b]、[%[addr]]、#8” ^ :1:11:注意:在此处实例化为程序集 ldrd r0, r1, [r0], #8 ^ 产生 2 个错误。

那么,让我们阅读一些错误消息:

基址寄存器需要与目标寄存器不同

好的,与 GCC 类似的问题(是的,它更像是一个错误而不是警告)

错误:Rt 必须是偶数

等等什么?ldrd r1, r2 ...第一个操作数确实必须是偶数寄存器,第二个操作数必须是后面的奇数寄存器。

来自 ARM 指令参考:

Rt:第一个目标寄存器。对于 ARM 指令,必须是偶数,而不是 R14。

Rt2:第二个目标寄存器。对于 ARM 指令,必须为

我很确定我在 EASM 中做了一些错误的事情(因为它实际上几乎是唯一有效的代码行,所以不难猜测)。

这是我到目前为止所理解的限制:

Output:

据我了解,我想要输出的寄存器是只写的。

‘=’标识一个只能写入的操作数

我开始使用“g”作为约束(相同的效果),但选择“X”来给可能的编译器更多的自由:

'X' 允许任何操作数。

Input:

我在两者中都使用“r”ldrd从同一个寄存器读取。 我也尝试过“X”,但遇到了同样的问题。

'r' 允许使用寄存器操作数,前提是它位于通用寄存器中。

一些注释,因为这篇文章太短了:/

  • 主机:Linux(Debian)
  • 目标:Zynq 7000(PS侧:Cortex A9)
  • Clang --version:Debian clang 版本 11.0.1-2
  • 跨 gcc:arm-linux-gnueabihf-gcc (Debian 10.2.1-6) 10.2.1 20210110
  • 本机 gcc:gcc (Debian 10.2.1-6) 10.2.1 20210110
  • 调整二进制文件以手动设置操作码中的寄存器似乎按预期工作

所以,我真的不知道我在这里做错了什么。欢迎任何指点。


GCC一般都支持相同的内联汇编功能为armclang,但不幸的是 GCC 手册没有记录它们。在armclang文档中,您可以read:

如果您使用 64 位值作为 A32 或 32 位 T32 指令中内联汇编语句的操作数,并且使用 r 约束代码,则将分配一对偶数/奇数通用寄存器来保存它。不保证此寄存器分配符合 l 或 h 约束。

使用 r 约束代码可以使用 LDREXD/STREXD 等指令,这些指令需要偶数/奇数寄存器对。您可以使用 Q 和 R 模板修饰符引用保存该值的最高和最低有效一半的寄存器。

所以加载两个寄存器ldrd可能看起来像:

#include <stdint.h>

uint64_t get_pair(void *ptr) {
    uint64_t result;
    asm("ldrd %Q[pair], %R[pair], [%[addr]]"
        : [pair] "=r" (result)
        : [addr] "r" (ptr)
        : "memory");
    return result;
}

尝试一下神箭

如果你想分别提取两半,你可以在 asm 块后面加上类似的内容

uint32_t lo, hi;
lo = result; // conversion truncates
hi = result >> 32;

启用优化后,您可以确信编译器将仅存储高半寄存器,而不会实际执行移位。这是编译器识别的常见习惯用法。


您问题中的代码还有其他一些问题:

  • 您正在使用写回后递增寻址模式,该模式会修改您的地址寄存器,但您不会将此通知编译器。你需要让你的addr输入输出操作数:将其与输出一起列出并使用+r约束。但请记住,除非您稍后在代码中实际使用更新后的值,否则这是没有意义的;如果不是,那么就使用非写回寻址模式。

  • 默认情况下,编译器假定您的 asm 语句不读取任何内存,因此可以对内存写入进行重新排序。asm volatile并不能阻止这种情况发生;它只会阻止编译器在认为其输出未使用时完全忽略 asm。

    A memory正如我上面的示例所示,clobber 是最简单、最粗暴的方法;它告诉编译器,asm 语句可以读取或写入内存的任意部分,因此不能对它之后的其他内存访问进行重新排序。更好的是一个m输入操作数,带有一个变量,其类型具有要读取的大小;我不会在这里打扰它,但是看看如何指示可以使用内联 ASM 参数*指向*的内存?了解更多信息。

我不会将读取放在两个单独的 asm 语句中并使用写回模式来更改它们之间的指针,而是将它们放在单个 asm 语句中并完全跳过写回寻址。这是我的尝试(尝试一下 Godbolt):

  uint64_t hi64, lo64; 
  asm inline("ldrd %Q[lo], %R[lo], [%[addr]] \n\t"
             "ldrd %Q[hi], %R[hi], [%[addr], #8]"
             : [lo] "=&r" (lo64), [hi] "=r" (hi64)
             : [addr] "r" (ptr)
             : "memory");
  words.a = lo64;
  words.b = lo64 >> 32;
  words.c = hi64;
  words.d = hi64 >> 32;

注意“早起”&上的修饰符lo操作数,表明它是在读取所有输入之前写入的。如果没有这个,编译器可能会使用其中之一lo注册为addr,在这种情况下它将被第一个覆盖ldrd指令,第二个指令就会中断。然而我们并没有使用& on hi; since addr之后不需要hi是这样写的,如果他们使用相同的寄存器就可以了。

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

在 GNU C 内联汇编中使用 LDRD?使用什么限制? 的相关文章

随机推荐

  • 如何创建数字选择器对话框?

    我希望能够创建一个对话框 允许用户从指定范围中选择一个数字 我知道现有的小部件 例如来自 Quietlycoding 的小部件和 SimonVT 的小部件 已经做到了这一点 但我很难将它们正确地集成到我的应用程序中 此外 这些主要是小部件
  • MySQL - 当嵌套选择使用该表时如何从表中删除?

    我正在尝试做这样的事情 DELETE FROM table 1 WHERE table 1 id IN SELECT table 1 id FROM table 1 table 2 table 3 WHERE table 1 id tabl
  • 像 xcode 一样批量优化 iphone/ipad 显示的 PNG

    我正在为 ipad 开发杂志查看器 但我在性能方面遇到了困难 我发现显示 png 最昂贵的部分是加载过程 我知道 xcode 能够在构建过程中优化 png 并且此类图像的加载速度要快得多 但我无法将所有图像都包含到构建中 因为它会很大 您知
  • Angular2 路由器中的生命周期挂钩

    在 angular2 路由器中添加生命周期挂钩时遇到问题 如果任何人都可以分享在 Angular2 路由器中添加生命周期挂钩的代码 那将会很有帮助 我了解了如何添加 可以激活 挂钩 感谢埃里克的帮助 与其他生命周期挂钩不同 CanActiv
  • WCF REST 错误 HTTP 307

    我有一个 REST WCF 服务 当尝试对此服务执行如下操作的 POST 时 我收到以下错误 OperationContract WebInvoke Method POST UriTemplate RequestFormat WebMess
  • 有没有一种干净的方法可以在 Spring Web API 中将字符串返回为 json?

    例如 我必须执行如下 RequestMapping value get string method RequestMethod GET public ResponseBody String getString return Hello Wo
  • Python <> 运算符是什么

    到底是什么 lt gt Python 中的运算符 为什么它没有文档记录 据我所知 是不是一样 or is not In Python 2 x lt gt 是相同的 i e 不等于 而不是is not这是 不等同于 但后者是首选 比较运算符
  • MySQL:要求 SSL 未显示在拨款中

    MySQL 8 不显示REQUIRE SSL in the SHOW GRANTS output 在 MariaDB 上 当我使用创建用户时REQUIRE SSL 它显示在补助金中 Server version 10 2 22 MariaD
  • 如何将 C# 数据表传递给 JavaScript 函数

    我在代码隐藏中有这些数据 并尝试以各种格式将其传递给 javascript 函数 列表数组 json 字符串 但无法通过 javascript var 对象获取数据 这是后面代码中最后一个数据格式 List
  • 如何阻止 Visual Studio“发布网站”发布我的 ReSharper 文件夹?

    这真的很烦人 因为它们不是该项目的一部分 我知道这确实很旧 但也许我的回复会对其他人有所帮助 我在这里找到了解决方案 http www meadow se wordpress p 137 基本上 将这些行添加到选项卡上方 Web 部署项目文
  • Windows 7 上的 XAMPP 上的 Kohana 3.2 错误:目录 APPPATH\cache 必须可写

    当我查看使用 Kohana 3 2 创建的网站时 会出现狂野的错误 Kohana Exception 0 目录 APPPATH cache 必须可写 我使用 Windows 7 和 XAMPP 但不知道该怎么做 因为目录 logs 是可写的
  • 数据框中列表列的极坐标交集

    import polars as pl df pl DataFrame a 1 2 3 8 9 4 b 2 3 4 4 5 6 所以给定数据帧 df a b 1 2 3 2 3 4 8 9 4 4 5 6 我想要一个c列 它是a和b的交集
  • 解决方案克隆发生在连续产生“新的最佳分数”的步骤中

    最近引起我注意的是 当产生 新的最佳成绩 时 步子比其他人慢 这绝对是在产生 新的最佳分数 的每一步中发生的解决方案克隆 因此 如果 新的最佳得分 步骤不是连续的 这也没关系 例如 如果我们连续执行 50 个步骤 则解决方案克隆过程将执行
  • 使用 boost 通过 TCP 序列化和发送对象

    我正在尝试通过 tcp 连接发送 C 对象 我的对象都是可序列化的 使用增强序列化 TCP服务器 客户端是用boost asio制作的 基本上我想发送这样的消息 其中包含消息类型 正在发送的对象的类型 和数据本身 序列化对象 以及数据的大小
  • 如何使用 CLI 2.x 添加 preAuthorizedApplications

    在 Azure AD 中 在公开 API 部分下 我希望使用 CLI 2 x 自动注册 API 和 Web 应用程序 我查看了文件here但找不到任何涉及 preAuthorizedApplications 的内容 搜索仅产生了遗留支持的信
  • 为什么释放堆内存比分配它慢得多?

    这是一个经验假设 分配比解除分配更快 这也是one我猜想 为什么基于堆的存储 比如STL容器或其他 选择不将当前未使用的内存返回给系统 这就是为什么缩小以适合成语诞生 当然 我们不应该混淆 heap 记忆与 heap 类似的数据结构 So
  • 有人可以向我解释 git diff 在这里看到什么区别吗?

    我在 Windows 7 上通过 msysgit 使用 git 最近让我非常痛苦的一个问题是 一旦我切换到某些分支 git 就会认为某些文件已被更改 然后我无能为力让它停止认为这些文件已更改 在我的案例中重现的步骤 可能与每个人都不相关 如
  • 如何更改 JTextPane 中特定单词的颜色?

    如何更改特定单词的颜色JTextPane就在用户打字时 我应该覆盖吗JTextPane paintComponent method 不 您不应该重写 PaintComponent 方法 相反 你应该使用StyledDocument 您还应该
  • Chrome 网上应用店扩展应用程序内购买 INTERNAL_SERVER_ERROR

    Chrome 扩展程序中的 Chrome InApp 购买流程取得了部分成功 我已包含 buy js 文件并实现了此处 Chrome 演练中列出的流程 https developer chrome com webstore payments
  • 在 GNU C 内联汇编中使用 LDRD?使用什么限制?

    TL DR我在玩 easm 并烧伤了我的手指 我的限制有意义吗 当我玩弄内存时 我想测试在 ARM CPU 皮质 A9 上手动读取一些内存 免责声明 这里的学习目的 当然我同意依赖优化器在 99 999 的情况下都是正确的做法 但我真的很想