在内联 GNU 汇编器中获取字符串长度

2024-01-10

我正在重新学习我在非常旧的 MS-DOS 机器上使用的汇编程序!

这是我对该函数应该是什么样子的理解。它编译但崩溃了SIGSEGV当试图把0xffffffff in ecx.

该代码在具有 32 位 Debian 9 的 VM 中运行。如有任何帮助,我们将不胜感激。

    int getStringLength(const char *pStr){

        int len = 0;
        char *Ptr = pStr;

        __asm__  (
            "movl %1, %%edi\n\t"
            "xor %%al, %%al\n\t"
            "movl 0xffffffff, %%ecx\n\t"
            "repne scasb\n\t"
            "subl %%ecx,%%eax\n\t"
            "movl %%eax,%0"
            :"=r" (len)     /*Output*/
            :"r"(len)       /*Input*/
            :"%eax"         /*Clobbered register*/


    );

        return len;
    }

使用GCC的内联汇编来学习汇编的问题在于,你花了一半的时间来学习gcc的内联汇编如何工作,而不是真正学习汇编。例如,我可以这样编写相同的代码:

#include <stdio.h>

int getStringLength(const char *pStr){

    int len;

    __asm__  (
        "repne scasb\n\t"
        "not %%ecx\n\t"
        "dec %%ecx"
        :"=c" (len), "+D"(pStr)     /*Outputs*/
        :"c"(-1), "a"(0)            /*Inputs*/
        /* tell the compiler we read the memory pointed to by pStr,
           with a dummy input so we don't need a "memory" clobber */
        , "m" (*(const struct {char a; char x[];} *) pStr)

    );

    return len;
}

查看编译器的asm输出在 Godbolt 编译器资源管理器上 https://gcc.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,source:'%23include+%3Cstdio.h%3E%0A%0Aint+getStringLength(const+char+*pStr)%7B%0A%0A+++++int+len%3B%0A%0A+++++__asm__++(%0A+++++++++%22repne+scasb%5Cn%5Ct%22%0A+++++++++%22not+%25%25ecx%5Cn%5Ct%22%0A+++++++++%22dec+%25%25ecx%22%0A+++++++++:%22%3Dc%22+(len),+%22%2BD%22(pStr)%0A+++++++++:%22c%22(-1),+%22a%22(0)%0A%23if+0%0A+++++++++//+works+for+clang+(not+gcc)%0A+++++++++,+%22m%22(*pStr)%0A%23elif+1%0A+++++++++//+works+for+gcc+and+clang%0A+++++++++,+%22m%22+(*(const+struct+%7Bchar+a%3B+char+x%5B%5D%3B%7D+*)+pStr)%0A%23else%0A+++++++++//+nothing.++Works+only+for+ICC%0A%23endif%0A+++++)%3B%0A%0A+++++return+len%3B%0A%7D%0A%0Achar+buff%5B50%5D+%3D+%22hello+world%22%3B%0A%0Aint+foo()%0A%7B%0A++++buff%5B4%5D+%3D+0%3B+++++//+Can+be+optimized+away+if+the+compiler+doesn!'t+think+the+asm+reads+buff%5B4%5D%0A++++int+a+%3D+getStringLength(buff)%3B%0A++++buff%5B4%5D+%3D+1%3B+++++//+because+this+store+makes+the+%3D+0+store+%22dead%22.%0A%0A++++//printf(%22%25s:+%25d%5Cn%22,+buff,+a)%3B%0A++++return+a%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:33.46801346801348,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:g72,filters:(b:'0',commentOnly:'0',directives:'0'),options:'-xc+-std%3Dgnu11+-Wall+-O3+',source:1),l:'5',n:'0',o:'x86-64+gcc+7.2+(Editor+%231,+Compiler+%231)',t:'0')),k:33.198653198653204,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang400,filters:(b:'0',commentOnly:'0',directives:'0'),options:'-xc+-std%3Dgnu11+-Wall+-O3',source:1),l:'5',n:'0',o:'x86-64+clang+4.0.0+(Editor+%231,+Compiler+%232)',t:'0')),k:33.33333333333333,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',m:100,n:'0',o:'',t:'0')),version:4。虚拟内存输入是棘手的部分:请参阅评论中的讨论和在海湾合作委员会邮件列表上 https://gcc.gnu.org/ml/gcc/2017-08/msg00120.html寻找最优化的方法来做到这一点,而且仍然是安全的。

