Thumb-2: Add support for loadable modules
authorCatalin Marinas <catalin.marinas@arm.com>
Fri, 24 Jul 2009 11:32:59 +0000 (12:32 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 24 Jul 2009 11:32:59 +0000 (12:32 +0100)
Modules compiled to Thumb-2 have two additional relocations needing to
be resolved at load time, R_ARM_THM_CALL and R_ARM_THM_JUMP24, for BL
and B.W instructions. The maximum Thumb-2 addressing range is +/-2^24
(+/-16MB) therefore the MODULES_VADDR macro in asm/memory.h is set to
(MODULES_END - 8MB) for the Thumb-2 compiled kernel.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm/include/asm/elf.h
arch/arm/include/asm/memory.h
arch/arm/kernel/module.c

index c207504..c3b911e 100644 (file)
@@ -55,6 +55,9 @@ typedef struct user_fp elf_fpregset_t;
 #define R_ARM_MOVW_ABS_NC      43
 #define R_ARM_MOVT_ABS         44
 
+#define R_ARM_THM_CALL         10
+#define R_ARM_THM_JUMP24       30
+
 /*
  * These are used to set parameters in the core dumps.
  */
index 85763db..376be1a 100644 (file)
  * The module space lives between the addresses given by TASK_SIZE
  * and PAGE_OFFSET - it must be within 32MB of the kernel text.
  */
+#ifndef CONFIG_THUMB2_KERNEL
 #define MODULES_VADDR          (PAGE_OFFSET - 16*1024*1024)
+#else
+/* smaller range for Thumb-2 symbols relocation (2^24)*/
+#define MODULES_VADDR          (PAGE_OFFSET - 8*1024*1024)
+#endif
+
 #if TASK_SIZE > MODULES_VADDR
 #error Top of user space clashes with start of module space
 #endif
index bac03c8..f28c5e9 100644 (file)
@@ -102,6 +102,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
                unsigned long loc;
                Elf32_Sym *sym;
                s32 offset;
+               u32 upper, lower, sign, j1, j2;
 
                offset = ELF32_R_SYM(rel->r_info);
                if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) {
@@ -184,6 +185,58 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
                                        (offset & 0x0fff);
                        break;
 
+               case R_ARM_THM_CALL:
+               case R_ARM_THM_JUMP24:
+                       upper = *(u16 *)loc;
+                       lower = *(u16 *)(loc + 2);
+
+                       /*
+                        * 25 bit signed address range (Thumb-2 BL and B.W
+                        * instructions):
+                        *   S:I1:I2:imm10:imm11:0
+                        * where:
+                        *   S     = upper[10]   = offset[24]
+                        *   I1    = ~(J1 ^ S)   = offset[23]
+                        *   I2    = ~(J2 ^ S)   = offset[22]
+                        *   imm10 = upper[9:0]  = offset[21:12]
+                        *   imm11 = lower[10:0] = offset[11:1]
+                        *   J1    = lower[13]
+                        *   J2    = lower[11]
+                        */
+                       sign = (upper >> 10) & 1;
+                       j1 = (lower >> 13) & 1;
+                       j2 = (lower >> 11) & 1;
+                       offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) |
+                               ((~(j2 ^ sign) & 1) << 22) |
+                               ((upper & 0x03ff) << 12) |
+                               ((lower & 0x07ff) << 1);
+                       if (offset & 0x01000000)
+                               offset -= 0x02000000;
+                       offset += sym->st_value - loc;
+
+                       /* only Thumb addresses allowed (no interworking) */
+                       if (!(offset & 1) ||
+                           offset <= (s32)0xff000000 ||
+                           offset >= (s32)0x01000000) {
+                               printk(KERN_ERR
+                                      "%s: relocation out of range, section "
+                                      "%d reloc %d sym '%s'\n", module->name,
+                                      relindex, i, strtab + sym->st_name);
+                               return -ENOEXEC;
+                       }
+
+                       sign = (offset >> 24) & 1;
+                       j1 = sign ^ (~(offset >> 23) & 1);
+                       j2 = sign ^ (~(offset >> 22) & 1);
+                       *(u16 *)loc = (u16)((upper & 0xf800) | (sign << 10) |
+                                           ((offset >> 12) & 0x03ff));
+                       *(u16 *)(loc + 2) = (u16)((lower & 0xd000) |
+                                                 (j1 << 13) | (j2 << 11) |
+                                                 ((offset >> 1) & 0x07ff));
+                       upper = *(u16 *)loc;
+                       lower = *(u16 *)(loc + 2);
+                       break;
+
                default:
                        printk(KERN_ERR "%s: unknown relocation: %u\n",
                               module->name, ELF32_R_TYPE(rel->r_info));