Issue
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) ...
Solution
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.
Answered By - Nate Eldredge Answer Checked By - Pedro (WPSolving Volunteer)