本文详细记录了Contiki动态加载模块Loader在IAR下移植遇到的问题,分析及解决。
1. 编译错误
1.1 could not open source file
Contiki是在Linux平台下开发的,其源码引用了Linux库头文件(仅仅引用库文件,自己实现,比如dlfcn.h
的dlopen
函数),当在IAR编译时提示could not open source file
错误,解决方法是将这些头文件从Linux找出来并加到IAR的工程目录,Linux库头文件可能又包含其他库头文件,不过不用担心,会很快收敛的:-) 涉及到文件如下:
./include/malloc.h dlfcn.h features.h unistd.h
./include/i386-linux-gnu/bits/predefs.h wordsize.h dlfcn.h environments.h esizes.h confname.h types.h posix_opt.h
./include/i386-linux-gnu/sys/cdefs.h
./include/i386-linux-gnu/gnu/stubs.h stubs-32.h
./include/getopt.h
为了尽量少改动代码,根据源码,用文件目录core/linux_lib
组织这些被引用库头文件,记得将相应路径加到IAR的预处理路径中,linux_lib
文件压缩包文件:linux_lib.rar。
1.2 FILE未定义
编译提示FILE
未定义,在malloc.h
文件,如下,在Contiki并没有用到这些函数,注释掉。
extern int malloc_info (int __options, FILE *__fp);
1.3 expression must be a pointer to a complete object type
编译时,提示错误"expression must be a pointer to a complete object type ",定位于Contiki\core\loader\cmod.c
94,如下:
h.bss = h.data + h.datasize;
h
类型是cle_info
结构体,bss
、data
、datasize
类型分别为void *
、void *
、cle_word
(实质是u16_t
)。理论上是没有错的,但这跟编译器有关(GCC可以,IAR不行),主要问题在于类似的隐式转换,这里将类型转换显式化(假设是32位的处理器),如下:
h.bss = (void *)((u32_t)h.data + h.datasize);
1.4 类型不匹配
编译时,在文件core\loader\cmod.c
提示错误`a value of type "void " cannot be assigned to an entity of type "void ()(void)",提示错误的源代码如下:
cmod_module[imod].fini = cle_lookup(&h, pread, off, "_fini");
init = cle_lookup(&h, pread, off, "_init");
.init
和.fini
是程序初始化与终结代码段,这两个段的代码会最终拼成两个函数_init()
和_fini()
。cle_lookup
函数返回void类型的指针,但cmod_module[imod].fini
是函数指针。
//filename:cle.c function:cle_lookup()
return (void*)(uintptr_t)(addr + s.st_value);
struct cmod_info cmod_module[CMOD_NMODULES];
struct cmod_info
{
void *ram;
void(*fini)(void);
};
问题转化为将指针转换为函数指针(也许在GCC无须修改),修改后代码如下:
cmod_module[imod].fini = (void (*)(void))(cle_lookup(&h, pread, off, "_fini"));
同理,init = cle_lookup(&h, pread, off, "_init")
改成:
init = (void (*)(void))(cle_lookup(&h, pread, off, "_init"));
在elfloader_compat.c
也有类似的问题,修改后代码如下:
elfloader_fini = (void (*)(void))(cle_lookup(&h, xmem_pread, eepromaddr, "_fini"));
elfloader_init = (void (*)(void))(cle_lookup(&h, xmem_pread, eepromaddr, "_init"));
关于如何将指针转换为函数指针可参考博文《如何将一个指针强制转换为一个函数指针》。
1.5 off_t和intptr_t重定义
off_t
分别在platform/stm32test/contiki-conf.h
、core/linux_lib/sys/unistd.h
定义,前者实质是unsigned long
,后者实质是long int
,这里注释后者的定义。
intptr_t
分别在IAR Systems\Embedded Workbench 5.4\arm\INC\stdint.h
、core/linux_lib/sys/unistd.h
定义,前者实质是__INTPTR_T_TYPE__
,后者实质是int,这里注释后者的定义。
1.6 _etext
、_edata
、__data_start
未定义
编译时,在elfloader_compat.c
提示_etext
、_edata
、__data_start
未定义错误,这些符号(通常称特殊符号)被定义在链接脚本中(ld链接器是这样,IAR也是吗?),程序使用需先声明,链接器会在链接成可执行文件时解析成正确的值。在elfloader_compat.c
加入如下代码:
/***added by jelline***/
extern unsigned long _etext; //代码段结束地址 _stext为代码段开始地址
extern unsigned long __data_start; //初始化的数据开始地址
extern unsigned long _edata; //初始化的数据结束地址
1.7 ROM_ERASE_UNIT_SIZE未定义
编译时,在elfloader_compat.c
报ROM_ERASE_UNIT_SIZE未定义错误
,Contiki是按sector擦除的,这里将ROM_ERASE_UNIT_SIZE
定义为COFFEE_SECTOR_SIZE
,在elfloader_compat.c
加入如下代码:
#include "cfs-coffee-arch.h"
#define ROM_ERASE_UNIT_SIZE COFFEE_SECTOR_SIZE
2. 链接错误
2.1 elfloader_arch_allocate_ram等重定义
链接时,提示elfloader_arch_allocate_ram
、elfloader_arch_allocate_ram
、elfloader_arch_relocate
、elfloader_arch_write_rom
重定义,如下:
图1 重定义错误
elfloader_arch_allocate_ram
、elfloader_arch_allocate_ram
、elfloader_arch_relocate
、elfloader_arch_write_rom
分别在文件core/loader/elfloader-stub.c
、cpu/arm/stm32f103/elfloader-stm32f10x.c
实现。elfloader-stub.c
可以理解为elfloader-stub.h
实现的模板,我本以为还要自己实现呢(我的MCU是stm32f103),原来Contiki已经有了,这里将elfloader-stub.c
从工程目录移除,并添加elfloader-stm32f10x.c
。
2.2 elfloader_load、elfloader_unknow重定义
链接时,提示elfloader_load
、elfloader_unknow
重定义,其分别在文件core/loader/elfloader.c、core/loader/elfloader_compat.c
实现。这里将elfloader_compat.c
从工程目录移除。
至此,编译链接成功,接下来就是写些测试例子测试下:-)