0

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 <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;

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

LD .tmp_vmlinux.kallsyms1 arch/arm64/kernel/head.o: in function 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 symbol 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 <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) ...
Chan Kim
  • 5,177
  • 12
  • 57
  • 112
  • 1
    Wait, you have a correct `ldr x26, =myptr` just two lines later, getting an address from the literal pool. So you can just do the same thing. `ldr x28, =mydebug2`. Did you just typo out the `=` ? – Nate Eldredge Apr 04 '23 at 02:40
  • 2
    An `adrp / add` combination also works, as in https://stackoverflow.com/questions/64838776/understanding-arm-relocation-example-str-x0-tmp-lo12zbi-paddr/64841097#64841097, and is a couple bytes fewer. – Nate Eldredge Apr 04 '23 at 02:41
  • I tried `ldr x28, =mydebug2` expecing x28 will get 0xffffffc008c4b028, but x28 becomes 0. By the way, mydebug2 is an uninitialized data array so it sits on BSS. What I want to do is write an arbitrary data in mydebug2 array for debug. – Chan Kim Apr 04 '23 at 04:52
  • @NateEldredge And I also tried 'ldr_l x28, =mydebug2` but it was the same. (see my add to the post). – Chan Kim Apr 04 '23 at 05:11
  • Has [`__relocate_kernel`](https://github.com/torvalds/linux/blob/148341f0a2f53b5e8808d093333d85170586a15d/arch/arm64/kernel/head.S#L781) been executed at this point? If not, then yeah, you won't be able to use absolute addresses in any form. – Nate Eldredge Apr 04 '23 at 05:38
  • 2
    So getting the address pc-relative via adrp is the way to go. If you want the address itself, you would want `adr_l`. If you use `ldr_l` then you are actually loading from the address, in which case zero would be a reasonable result to expect since `mydebug2` is not initialized with anything. Also the `=` is incorrect here. Try `adr_l x28, mydebug2 / add x28, x28, #8 / str x27, [x28], #8`. – Nate Eldredge Apr 04 '23 at 05:41
  • @NateEldredge Yes, you are correct. I had been confused by `ldr_l` and `adr_l`. So I used `adr_l x28, mydebug2` to load the address of the variable and using qemu I could see it works. Thanks always! – Chan Kim Apr 04 '23 at 06:24
  • @NateEldredge please write a short answer so that I can choose it. – Chan Kim Apr 04 '23 at 06:25

1 Answers1

1

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 Understanding ARM relocation (example: str x0, [tmp, #:lo12:zbi_paddr]), 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.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82