如何使用汇编将变量的地址加载到寄存器中?(ARM64 Linux 内核代码)



稍后返回汇编代码…
我把这段代码放在linux内核启动(linux-5.15.68)之后。

.global mydebug2
....(skip)
SYM_CODE_START(primary_entry)
mov x27, #0x3333
ldr x28, mydebug2
add x28, x28, #8
str x27, [x28], #8
ldr x26, =myptr
str x28, [x26]
b .
bl  preserve_boot_args
bl  init_kernel_el          // w0=cpu_boot_mode

buffer "mydebug2"在init/main.c中是这样定义的。

#include <test/mwtest.h>
uint64_t mydebug2[MWBUF_LENGTH] = {0,};
uint32_t myidx = 0;
uint64_t mycnt = 0; // for left shift, 64bit
asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
char *command_line;
char *after_dashes;

当我编译它时,我在最后得到这个错误。

LD .tmp_vmlinux。kallsyms1拱/arm64/内核/头。O:在用在init/main的。bss节中定义的primary_entry': /home/ckim/prj1/LinuxDevDrv/linux-5.15.68/arch/arm64/kernel/head.S:97:(.init.text+0x4): relocation truncated to fit: R_AARCH64_LD_PREL_LO19 against symbolmydebug2。O make: ***[Makefile:1214: vmlinux] Error 1

我猜mydebug2是虚拟地址(如0xffffffc008c4b028),ldr x28, mydebug2指令不能将该地址加载到x28。我如何将地址加载到x28?

(顺便说一句,我知道在当前设置中,物理地址是如何映射到内核虚拟地址的。我看到0xffffffc008000000对应的物理内存0x80200000)。

:正如Nate Eldredge建议的那样,我尝试使用在arch/arm64/include/asm/assembly .h中定义的adrp和add对,如下所示。

/*
* Pseudo-ops for PC-relative adr/ldr/str <reg>, <symbol> where
* <symbol> is within the range +/- 4 GB of the PC.
*/
/*
* @dst: destination register (64 bit wide)
* @sym: name of the symbol
*/
.macro  adr_l, dst, sym
adrp    dst, sym
add dst, dst, :lo12:sym
.endm
.... more (skip) ...

在ARM64寄存器中生成地址的最常用方法是使用adrp与pc相关。ldr x28, =mydebug2语法,从字面量池加载,通常也是一种选择,但在这种情况下,似乎内核的重定位修复还没有完成,所以它不会工作。

所以在理解ARM重定位(例如:str x0, [tmp, #:lo12:zbi_paddr])之后,你想要做

mov x27, #0x3333
adrp x28, mydebug2
add x28, x28, #:lo12:mydebug2
add x28, x28, #8
str x27, [x28], #8

由于完整的相对地址不能放在一条指令中,adrp给出了21位高位,#lo12:mydebug2是12位低位。

实际上,您可以通过在地址计算中构建+8来节省一条指令:

mov x27, #0x3333
adrp x28, mydebug2+8
add x28, x28, #:lo12:mydebug2+8
str x27, [x28], #8

(注意+8需要出现在两个位置,以考虑到+8可能导致低12位的进位,这是add可以作为立即编码的全部。)

您需要在下一行对myptr做同样的事情。

最新更新