在插入外部交叉编译的SPARC Linux模块时出现重定位错误。

8
首先,我不是专家,请原谅我在解释时可能会出现的错误。
我正在尝试使用Sparc-Linux-GCC-4.4.2交叉编译一个外部Linux模块,用于SPARC机器。Linux内核版本为2.6.36.4-00037-g059aa91-dirty,并已使用LEON处理器的一些文件进行了修补。构建流程已提供给我,它使用LinuxBuild、Buildroot和Busybox。我正在尝试制作一个32位操作系统。
一切似乎都正常,但在我编译模块并尝试将其insmod到SPARC系统后,我收到以下错误:
module hellok:  Unknown relocation: 6

这个错误来自于 ~/linuxbuild-1.0.3/linux/linux-2.6-git/arch/sparc/kernel/module.c 文件。为了完整起见,我会提供整个方法:

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

我理解默认情况是我正在遇到的情况。在我的~/linuxbuild-1.0.3/dist/buildroot/build-br/staging/usr/include/elf.h文件中定义了ELF_R_TYPE(rel[i].r_info(SPARC重定位)类型,其中一些如下:

/* 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.  */
...

因此,重定位6对应于R_SPARC_DISP32,也称为PC相对32位。这在module.c的case语句中定义,但仅在64位部分下。我认为我需要自己编写重定位,或者找出我需要的重定位标志并在编译期间更改标志。我不太理解重定位代码中发生了什么,请帮助我弄清楚如何修复此问题。我认为我不能将操作系统构建为64位,因为它似乎会破坏系统,请帮我找到替代方案。


这可能与内存管理单元(MMU)有关吗? - Stuart
1
只是随口一问,你能否使用[target]-objdump -r从目标机器中反汇编一些已编译的模块,并找出它们正在使用哪种类型的重定位?然后找出在构建系统中选择要使用的类型的位置?你对你的构建系统/配置/头文件与机器上的匹配程度有多自信? - Chris Stratton
我不能确定一切都配置正确了,但是所有步骤都按照文档完成了。目前可用的构建外部模块的文档是针对老版本的Linux,所以我一直在尝试自己解决问题。 - Stuart
1
查看正在运行的系统中 /lib/modules 下是否有任何未使用的内容,如果有,请执行以下操作:readelf -r something.ko | grep -o 'R[^ ]*' | sort -u 并比较系统上的一个和您构建的一个。您确定正在使用的内核源与系统上的内核版本相同(即 uname -a)吗?是否有 /proc/config.gz 可供检查? - Chris Stratton
1
没有放置在/lib/modules中。没有/proc/config.gz。我通过“make xconfig”构建了一些非自定义模块,它们具有与我正在尝试构建的模块相同的重定位标志。我可以使用以下标志删除[.eh_frame]和重定位错误: -fno-dwarf2-cfi-asm: 将DWARF 2展开信息作为编译器生成的“.eh_frame”部分发出,而不是使用GAS的“.cfi_*”指令。使用这个会有什么问题吗? - Stuart
显示剩余4条评论
1个回答

2
我在自己的模块中遇到了完全相同的问题,这是针对 Leon-Linux 配置 (linuxbuild-1.0.1) 的。我的解决方法是将代码的 'case R_SPARC_DISP32' 部分 (4行) 移动到 '#endif /* CONFIG_SPARC64 */' 后面。虽然这样有点取巧,但至少现在我可以 insmod 模块了... 现在我需要查找应用程序调用模块例程时,用户界面方面的副作用。
敬请期待...
最好的问候, Karim

我以前也试用过这个,但是因为不确定是否合适,所以感到不舒服。我认为-fno-dwarf2-dfi-asm标志更好,但不能确定。 - Stuart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接