Vxworks 动态加载的几种方法
使用动态加载目标模块很多好处,比如可以在不破坏原来的环境下增加调试定位功能,
相当于给系统打“补丁”;不需要编译原来的代码(甚至不用原来的代码)而只需要关注正在
调试的代码,这样能减少编译时间和减少映象的加载量。
实现目标模块的动态加载有很多中方法,如在主机环境的界面上通过在目标模块上单击
鼠标右键,选择“Download 文件名”;也可以通过 wShell 和 GDB 命令行窗口实现。本文通
过 tshell 下使用 ld( )、 loadModule( )、loadModuleAt( )中一个函数来实现,当然在代码中也可
以自如的调用它们。
ld 命令是由用户接口子程序库 usrLib 提供的一个加载命令。使用 ld 的前提是在 config.h
中定义 INCLUDE_LOADER。这样,在 usrRoot()函数中就会自动调用加载模块初始化函数
moduleLibInit ( );同时,根据 CPU 类型,自动决定目标模块的格式。如果 CPU 是 MIPS,
PPC,ARM,I80X86,COLDFIRE,SIMSPARCSOLARIS,SH 等,加载的目标模块格式是 elf
类型,就会调用 loadElfInit ( );如果 CPU 是 I960,AM29XXX 等,加载的目标模块的格式则是
coff 类型,就会调用 loadCoffInit t ( )函数。
在 ARM 和 PPC 下,Tornado 编译器生成的.o 或.out 都是 ELF 类型,打开目标文件都会
看到文件头有 ELF(45,4C,46)标记。这时可以通过 ftp 工具把它加载到文件系统(如使用 copy
命令加载到 RAM 盘)中,再调用 ld( )或 loadModule( )函数加载到内存中运行。
ld 的函数原型:MOUDLE_ID ld ( int syms,BOOL noAbort, char *name);参数 syms 决定目
标文件的符号怎么处理,0:添加全局符号到系统符号表中;1:添加全局和局部符号到系统
符号表中;-1:符号不添加到系统符号表中。一般选 1,便于 shell 下使用其中的符号。参数
noAbort 表明是否可以忽略加载期间出现的错误,为 TRUE 则忽略。Name 则为加载的文件名。
含文件路径。
loadModule 的函数原型是 MODULE_ID loadModule ( int fd, int symFlag );fd 为文件描述
符号,需要先打开文件获取 fd;参数 symFlag 含义有 LOAD_NO_SYMBOLS(2);
LOAD_LOCAL_SYMBOLS(4);LOAD_GLOBAL_SYMBOLS(8);LOAD_ALL_SYMBOLS
(0xC)。
假如已经将需要加载的文件 demo.o 放到 RAM 盘中,则加载到内存中的方式有以下几种:
(1)-> ld 1,0,”RamDisk:demo.o”
(2)-> ld (1) <RamDisk:demo.o
(3)->fdT = open (”RamDisk:demo.o”, O_RDONLY);
->loadModule (fdT, 0xC);
->close (fdT);
加载函数返回的是 MODULE_ID,这是该加载模块的标记。使用卸载 unldByModuledId 时
可以使用模块 ID。查看加载模块的具体信息的函数是 moduleShow()。
-> moduleShow
MODULE NAME MODULE ID GROUP # TEXT START DATA START BSS START
--------------- ---------- ---------- ---------- ---------- ----------
RamDisk:demo.o 0x3fff9e8 1 0x3fff3e8 0x3fff4e4 0x3fff4e4
对于某些应用,使用 ld 或 loadModule 会出现如下错误:
Relocation value does not fit in 24 bits.
ld error: error loading file (errno = 0x3d0001).
这是因为函数在内存中的位置超出了跳转的最大距离(一般跳转指令是 24bit 地址,32M)。
为了规避这个问题,可以使用长跳转来完成函数的调用。这就要求编译目标文件时在编译选
项中使用“长调用”的选项。如 ARM 加 –mlong-calls;PPC 处理器加-mlongcall。
当然也可以使用 loadModuleAt ()解决这个问题,将目标模块加载到指定地址,使所需
要的函数在跳转地址以内。其原型是:
MODULE_ID loadModuleAt ( int fd, int symFlag,
char **ppText, char **ppData, char **ppBss);
ppText,ppData,ppBss 是分别是加载后 text 段,data 段,bss 段指向的地址。假如加
载 demo.o 到 1M 空间处(保证这个空间没有被用):fdT = open (”RamDisk:demo.o”,
O_RDONLY); pText = 0x100000; pData = pBss = 0xffffffff; /* (LD_NO_ADDRESS) */
loadModuleAt(fdT, 8 ,& pText, & pData, & pBss); close (fdT);
加载完成后,在 shell 下就可以执行最新的目标文件中的函数了。如果函数以前有的,按
最后加载的函数为准。可见,加载的目标模块都经过重定位,符号解析和添加的过程。
如果目标模块中使用的全局变量或函数在本模块和以前的程序中都没有定义,则加载时会
出现类似 Undefined symbol 的错误。
有时候从主机上下载的目标模块中的函数符号不会出现在目标机 shell 中,反过来也一样,
目标机 shell 生成的符号对主机 wShell 也不可见。出现这种情况的主要原因是目标机和主机
的符号没有同步化,需要在编译映象时定义 INCLUDE_SYM_TBL_SYNC。