一篇对于了解KernelRelocate函数,很棒的文章。全局变量在ROM中时不能被写,所以需要把全局变量放到RAM中,这样程序就可以对全局变量进行读写操作。下面是文章的详细内容:
eboot. nb0是一个包含全局变量初始化pTOC结构体的文件, 所以当eboot. nb0启动时就可以通过BootloaderMain()==> KernelRelocate( pTOC) 来 实现eboot. nb0自己初始化自己定义的全局变量的工作, 其实KernelRelocate就相当于ADS启动汇编中对如下四个section的操作:[ luther. gliethttp]
Image$$RO$$Limit
Image$$RW$$Base
Image$$ZI$$Base
Image$$ZI$$Limit
但是我们在源程序中只能找到pTOC的定义ROMHDR * volatile const pTOC = ( ROMHDR *)- 1; 根本找不到对它进行赋值的任何操作, 无论是. s汇编还是任何宏中, 那pTOC又是从哪里得到了有效的数值的呢?这就是我们下面继续讨论的问题, 一切疑惑都可以从romimage. exe中获得答案.[ luther. gliethttp]
romimage. exe源码位于WINCE500\PRIVATE\ WINCEOS\COREOS\NK\TOOLS\ROMIMAGE\ROMIMAGE目录下,
C:\ WINCE500\PRIVATE\ WINCEOS\COREOS\NK\TOOLS\ROMIMAGE\ROMIMAGE\module. cpp| 118| if( token == "pTOC"){
void Module:: check_special_symbol(string token, DWORD o32_section, DWORD offset, MemoryList & memory_list){
...
if( is_kernel()){
if( token == "pTOC"){
//我们在eboot源码PLATFORM\SMDK2440A\Src\Bootloader\Eboot_usb\blcommon.c中定义了该符号,如下:
//ROMHDR * volatile const pTOC = (ROMHDR *)-1; // Gets replaced by RomLoader with real address
//记录pTOC指针所在位置[luther.gliethttp]
m_TOC_offset = offset + m_load_offset; // doesn't get load offset added, because only compared with rva later
LAST_PASS_PRINT printf("Found pTOC at %08x\n", m_TOC_offset);
}
if( needs_signing()){
if( token == "OEMIoControl")
s_oem_io_control = offset + m_初始化c代码定义的非0值全局变量[ luther. gliethttp] load_offset - page_size();
}
...
}
...
}
bin. cpp| 87| kernel-> write_TOC_ptr( romhdr_offset);
bool write_bin( AddressList & hole_list, CopyList & copy_list,
ModuleList & module_list, FileList & file_list,
MemoryList & memory_list, MemoryList & reserve_list,
ModuleList::iterator & kernel, Config & config, MemoryList::iterator xip_mem){
...
// write toc into kernel
if( xip_mem-> is_kernel() && kernel-> is_kernel())
kernel-> write_TOC_ptr( romhdr_offset);//将romimage.exe计算后的toc起始地址存入pTOC 指针所在处,这样eboot.nb0中的pTOC指针就指向了romhdr_offset这个有效空间[luther.gliethttp].
...
}
初始化c代码定义的非0值全局变量[ luther. gliethttp]
void Module:: write_TOC_ptr( DWORD addr){
assert( is_kernel());
if(! m_TOC_offset){
fprintf(stderr, "Error: Found NULL or missing TOC pointer for %s\n", m_name. c_str());
exit( 1);
}
// *(DWORD *)(m_o32_list[0].data.ptr() + m_TOC_offset - page_size()) = addr;
*( DWORD *) rva2ptr( m_TOC_offset) = addr;//等效于eboot.nb0中执行pTOC = (void*)addr;[luther.gliethttp]
}
来看看eboot. nb0是怎么使用pTOC来初始化eboot. bin定义的全局变量的: main==> BootloaderMain()==> KernelRelocate( pTOC)
typedef struct COPYentry {
ULONG ulSource; // copy source address
ULONG ulDest; // copy destination address
ULONG ulCopyLen; // copy length
ULONG ulDestLen; // copy destination length
// (zero fill to end if > ulCopyLen)
} COPYentry;
//
// KernelRelocate: move global variables to RAM
//
static BOOL KernelRelocate ( ROMHDR *const pTOC)
{
ULONG loop;
COPYentry * cptr;
if ( pTOC == ( ROMHDR *const) - 1)
{
return (FALSE); // spin forever!
}
// This is where the data sections become valid... don't read globals until after this
// 就像这句话描述的一样,只有执行完该函数之后,全局变量所在地址处才有了真实的全局变量数值,
// 所以只有执行完该函数之后,我们才能够访问全局变量[luther.gliethttp]
for ( loop = 0; loop < pTOC-> ulCopyEntries; loop++)
{
//ulCopyOffset为若干个COPYentry结构体的内存偏移地址
//COPYentry为全局变量描述结构体
//其中ulDest为全局变量被使用时的目的地址
//其中ulSource为全局变量被压缩存储在ROM中的起始地址
//其中ulCopyLen为全局变量真实个数长度
//其中ulDestLen为期望全局变量长度
//ulDestLen一定>=ulCopyLen
//如果ulDestLen大于ulCopyLen,那么说明,该region的全局变量除了有非0数据之外
//还存在ulDestLen减去ulCopyLen字节的清0数据空间[lutehr.gliethttp]
//其实KernelRelocate就相当于ADS中如下汇编代码:
/* add r2, pc,#-(8+.-CInitData) ; @ where to read values (relative)
ldmia r2, {r0, r1, r3, r4}
cmp r0, r1 ; Check that they are different
beq EndRW
LoopRW 初始化c代码定义的非0值全局变量[luther.gliethttp]
cmp r1, r3 ; Copy init data
ldrcc r2, [r0], #4
strcc r2, [r1], #4
bcc LoopRW
EndRW
mov r2, #0
LoopZI 初始化c代码未定义的全局变量或者强行指定为0值的全局变量[luther.gliethttp]
cmp r3, r4 ; Zero init
strcc r2, [r3], #4
bcc LoopZI
b EndInitC
CInitData
IMPORT |Image$$RO$$Limit| ; End of ROM code (=start of ROM data)
IMPORT |Image$$RW$$Base| ; Base of RAM to initialise
IMPORT |Image$$ZI$$Base| ; Base and limit of area
IMPORT |Image$$ZI$$Limit| ; Top of zero init segment
DCD |Image$$RO$$Limit| ; End of ROM code (=start of ROM data)
DCD |Image$$RW$$Base| ; Base of RAM to initialise
DCD |Image$$ZI$$Base| ; Base and limit of area
DCD |Image$$ZI$$Limit| ; Top of zero init segment
*/
cptr = ( COPYentry *)( pTOC-> ulCopyOffset + loop*sizeof( COPYentry));
if ( cptr-> ulCopyLen)
memcpy(( LPVOID) cptr-> ulDest,( LPVOID) cptr-> ulSource, cptr-> ulCopyLen);
if ( cptr-> ulCopyLen != cptr-> ulDestLen)
memset(( LPVOID)( cptr-> ulDest+ cptr-> ulCopyLen), 0, cptr-> ulDestLen- cptr-> ulCopyLen);
}
return (TRUE);
}