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

huangapple go评论54阅读模式
英文:

How to load a variable's address into a register using assembly? (arm64 linux kernel code)

问题

以下是翻译后的代码部分,不包含问题内容:

回到汇编代码一段时间后..
我已经把这段代码放在 Linux 内核启动后 (linux-5.15.68)。

.global mydebug2
....(跳过)
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

"mydebug2" 缓冲区在 init/main.c 中定义如下。

#include <test/mwtest.h>
uint64_t mydebug2[MWBUF_LENGTH] = {0,};
uint32_t myidx = 0;
uint64_t mycnt = 0; // 用于左移,64 位

asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
    char *command_line;
    char *after_dashes;

请注意,这只是代码部分的翻译。

英文:

Back to assembly code after a while..
I have put this code right after the linux kernel starts (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

The buffer "mydebug2" is defined in init/main.c like this.

#include &lt;test/mwtest.h&gt;
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;

When I compile it, I get this error at the end.

> LD .tmp_vmlinux.kallsyms1 arch/arm64/kernel/head.o: in function
> primary_entry&#39;:
&gt; /home/ckim/prj1/LinuxDevDrv/linux-5.15.68/arch/arm64/kernel/head.S:97:(.init.text+0x4):
&gt; relocation truncated to fit: R_AARCH64_LD_PREL_LO19 against symbol
&gt;
mydebug2' defined in .bss section in init/main.o make: ***
> [Makefile:1214: vmlinux] Error 1

I guess mydebug2 is virtual address (like 0xffffffc008c4b028), and ldr x28, mydebug2 instruction cannot load that address into x28. How can I load the address to x28?

(BTW, I know in the current setting, how the physcial address is mapped into kernel virtual address. I see 0xffffffc008000000 corresponds to physcial memory 0x80200000).

ADD : as Nate Eldredge suggested I tried using the adrp and add pair which is defined in arch/arm64/include/asm/assembler.h as below.

/*
 * Pseudo-ops for PC-relative adr/ldr/str &lt;reg&gt;, &lt;symbol&gt; where
 * &lt;symbol&gt; 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) ...

答案1

得分: 1

生成ARM64寄存器中地址的最常见方法是使用adrp指令,使用pc-relative寻址。 使用ldr x28, =mydebug2语法来从字面池中加载数据通常也是一种选择,但在这种情况下,内核的重定位修复尚未完成,因此这种方法不可行。

因此,根据 https://stackoverflow.com/questions/64838776/understanding-arm-relocation-example-str-x0-tmp-lo12zbi-paddr/64841097#64841097,您可以执行以下操作:

    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执行相同的操作。

英文:

The most common way to generate an address in a register on ARM64 is pc-relative, using adrp. The ldr x28, =mydebug2 syntax, to load from the literal pool, is usually also an option, but in this case, it seems that the kernel's relocation fixups have not been done yet, so that won't work.

So following https://stackoverflow.com/questions/64838776/understanding-arm-relocation-example-str-x0-tmp-lo12zbi-paddr/64841097#64841097, you want to do

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

Since the complete relative address won't fit in one instruction, adrp gives you the 21 high bits, and #lo12:mydebug2 is the 12 low bits.

Actually, you can save one instruction by building the +8 into the address computation:

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

(Note the +8 needs to be in both places to account for the possibility that the +8 causes a carry out of the low 12 bits, which is all that add can encode as an immediate.)

You'll need to do the same thing with myptr on the next line.

huangapple
  • 本文由 发表于 2023年4月4日 10:30:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75925087.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定