linux-kernelcross-compilingkernel-modulesparcrelocation

Relocation Error when Inserting External Cross-Compiled SPARC Linux Module


First off: I am not an expert, so please excuse any mistakes I make trying to explain myself.

I am trying to cross-compile an external Linux module for a SPARC machine using Sparc-Linux-GCC-4.4.2. The version of the Linux kernel is 2.6.36.4-00037-g059aa91-dirty. It has been patched with some files from the LEON processor. The build flow is provided to me and it uses LinuxBuild, Buildroot, and Busybox. I am trying to make a 32 bit OS.

Everything seems to work but after I compile the module and try to insmod it to the SPARC system I get this error:

module hellok:  Unknown relocation: 6

This error comes from ~/linuxbuild-1.0.3/linux/linux-2.6-git/arch/sparc/kernel/module.c I will provide the whole method for the sake of completeness:

int apply_relocate_add(Elf_Shdr *sechdrs,
           const char *strtab,
           unsigned int symindex,
           unsigned int relsec,
           struct module *me)
{
unsigned int i;
Elf_Rela *rel = (void *)sechdrs[relsec].sh_addr;
Elf_Sym *sym;
u8 *location;
u32 *loc32;

for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
    Elf_Addr v;

    /* This is where to make the change */
    location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr
        + rel[i].r_offset;
    loc32 = (u32 *) location;

#ifdef CONFIG_SPARC64
    BUG_ON(((u64)location >> (u64)32) != (u64)0);
#endif /* CONFIG_SPARC64 */

    /* This is the symbol it is referring to.  Note that all
       undefined symbols have been resolved.  */
    sym = (Elf_Sym *)sechdrs[symindex].sh_addr
        + ELF_R_SYM(rel[i].r_info);
    v = sym->st_value + rel[i].r_addend;

    switch (ELF_R_TYPE(rel[i].r_info) & 0xff) {
#ifdef CONFIG_SPARC64
    case R_SPARC_64:
        location[0] = v >> 56;
        location[1] = v >> 48;
        location[2] = v >> 40;
        location[3] = v >> 32;
        location[4] = v >> 24;
        location[5] = v >> 16;
        location[6] = v >>  8;
        location[7] = v >>  0;
        break;

    case R_SPARC_DISP32:
        v -= (Elf_Addr) location;
        *loc32 = v;
        break;

    case R_SPARC_WDISP19:
        v -= (Elf_Addr) location;
        *loc32 = (*loc32 & ~0x7ffff) |
            ((v >> 2) & 0x7ffff);
        break;

    case R_SPARC_OLO10:
        *loc32 = (*loc32 & ~0x1fff) |
            (((v & 0x3ff) +
              (ELF_R_TYPE(rel[i].r_info) >> 8))
             & 0x1fff);
        break;
#endif /* CONFIG_SPARC64 */
    
    
    case R_SPARC_32:
    case R_SPARC_UA32:
        location[0] = v >> 24;
        location[1] = v >> 16;
        location[2] = v >>  8;
        location[3] = v >>  0;
        break;

    case R_SPARC_WDISP30:
        v -= (Elf_Addr) location;
        *loc32 = (*loc32 & ~0x3fffffff) |
            ((v >> 2) & 0x3fffffff);
        break;

    case R_SPARC_WDISP22:
        v -= (Elf_Addr) location;
        *loc32 = (*loc32 & ~0x3fffff) |
            ((v >> 2) & 0x3fffff);
        break;

    case R_SPARC_LO10:
        *loc32 = (*loc32 & ~0x3ff) | (v & 0x3ff);
        break;

    case R_SPARC_HI22:
        *loc32 = (*loc32 & ~0x3fffff) |
            ((v >> 10) & 0x3fffff);
        break;

    default:
        printk(KERN_ERR "module %s: Unknown relocation: %x\n",
               me->name,
               (int) (ELF_R_TYPE(rel[i].r_info) & 0xff));
        return -ENOEXEC;
    };
}
return 0;
}

So I understand the default case is the one I am falling under. ELF_R_TYPE(rel[i].r_info (SPARC Relocations) types are defined in my ~/linuxbuild-1.0.3/dist/buildroot/build-br/staging/usr/include/elf.h file and some are as follows:

/* SPARC relocs.  */

#define R_SPARC_NONE        0   /* No reloc */
#define R_SPARC_8           1   /* Direct 8 bit */
#define R_SPARC_16          2   /* Direct 16 bit */
#define R_SPARC_32          3   /* Direct 32 bit */
#define R_SPARC_DISP8       4   /* PC relative 8 bit */
#define R_SPARC_DISP16      5   /* PC relative 16 bit */
#define R_SPARC_DISP32      6   /* PC relative 32 bit */
#define R_SPARC_WDISP30     7   /* PC relative 30 bit shifted */
#define R_SPARC_WDISP22     8   /* PC relative 22 bit shifted */
#define R_SPARC_HI22        9   /* High 22 bit */
#define R_SPARC_22          10  /* Direct 22 bit */
#define R_SPARC_13          11  /* Direct 13 bit */
#define R_SPARC_LO10        12  /* Truncated 10 bit */
#define R_SPARC_GOT10       13  /* Truncated 10 bit GOT entry */
#define R_SPARC_GOT13       14  /* 13 bit GOT entry */
#define R_SPARC_GOT22       15  /* 22 bit GOT entry shifted */
#define R_SPARC_PC10        16  /* PC relative 10 bit truncated */
#define R_SPARC_PC22        17  /* PC relative 22 bit shifted */
#define R_SPARC_WPLT30      18  /* 30 bit PC relative PLT address */
#define R_SPARC_COPY        19  /* Copy symbol at runtime */
#define R_SPARC_GLOB_DAT    20  /* Create GOT entry */
#define R_SPARC_JMP_SLOT    21  /* Create PLT entry */
#define R_SPARC_RELATIVE    22  /* Adjust by program base */
#define R_SPARC_UA32        23  /* Direct 32 bit unaligned */

/* Additional Sparc64 relocs.  */
...

So relocation 6 corresponds to R_SPARC_DISP32 aka PC relative 32 bit. This is defined in the module.c case statement, but only under the 64-bit section. I think I either need to write the relocation myself or figure out what relocation flag I need and change the flag during compilation. I don't really understand what is going on in the relocation code, so please help me figure out how I should fix this. I don't think I can build the OS as 64-bit because it seems to break the system, so please help me find alternate solutions.


Solution

  • I have exactly the same problem with my own module, for a Leon-Linux configuration (linuxbuild-1.0.1).

    What I did is that I moved the 'case R_SPARC_DISP32' portion of code (the 4 lines) just after the line '#endif /* CONFIG_SPARC64 */'

    That's some bad hack Harry ;-) but at least I can now insmod the module... Now I need to look for side-effects on the user-land side, when apps call the routines of the module.

    So, to be continued...

    Best regards, Karim