【问题标题】:Arm64 - Absolute branching generating exceptionArm64 - 绝对分支生成异常
【发布时间】:2021-02-06 02:15:16
【问题描述】:

在带有 BLR 的 64arm 中使用其绝对地址 (0x80904) 跳转到 C 函数失败:

LDR x3, =0x80904 
SUB x0, x3, x19
BL print_x0 // Prints something to uart, i.e, x0 == x19

BLR x19  // Fails!

地址不错:BLR x3 有效。

x19 值在print_x0 之后没有变化。我测试过了。

为什么分支会产生这个异常?

谢谢!

【问题讨论】:

  • 根据页表或任何其他内存保护设置,该代码地址是否在可执行内存中?
  • sp 也有效吗?不知道这是否会导致同样的异常。
  • 如果esr_el1的最低6位可以信任,那么根据手册,这将是“地址大小错误,翻译或翻译表基址寄存器的0级”,建议您的页表映射到超出范围的物理地址......否则我同意@Jester:makie确保sp有效,并另外检查您是否启用了SP对齐检查(如果是,请确保SP实际上是16字节对齐)。
  • anotherFunc 有没有可能搞砸了堆栈?无论如何,如果您有办法制作一个,在blr x19 的站点上进行寄存器转储似乎很有用。
  • 这并没有什么帮助——它们都会给出function 的“预期”地址,而不是你最终跳转到的位置。如果你从绝对地址0x80904 中转储几个字,看看它们是否与函数的机器码匹配?

标签: assembly arm64 osdev bare-metal


【解决方案1】:

首先感谢大家的帮助!我让它工作。也就是说,我真的不明白问题出在哪里。

下面的函数创建了一个结构,其中包含我调用的函数的地址(请参阅fn)。这个地址后来在我尝试BLR x19之前被加载到x19中。

struct task_struct * copy_process(unsigned long fn)
{
    struct task_struct *p;
    p = (struct task_struct *)get_free_page();

    char buff[] = "0000000000000000";
    parse_int((unsigned long)fn, buff, 16);
    uart_send_string("fn: ");
    uart_send_string(buff);
    uart_send_string("\n");

    // Configure the task struct
    p->cpu_context.x19 = fn;
    return p;
}

注释掉parse_int((unsigned long)fn, buff, 16); 就足够了,一切都按预期工作。

这是parse_int 的样子:

void parse_int(u64 number, char* str, int base) {
    if(base > 36)
        return;

    int length = 0;
    while (str[length] != '\0') {
        str[length] = '0';
        length++;
    }

    char remainder;
    while(number >= 0 && length >= 0) {
        length--;
        remainder = (number % base);
        if(remainder > 9) {
            remainder -= 10;
            str[length] = 'A' + remainder;    
        } else {
            str[length] = '0' + remainder;
        }
        number /= base;
    }
}

我猜想内存在这个调用中被破坏了,但我不知道是怎么回事。

如果您知道原因,请随时回答原始问题,我会将其标记为好问题。我也会删除我的这个答案,因为这可能对有同样问题的人没有帮助。

谢谢

【讨论】:

  • length 在第二个循环中变为 0 时,您将再次递减它并访问 str[-1] 这很糟糕。
  • number 是无符号的,所以number >= 0 总是正确的。因此,无论输入如何,您都会降到length == 0
  • 真丢脸!我花了几天时间试图找出问题所在。结果我刚刚犯了这些愚蠢的逻辑错误。修复while条件后一切正常。我想我太习惯于使用高级语言进行编程,当我做诸如超越界限之类的事情时,这种语言会受到指责。在我的辩护中,我可能在下班后深夜编码:-)
  • 如果启用-Wextra,您确实会收到number >= 0 错误的警告。
  • parse_int 命名错误 - 它格式化 int 作为字符串。 “解析”意味着阅读文本,而不是创建文本。顺便说一句,为了提高性能,您可能希望对几个常见的基数(如 10 和 16)进行特殊处理,因此编译器将生成除以这些特定常量的特殊情况循环(移位为 16,或乘法逆元为 10)而不是使用缓慢的通用 udiv 指令。请参阅 glibc 的示例:github.com/lattera/glibc/blob/…。 (另请注意返回第一位 ptr 而不是 0 填充)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-06
  • 1970-01-01
  • 2013-04-08
  • 1970-01-01
  • 2021-03-29
  • 2023-03-16
  • 2018-12-11
相关资源
最近更新 更多