DSP

VDSP下.doj文件到uClinx下.O文件的转换

2019-07-13 19:56发布

  1   目标 本文开发环境:uclinux-2.6、vdsp-5.0、bfin-uclinux-gcc、bf561 dsp 本文致力于从二进制层面上研究VDSP和GCC两种编译器生成代码的异同。目标是将两种不同编译器产生的obj文件进行链接,使之产生一个可以在uclinux下执行的程序。 之所以选择从doj文件入手,主要原因有以下几点: 1、因为程序最终需要在uclinux下运行,所以一些输入输出的库只能使用uclinux下的库。 2、根据官方的说法,GCC编译代码的效率大概是VDSP的80%,但是对于一些调用很少的函数来讲,其实没有必要在VDSP下编译。我们所需要做的,只是将一些关键的算法在VDSP下实现就行了。 3、doj与o都只是在文件中存放了符号表,而没有产生具体的地址信息,这就为跨编译器的库调用提供了可能。 4、doj和o文件都是ELF格式的,容易相互转换。 2   VDSP程序部分 在VDSP下按默认设置生成一个带LDF的工程文件,在其中的coreA工程中加入test.c文件,其内容如下: #include   int add_func(int a, int b) {        printf("%d + %d = %d", a, b, a + b);        return a + b; }   int sub_func(int a, int b) {        printf("%d - %d = %d", a, b, a - b);        return a - b; } 这个文件提供了两个很简单的函数供uClinux下的文件调用,同时这两个函数也调用了uClinux下提供的printf函数进行输出。 为了尽可能减少干扰,采用Release编译生成test.o。 3   Linux下的程序部分 为了便于与VDSP下生成的OBJ文件相比较,在linux下同样将test.c进行编译生成test.o。只是最终使用VDSP下生成的doj文件进行链接。 在Linux下提供一个主程序main.c,并在主程序中调用VDSP程序提供的add_func和sub_func两个函数。其内容如下: int main(void) { printf(“3 + 4 = %d/n”, add_func(3, 4)); printf(“5 - 3 = %d/n”, sub_func(3, 4)); } 4   e_machine的问题 经过第一步处理后,链接提示为EM_TYPE错误,比较test.doj和main.o的ELF文件头发现:test.doj使用的e_machine值为0x22,而main.o使用的e_machine值为0x6a。这样造成了bfin-uclinux-ld无法识别。 解决方法为直接将test.doj中的e_machine改为0x6a。需要注意的是,VDSP生成的DXE文件所使用的e_machine值为0x6a,这点与doj文件中的值不同。 5   没有为段指定内存区域的问题 经过上一步处理后进行链接,提示:“no memory region specified for loadable section on ‘program’”。 因为VDSP默认的代码是放在program段中的,而uclinux则是放在.text段中的。本程序又没有提供自己的LDS文件进行链接控制,所以产生这样的错误。 不管它,先将program改为.text再说。 下一个错误:“no memory region specified for loadable section on ‘data1’”,与上一个错误类似,VDSP默认的数据段名称是data1,而uclinux则是放在.data中。再改为.data。 下一个错误:“no memory region specified for loadable section on ‘constdata’”,与上一个错误类似,VDSP默认的只读数据段名称是constdata,而uclinux则是放在.rodata中。再改为.rodata。 6   错误的reloc值 错误:“error: test.o contains a reloc (0x00001306) for section .text that refences a non-existent global symbol”。 reloc是elf文件中定义的一个段,它记录了一些需要在链接时确定地址的符号。在VDSP中默认的reloc段名称为.rela.program,在此段中的第一个reloc值就是: r_offset: 0x12 sym_idx: 19 r_sym: .epcrodata r_type: 6 r_addend: 0 r_info: 0x1306 其中sym_idx就指明了这个符号在符号表中的序号。经过查找,在.symtab这个符号表段中确实存在,那么为什么会发生这样的错误呢? 比较VDSP生成的test.o和gcc生成的test.o后发现,在.symtab这个符号表段中有一处不同,那就是sh_info的值。通过查阅elf格式手册后发现,sh_info的值在此处应该指明LOCAL的符号的个数,在gcc生成的test.o中就指明了这点,而VDSP生成的test.o中这个值则为0,这也是提示中出现global symbol的原因。 OK,将sh_info改为LOCAL的符号数量27(0x1b)。 7   final link failed 错误:“final link failed: 错误的值”。 查看所有的section信息,发现了.annotations,.align.program,.align.data1,.align.constdata这几个段的类型无法识别,直接删除这几个段。 还是final link failed。 比较.rela.program段,可以发现VDSP生成的test.o中包含了一些手册中未出现的链接操作类型。即r_info的值。很遗憾没有找到VDSP中对这个字段的定义,只能根据GCC生成的test.o来比较猜测。 以下为VDSP生成的test.o中的rela表: Index *r_offset sym_idx *r_sym *r_type r_addend r_info 1 0x12 19 .epcrodata 0x6 0x0(0) 0x1306 2 0x16 19 .epcrodata 0x7 0x0(0) 0x1307 3 0x1a 28 _printf 0xe0 0x0(0) 0x1ce0 4 0x1a 7 .__operator 0xe 0x0(0) 0x70e 5 0x42 19 .epcrodata 0xe0 0x0(0) 0x13e0 6 0x42 6 .__constant 0xe1 0x10(16) 0x6e1 7 0x42 7 .__operator 0xe2 0x0(0) 0x7e2 8 0x42 7 .__operator 0x6 0x0(0) 0x706 9 0x46 19 .epcrodata 0xe0 0x0(0) 0x13e0 10 0x46 6 .__constant 0xe1 0x10(16) 0x6e1 11 0x46 7 .__operator 0xe2 0x0(0) 0x7e2 12 0x46 7 .__operator 0x7 0x0(0) 0x707 13 0x4a 28 _printf 0xe0 0x0(0) 0x1ce0 14 0x4a 7 .__operator 0xe 0x0(0) 0x70e 以下为GCC生成的test.o中的rela表: Index *r_offset sym_idx *r_sym *r_type r_addend r_info 1 0x10 5   0x7 0x0(0) 0x507 2 0x14 5   0x6 0x0(0) 0x506 3 0x20 8 _printf 0xa 0x0(0) 0x80a 4 0x40 5   0x7 0x10(16) 0x507 5 0x44 5   0x6 0x10(16) 0x506 6 0x50 8 _printf 0xa 0x0(0) 0x80a 对照修改后的rela表为: Index *r_offset sym_idx *r_sym *r_type r_addend r_info 1 0x12 24 constdata 0x6 0x0(0) 0x1306 2 0x16 24 constdata 0x7 0x0(0) 0x1307 3 0x1a 28 _printf 0xa 0x0(0) 0x1ce0 4 0x42 24 constdata 0x6 0x10(16) 0x6e1 5 0x46 24 constdata 0x7 0x10(16) 0x6e1 6 0x4a 28 _printf 0xa 0x0(0) 0x1ce0 8   运行错误 经过上述的修改,程序可以正常链接,但是运行时错误,不断产生exception。 仔细比较两个ELF文件后,发现在rela表中还有一个不是很明显的区别。 链接器在链接时要把一些符号替换为具体的值,比如 R0.h = 0x33这样的汇编语句,它的机器码为 00 e3 00 33,而生成的OBJ文件中,0x33的值将存放在.rodata段中,其保留的机器代码为00 e3 00 00。再由链接器在链接时将值写入到00 00这个位置,从而完成替换工作,而替换的地址就写在rela表的r_offset字段中。但是VDSP生成的test.o中,这个偏移地址指向这四个字节的首地址,即00 e3 00 00的第一个字节。但是GCC生成的test.o中,这个偏移地址却指向第三个字节,即00 00的位置,也就是说它们将相差两个字节。 将这个地址偏差修改后再链接,一切OK。   附:本人自己写的一个简单修改工具,elfeditor,请在资源中查找。欢迎提意见喔!