【问题标题】:Android Studio 64 bit inline ARM assemblyAndroid Studio 64 位内联 ARM 程序集
【发布时间】:2019-12-12 19:37:22
【问题描述】:

我正在尝试从 Android Studio 3.4.2 为 aarch64 运行一些内联​​汇编代码,但出现编译错误 error: Don't know how to handle indirect register inputs yet for constraint 'r'

我的代码sn-p如下

std::string system_file = "/system/bin/sh";
std::int64_t file_descriptor = -1;

#ifdef __aarch64__
    __asm__ volatile("mov x8, #180\n\t"
                     "mov x0, %1\n\t"
                     "mov x1, #0\n\t"
                     "svc #1\n\t"
                     "mov %0, x0\n\t"
                    :"=r"(file_descriptor)
                    :"r"(system_file)
                    :"x0","x1","x8"
                    );
#endif

基于本文的 ARM64 系统调用

https://reverseengineering.stackexchange.com/questions/16917/arm64-syscalls-table

取自

https://github.com/torvalds/linux/blob/v4.17/include/uapi/asm-generic/unistd.h

我不确定错误是由于 Android Studio 还是我在这里做错了什么?任何建议表示赞赏。

**** 编辑 ****

我想我可能一直在查看错误的系统调用号。相反,我应该看看

#define __NR_openat 56
__SC_COMP(__NR_openat, sys_openat, compat_sys_openat)

现在我正在尝试这样做,但似乎是错误的。

    std::string system_file = "/system/bin/sh";
    const char *ptr  = system_file.c_str();
    std::int64_t file_descriptor = 0;
    register std::int64_t x8 asm("x8") = 56;
    register std::int64_t x0 asm("x0") = 0;
    register std::int64_t x2 asm("x2") = 0;

    __asm__ volatile("svc #1"
    :"=r"(file_descriptor)
    :"r"(x0),"r"(ptr),"r"(x2),"r"(x8)
    :"memory"
    );

    __android_log_print(ANDROID_LOG_DEBUG,"native_code","file_descriptor: %i",file_descriptor);

我可以在 logcat 中看到一个数字“56”,但无论文件是否存在,它都是相同的数字。和系统调用号一样是巧合吗?

【问题讨论】:

  • 我没有设置来测试这个,但您是否打算将字符串的 address 传递给调用?如果是这样,也许使用system_file.c_str()
  • 不要忘记使用"memory" clobber 或虚拟输入操作数来告诉编译器您读取了system_file 指向的内存。 (您不能将 std::string 按值放入一个寄存器中)。此外,您不需要也不应该在模板中使用任何 mov 指令:使用 register long callnum asm("x0"); 等本地变量将您的输入/输出放在正确的寄存器中。例如iOS ARM64 Syscalls 有点好。与 Linux 电话号码相同的内联汇编应该可以工作。
  • @PeterCordes - 我正在为此尝试使用本地寄存器变量。我无法确认的一件事是svc 指令可能会破坏(r0 除外)的哪些寄存器(如果有)。例如,我知道 amd64 clobbers rcx 和 r11,但找不到 arm64 的类似参考。它会留下其他一切吗?
  • @DavidWohlferd:对于 Linux,我认为是这样。默认情况下,Linux 保留除 return val 之外的所有 reg,除非 syscall 指令本身破坏了一些(如 x86-64 syscall)。 iOS 显然最多可以返回 2 个寄存器并修改标志以指示错误与否,但 Linux 系统调用只使用 1 个 reg 作为返回值,并使用返回值 >= unsigned -4095 对错误“带内”进行编码

标签: android-studio assembly inline-assembly arm64


【解决方案1】:

作为一般规则,您希望将尽可能多的代码移出内联汇编。例如,在这种情况下,您将 #180 移动到 x8 以指示要调用的服务编号。但是如果你想连续打开 2 个文件,两次都在 x8 中通过 180 怎么办?理想情况下,如果可以的话,您应该避免两次将相同的值设置到同一个寄存器中,对吧?但是通过将 mov 放入 asm 中,您会强制每次都设置该值。

您可以通过将作业移出 asm 来避免这种情况。虽然我没有运行它的 arm64 平台,但 Godbolt 的输出看起来正确:

std::string system_file = "/system/bin/sh";
const char *ptr  = system_file.c_str();
register std::int64_t file_descriptor asm("x0");
register std::int64_t x8 asm("x8") = 180;
register std::int64_t x1 asm("x1") = 0;

__asm__ volatile("svc #1"
                :"=r"(file_descriptor)
                :"0"(ptr),"r"(x1),"r"(x8)
                :"memory"
                );

由于 x1 和 x8 被列为inputs(即在第二个冒号之后),gcc 假定它们的值不变。而且由于分配是在 C 代码中进行的,它也知道那里有什么值。

“0”可能看起来有点奇怪,但它表示ptr 将与参数#0 位于同一位置(即file_descriptor,它使用asm("x0"))。所以输入是ptr,输出是file_descriptor,并且都使用同一个寄存器。

我还要提一下,我见过的所有svc 样本都使用svc #0

【讨论】:

  • 谢谢大卫。有没有我可以在线阅读的文章以了解更多关于您使用的语法的信息?我不是开发人员,所以我不太确定这种语法,因为我不经常使用它。
  • 我想说从上面链接的 gcc 文档开始。您可以在该链接的第二块 (constraint) 中看到正在讨论的“0”技巧。此外,docs 中的“上一级”为您提供了所有 gcc 的 asm 概念的链接,包括使用 asm("x8") 作为“显式寄存器变量”。我还推荐this,因为大多数情况下我认为使用内联asm 是个坏主意,但在这种情况下是不可避免的,因为gcc 没有内置svc。如果这回答了您的问题,请不要忘记标记接受。
猜你喜欢
  • 1970-01-01
  • 2011-09-04
  • 2021-08-17
  • 2016-03-31
  • 2019-01-28
  • 1970-01-01
  • 2019-07-22
  • 1970-01-01
相关资源
最近更新 更多