将此与您的示例进行比较

  1. 我不初始化len,因为 asm 将其声明为输出 (=c)。
  2. 没有必要复制pStr因为它是一个局部变量。根据规范,我们已经被允许更改它(尽管因为它是const我们不应该修改它指向的数据)。
  3. 没有理由告诉内联汇编放置Ptr in eax,只是让你的汇编将其移动到edi。我只是将值放入edi首先。请注意,由于值edi正在改变,我们不能仅仅将它声明为“输入”(根据规范,内联汇编不能改变输入的值)。将其更改为读/写输出可以解决此问题。
  4. 不需要将 asm 设为 0eax,因为你可以让约束为你做这件事。作为一个附带的好处,gcc 将“知道”它在eax注册,并且(在优化版本中)它可以重用它(想想:检查 2 个字符串的长度)。
  5. 我可以使用约束来初始化ecx也。如前所述,不允许更改输入值。但既然我定义了ecx作为输出,gcc 已经知道我正在更改它。
  6. 由于 ecx、eax 和 edi 的内容均已显式指定,因此无需再破坏任何内容。

所有这些都使得代码(稍微)更短、更高效。

但这是荒谬的。你到底怎么知道这一切(我可以说“哎呀”吗?)?

如果目标是学习 asm,那么使用内联 asm 并不是最好的方法(事实上,我想说内联 asm 是一种bad idea https://gcc.gnu.org/wiki/DontUseInlineAsm在多数情况下)。我建议您将 getStringLength 声明为 extern 并将其完全编写在 asm 中,然后将其与您的 C 代码链接。

通过这种方式,您可以了解参数传递、返回值、保留寄存器(以及了解哪些寄存器必须保留以及可以安全地用作暂存器)、堆栈帧、如何将 asm 与 C 链接等等。这比了解内联汇编的官样文章更有用。

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

在内联 GNU 汇编器中获取字符串长度 的相关文章

随机推荐

  • Clang AST 解释

    我正在尝试解释 Clang AST 的部分内容 如下图所示 简而言之 我想做的是检查两个变量在不同的程序点是否相同 检查 AST 后 我注意到 AST 部分之间唯一的共同点是蓝色圆圈部分 谁能帮我看看这些十六进制数字在 AST 中对应什么
  • 迭代 git diff --name-only 结果时如何处理文件名中的空格

    我正在编写的脚本需要从 git diff 中遍历每个文件 但是 我不知道如何处理文件名中的空格 任何有空格的文件都会被分成 2 个文件 我知道他们需要被包裹起来 但我不知道在它进入之前如何实现这一点 param 当文件名中有空格时 我应该如
  • Python 3 有 LDAP 模块吗?

    我正在将一些 Java 代码移植到 Python 我们想使用 Python 3 但我在 Windows 中找不到适用于 Python 3 的 LDAP 模块 这迫使我们使用 2 6 版本 这很麻烦 因为其余代码已经是 3 0 格式 您可以使
  • Android - 如何创建意图来打开显示“帐户和同步设置”屏幕的活动

    我在导航到 帐户和同步设置 屏幕时看到了以下日志消息 但我对如何创建一个意图来导航到那里感到困惑 INFO ActivityManager 53 Starting activity Intent cmp com android provid
  • bootstrap container-fluid - 以正确的方式删除边距(溢出)

    如何从引导程序中删除所有边距container fluid类及其行 container fluid padding 0 这基本上满足了我的要求 但它为正文增加了 20px 溢出 那么我应该这样做 body html overflow x h
  • Delphi - 同步线程与框架

    下午好 我有一个框架 这个框架是我通过主窗体动态创建的 主要形式 Interface TInterface Create self with handlingInterface do begin Parent Form1 Left 0 To
  • 推荐的程序结构

    作为初学者 我已经制定了一些想法 但想向社区询问实施以下程序的最佳方法 它可以解码 8 种不同类型的数据文件 它们各不相同 但大多数相似 包含许多相似的字段 另外 有3代系统可以生成这些文件 每个都略有不同 但生成相同类型的文件 我需要制作
  • Typescript方法重载不同类型的参数但相同的响应

    我需要使用 TypeScript 重载一个方法 FooModel有 6 个参数 但 2 个字符串参数是唯一的强制参数 所以不是创建一个FooModel每次我想使用myMethod 我想超载myMethod并创建FooModel一旦进入那里
  • Rails 3 验证 IPv4 和 IPv6 格式

    我知道 IPv4 和 IPv6 的验证格式 但不确定如何将它们组合起来 所以至少一种格式应该是正确的 这是我的验证 validates src ip addr presence gt true uniqueness gt true form
  • 调用 php 中定义为对象变量的匿名函数[重复]

    这个问题在这里已经有答案了 我有如下 php 代码 class Foo public anonFunction public function construct this gt anonFunction function echo cal
  • Nodejs Scraper 不会移至下一页

    大家好 这是我其他问题的后续问题 我创建了一个 Nodejs Scraper 它似乎不想浏览页面 它停留在第一个页面 我的源代码如下 const rp require request promise const request requir
  • 如何验证 Blob 是否存在于已删除列表中

    以下代码将能够查看 blob 是否存在 var blob client GetContainerReference containerName GetBlockBlobReference blobFileName if blob Exist
  • 创建可变参数模板函数来测量和执行其他函数

    我目前正在尝试实现一个函数 该函数将任何其他函数和该函数的一组有 效输入值作为输入 并返回该函数的结果以及打印执行该函数所需的时间 这是我到目前为止所拥有的 template
  • Python 自定义日期时间(?) 格式处理

    假设我有一个代表时间和一周中的天数的字符串 例如 52300 表示一周的第 5 天 星期五 23 小时 00 分钟 我如何将其解析为时间或日期时间对象以添加 timedelta hours 3 并将其恢复为这种奇怪的格式 预期输出为 602
  • C语言使用Unix概念的目录监控代码

    在服务器上 进程监视 Unix 文件系统中的文件 如果客户端发送要监视的文件名 则服务器必须向客户端发送该文件是否被更改或删除的报告 对于服务器 客户端通信 我们应该使用消息队列或套接字 对于文件中的每个更改 服务器都必须通知该更改 对于所
  • 在 MacOS 上将 OpenSSL 更新到 1.1.1

    新的 LTS1 1 1 https www openssl org blog blog 2018 09 11 release111 的版本OpenSSL https github com openssl openssl is out 我如何
  • mongodb db.collection.find({}) 不执行任何超过 .limit(101) 的操作

    node mongodb 版本 2 0 43 和 MongoDB shell 版本 3 2 5 Centos虚拟机 我有一个 无上限 1600 个文档的集合 不是很大 并且所有文档都有标准的 BSON ids if I do db coll
  • sonar.analysis.mode 参数的替代方案

    我正在使用 Sonarqube 7 9 和 Gitlab 以及 Maven docker 映像 该映像使用以下命令调用我的 Sonarqubemvn batch mode verify sonar sonar DskipTests true
  • JavaFX 屏幕分辨率缩放

    我一直在寻找一种方法来做到这一点 但我只找到了这个问题没有答案 https stackoverflow com questions 32453173 javafx scale elements to screen resolution 我正
  • 在内联 GNU 汇编器中获取字符串长度

    我正在重新学习我在非常旧的 MS DOS 机器上使用的汇编程序 这是我对该函数应该是什么样子的理解 它编译但崩溃了SIGSEGV当试图把0xffffffff in ecx 该代码在具有 32 位 Debian 9 的 VM 中运行 如有任何