armv7 R_ARM_PREL31 relocation out of range error when loading module
Ard Biesheuvel
ardb at kernel.org
Wed Sep 17 23:55:36 PDT 2025
On Thu, 18 Sept 2025 at 04:11, William Zhang <william.zhang at broadcom.com> wrote:
>
> On Wed, Sep 17, 2025 at 3:11 AM Ard Biesheuvel <ardb at kernel.org> wrote:
> >
> > On Wed, 17 Sept 2025 at 04:55, William Zhang <william.zhang at broadcom.com> wrote:
> > >
> > > Hi,
> > >
> > > We recently ran into the driver loading error resulting from
> > > apply_relocate function when it handle the R_ARM_PREL31 case in
> > > arch/arm/kernel/module.c:
> > > section 29 reloc 0 sym '': relocation 42 out of range (0xc2ab9be8 ->
> > > 0x7fad5998)
> > >
> > > After some dig, it seems this issue is caused by this change:
> > > ac3b43283923 ("module: replace module_layout with module_memory")
> > >
> > > I can see the necessity of this change but it seems it can cause this
> > > kind of error under certain configurations. In our particular case, we
> > > use the 2G/2G split and our armv7 board has 1GB ddr memory. This means
> > > the kernel lower memory is from 0x8000-0000 to 0xc000-0000 and vmalloc
> > > range is from 0xc000-0000 and above.
> > >
> > > Before this module layout change, the module is loaded through two
> > > layout sections: core and init. The driver in the test is fairly big
> > > and core module layout(text, data, rodata, sections) can not fit into
> > > the 16MB MODULE memory so both text, data and rodata fallback to
> > > vmalloc range. But with this change, it will text, data and rodata one
> > > by one. So it ends up text in the MODULES memory but rodata is
> > > allocated in VMALLOC and the distance is more than 1GB hence we
> > > believe it causes the above error when R_ARM_PREL31 relocation can not
> > > support more than 1GB offset.
> > >
> > > Since the default split is 1G/3G model, the distance between vmalloc
> > > and module memory is always below 1G so we won't see this issue. But
> > > it seems to us the problem is real when use 2G/2G split.
> > >
> > > It appears the sections containing R_ARM_PREL31 in our driver are
> > > mostly in .ARM.exidx section. Some search shows this is used by ARM
> > > C++ exception handling but our driver does not use C++. Is there a way
> > > to disable such section for R_ARM_PREL31? If not, is there any
> > > workaround or fix to handle this run-time R_ARM_PREL31 relocation
> > > issue? The same apply_relocate function can handle R_ARM_JUMP24 out of
> > > range using PLTS. Can this apply to R_ARM_PREL31?
> > >
> >
> > Hi William,
> >
> > Two options come to mind:
> >
> > 1) use the frame pointer for unwind information instead of the C++
> > exception metadata, i.e., enable CONFIG_UNWINDER_FRAME_POINTER
> >
> > 2) ensure that ARM.exidx sections are allocated from the same bucket
> > as the .text sections they annotate, e.g., something like the below
> >
> > --- a/arch/arm/kernel/module-plts.c
> > +++ b/arch/arm/kernel/module-plts.c
> > @@ -225,6 +225,11 @@
> > mod->arch.init.plt = s;
> > else if (s->sh_type == SHT_SYMTAB)
> > syms = (Elf32_Sym *)s->sh_addr;
> > +#if defined(CONFIG_ARM_UNWIND) && !defined(CONFIG_VMSPLIT_3G)
> > + else if (s->sh_type == ELF_SECTION_UNWIND)
> > + s->sh_flags |= SHF_EXECINSTR;
> > +#endif
> > +
> > }
> >
> > if (!mod->arch.core.plt || !mod->arch.init.plt) {
>
> Thank you Ard for your quick response! We tried the option 1 and
> also tried with gcc -fno-unwind-tables option just for the driver,
> both do not generate any PRREL31 relocation and the driver can load
> successfully.
> But we have some tool that needs to use the unwind tables so option 2
> will be the right direction for us.
>
> Applied your patch however it still reports the same error but from
> different section as we confirmed all the ARM.exidx sections are
> allocated from .text section.
> mod_mem_type 0 execmem_type 0 base 0x7f3e8000 size 0x6eb000
> mod_mem_type 1 execmem_type 4 base 0x7fad4000 size 0x9e000
> mod_mem_type 2 execmem_type 4 base 0xc21a4000 size 0x3dd000
> mod_mem_type 4 execmem_type 0 base 0x7f1ae000 size 0x1000
> mod_mem_type 5 execmem_type 4 base 0x7fb73000 size 0x10c000
> section 43 reloc 11360 sym '': relocation 42 out of range
> (0x7facca74 -> 0xc2580734)
>
> This loc address 0x7facca74 is from the MOD_DATA memory based on mod
> mem allocation log above.
> I also print some extra info about the dstsect with
> pr_err("dstsec name %s add %#lx size %u rel 0x%px rel offset %u\n",
> strtab + dstsec->sh_name, dstsec->sh_addr, dstsec->sh_size,
> rel, rel->r_offset)
> and got this print out
> dstsec name FUNCTION__.2 add 0x7fabd3c0 size 86912 rel 0xc33f931c
> rel offset 63156
>
> FUNCTION__.2 is in the rodata section from the symbol table.
>
> So looks like there is reference to ARM.exidx from the data/rodata
> section and that also need to be taken care of?
It looks like the ARM.exidx, which is now in .text (the first entry in
your table), contains a reference to code that is emitted in .rodata
rather than .text, and .rodata (mod_mem_type 2) is emitted in the
vmalloc space, and is too far from .text.
If FUNCTION__.2 is really a function, why is it in .rodata and not in .text?
In any case, the right way to solve this would be to ensure that all
module sections are allocated in the vmalloc space if they cannot all
fit into the module region. That is slightly tricky to implement
correctly, but we could add a heuristic based on the module's overall
size.
More information about the linux-arm-kernel
mailing list