近日读到这样一段程序。可以在程序运行时调用另一个mod文件并执行。这样可以做到应用自升级,压缩程序文件等实用的功能。
要想理解这点,先看一下正常的BREW程序加载过程。
BREW程序的入口,即相当于c程序的main,是位于AEEModGen.c中的AEEMod_Load函数。这可以从mak文件的连接选项中看出来。
LINK_ORDER = -first AEEMod_Load
这个选项使得链接程序将该函数放在程序文件的0地址处。函数的定义如下:
int AEEMod_Load(IShell *pIShell, void *ph, IModule **ppMod)
{
// Invoke helper function to do the actual loading.
return AEEStaticMod_New(sizeof(AEEMod),pIShell,ph,ppMod,NULL,NULL);
}
BREW底层调用这个函数,需要提供三个参数。pIShell是ISHELL接口的指针,有了它就可以创建和访问其它接口。ph是helper function类函数的函数列表指针,有了它就可以调用MALLOC等函数。ppMod是用于返回给BREW底层的地址,存储生成的module的信息。
AEEMod_Load函数调用了AEEStaticMod_New函数。在这个函数中初始化了module数据结构,ppMod就是指向它的。函数结束后返回BREW底层。
BREW底层稍后通过存储在module数据结构中的函数指针,调用同样位于AEEModGen.c中的AEEMod_CreateInstance函数,生成该Module的某一个Applet实例。在这个函数中会调用到我们所编写的AEEClsCreateInstance函数。在我们这个函数中注册了HandleEvent函数和FreeAppDate函数。这些信息同样通过一个applet数据结构指针返回给BREW底层。这之后,BREW会通过给HandleEvent发各种Event,来驱动程序运行。
通过这整个过程,可以总结出BREW程序运行的必要条件。
程序必须得到IShell指针,Helper Function指针,这样程序中对BREW底层各种函数的调用才能进行。BREW底层必须知道两个地址,通过它们,可以将Event传递给程序,调用FreeAppDate函数。
因此,想在程序中调用另外一个mod,只需要想办法把程序里的IShell指针,Helper Function指针传递给mod,同时得到mod中的HandleEvent函数,FreeAppDate函数的地址就可以了。
剩下的问题就是,程序怎样和mod文件交互呢,mod文件的格式是怎样的?
生成mod的mak文件最后一般有这样两句话,
ld *.o a.elf
arm链接器ld生成ELF格式,之后用格式转换工具fromelf将ELF文件转换成mod文件。
ELF文件是带格式的可执行文件,对它的执行要靠操作系统的解析来进行。而通过–bin选项生成的mod文件,格式却是plain binary,即赤裸裸的二进制机器指令,其实是无格式的。只要将mod文件载入内存,跳转到它的0地址处,就可以一条条指令的执行下来。和mod文件交互,也就是要安排好mod文件0地址处的内容,知道mod文件的调用者会传入什么东西,mod文件会返回什么东西。
下面来看一种实际的调用方法吧。代码如下:
typedef int (*RunLoadMod)(IShell *pIShell, void *ph, IModule **ppMod);
pData = MyLoadZip(pIShell);
if( !pData )
return EFAILED;
DBGPRINTF(”to RunLoadMod”);
if( SUCCESS != ((RunLoadMod)pData)(pIShell,ph,&pOrgMod) )
{
DBGPRINTF(”RunLoadMod_Err”);
goto Crt_Err;
}
这段程序将mod文件读入内存,放在pData缓冲区内,用((RunLoadMod)pData)(pIShell,ph,&pOrgMod)一句执行之。RunLoadMod只不过是用typedef定义的一种函数指针类型。这句话的意思相当于是为待调用的mod文件准备好pIShell,ph,pOrgMod三个数据后,直接跳到内存中的mod文件的第一个字节处,将它当做普通机器指令一样执行。这种方法调用的mod文件,应是一个用完整的BREW框架编译后生成的。0地址处放下的是AEEMod_Load函数。Mod文件直接将Applet信息传递给BREW底层,注册HandleEvent函数。实际上是Mod文件取代了调用者的位置。
但是这段程序很可能会运行失败。因为BREW底层除了传参外,还做了其它事情。这在AEEStdlib.h中可以看出。
#define GET_HELPER() (*(((AEEHelperFuncs **)AEEMod_Load) – 1))
#define GET_HELPER_VER() (*((uint32 *)(((byte *)AEEMod_Load) – sizeof(AEEHelperFuncs *) – sizeof(uint32))))
这两个宏定义可以看成是两个全局常量。第一个是BREW Helper Function的函数指针表,第二个是函数指针表的版本号。上面提到的通过参数传递Helper Function函数指针,是只在模拟器上成立的。手机上其实是通过全局常量形式传递的。为保证mod可以正常运行,也应在缓冲区前保存这两个常量。
这种“冒名顶替”的方式,对于只想压缩一下mod的应用来说是完全可以的。但有时,我们希望新调用的mod只是整个应用的一小部分,调用者继续存在。这就需要调用者充当BREW底层的一部分角色,记住mod返回的各种信息,在适当的时候调用mod中的相应函数。新调用的mod如果想要取得主mod的信息,类似于上面提到的HelperFuncs全局常量指针的方法是个可行的选择。
其实知道了BREW程序的加载过程,就可以比较灵活的达到调用mod的目的,甚至BREW程序架构都是可以改动的。但是改动之后,如何在模拟器上进行调试就又成了一个问题。
程序中调用mod的技巧,在国外几年前就已得到了应用。甚至有家公司专门开发了利用这个技巧压缩mod的工具。
http://www.s-cradle.com/english/products/sophiacompress_brew/index.html
参考文章:
1.How to build A mod to load B mod?
http://brewforums.qualcomm.com/showthread.php?t=11637
2.How to run mod file that is loaded in the HEAP…
http://brewforums.qualcomm.com/showthread.php?t=18413&highlight=modloader
3.深入BREW模块加载机制
http://nicefuture.ycool.com/post.722858.html
4.深度剖析BREW实现原理收藏
http://blog.csdn.net/Gemsea/archive/2006/09/07/1190206.aspx
原译者:陈啸天(cxt_programmer)
请关注www.cpplite.com及bbs.cpplite.com
声明:原文出自大牛——ScreenShot作者A ntony P ranata 。本人英语很烂,之所以瞎译出来一是为了以后参考方便一些,二是在翻译的过程中自己能更仔细的看~。强烈建议大家对照英文原版来看哈。另外有些地方没有翻译,大家意会哈。
原文地址:
http://www.antonypranata.com/articles/new-symbian-os-9-executable-file-format-e32image
前言:
Symbian OS 9已经发布,与之前的版本相比,OS 9增加了许多新特性并有了一些改变,其中之一就是新的可执行文件格式(E32Image)。本文主要讨论了这个新的文件格式,如果你想了解OS 9之前的可执行文件格式,可以阅读一下我之前关于此话题的文章。
在开始之前,请注意我们仅仅讨论的是真机环境下的可执行文件格式(ARM)。为什么不讨论模拟器环境呢?因为运行在模拟器的可执行文件使用了一种不同的文件格式(Cxt注:模拟器下的可执行文件应该是windows的PE格式),如果你之前不了解任何一个平台的可执行文件格式,下面的介绍会更好的帮助你理解E32文件格式。
这篇文章是在Symbian网站或者其他拥有Symbian OS licensees的厂商(例如Nokia)提供的公开信息(文档)之上完成的。虽然我目前在一家手机厂商工作,但这篇文章涉及内容的正确性并未经过公司的核对。尽管我已经紧握最大的努力确保这篇文章内容的正确,但我仍然无法100%保证(Cxt音:看看,大牛总是很谦虚的,我们要做一个谦虚的人哈)。
EABI介绍( Introduction to EABI ):
ABI(应用程序二进制接口,Application Binary Interface)是由ARM及其合作伙伴们制定的一个标准,它定义了如何编译、链接,以及其他工具怎样生成obj文件和可执行文件。这个标准可以让不同编译器生成的obj文件互通,例如可以把不同编译器生成的obj文件组合在一起。EABI(嵌入式应用程序二进制接口, Embedded Application Binary Interface )也是这样的标准,它就是嵌入式平台的ABI。
写这篇文章的时候,有两种编译器可以用于Symbian OS 9——RVCT( RealView Compilation Tools )和GCCE。RVCT是ARM公司开发的编译器,license费用可达数千美元;与此不同,GCCE是一个由 CodeSourcery 开发的免费编译器;顾名思义,GCCE基于GNU编译器。
EABI编译器生成的格式为ELF( Executable and Linking Format ),它不同于Symbian OS 9之前的PE格式。然而Symbian OS并不使用ELF格式,因为Symbian OS通常在大小受限的ROM上,而标准ELF文件的尺寸通常又很大。所以Symbian把标准的ELF转换为Symbian特有的E32Image格式。如果你有安装Symbian SDK,可以在epoc32\tools目录下找到用于把ELF转换为E32Image的工具,它叫elf2e32.exe。
下图展示了Symbian OS 9的新工具链。如你所见,最后生成的是由elf2e32.exe转换完的Symbian特有格式——E32Image。Symbian网站和SDK中有更多关于这方面的介绍。(Cxt注:SDK help: » Symbian OS v9.1 » Symbian OS Tools And Utilities » Build tools guide » The native build targets 目录下有全面详细的文档介绍)。通常你不会看到下图所示的流程,因为Symbian OS使用一些脚本工具(例如bldmake、abld等)自动完成。

如果你不熟悉.dso文件也没关系,它其实与Symbian OS 9以及其他平台的.lib文件是一样的。(Cxt注:.dos提供导出的函数的名称和位置,DLL包含实际的函数和数据。)
我在前面说过,ELF文件通常都很大,不适合用于手机。减少ELF尺寸的方法之一是把函数名字替换为序号。例如可以把MyFunction()替换为1,正如你期望的,这种方法可以极大的减小ELF的尺寸,特别是当函数名称很长时(例如 ThisIsVeryLongFunction ())这种效果更加明显。额等等!如此一来,如何找到序号和函数名称之间的对应关系呢?别急,我们有一个.def文件,它提供了序号与函数名之间的对应关系。(Cxt:呼 …… )
E32Image概述( Overview of E32Image ):
现在让我们来看看E32Image吧。与其他标准的可执行文件类似,它也包含头(Header)、代码段(Code Section)、数据段(Data Section)、导入段(Import Section)等等。

上图展示了E32文件格式,与其他可执行文件格式一样,最开始的部分是header。下面我会着重介绍E32 header里面都有些什么东东。在Header之下还有一些其它section(Cxt:这部分大家意会哈^_^):
· Code section, contains all the object files (.o) of your source code as well as export address table that lists all the exported functions.
· BSS section, contains un-initialized data.
· Data section, contains initialized data.
· Import section, contains the information about all imported functions used by your program.
· Relocation section, contains relocation table needed by Symbian OS loader to load your program.
E32Image头( Header of E32Image ):
头信息也许是最有趣的部分,因为它包含了可执行文件的很多信息。E32ImageHeader的声明可以在SDK的\epoc32\include\f32image.h中找到。看看class E32ImageHeaderV 的声明,这可是E32Image文件格式完整的头信息。如果你仔细看了class E32ImageHeaderV ,你会发现它继承了E32HeaderComp(E32HeaderComp继承了E32ImageHeader)。下图对它们的关系做了更好的说明:

下面的代码片段展示了 EImageHeader , EImageHeaderComp 和 E32ImageHeaderV 的声明,请注意我删减了部分函数以及注释以便于更清晰的查看:
class E32ImageHeader
{
public :
TUint32 iUid1;
TUint32 iUid2;
TUint32 iUid3;
TUint32 iUidChecksum;
TUint iSignature; // ’EPOC’
TUint32 iHeaderCrc; // CRC-32 of entire header
TUint32 iModuleVersion; // Version number for this executable (used in link resolution)
TUint32 iCompressionType; // Type of compression used (UID or 0 for none)
TVersion iToolsVersion; // Version of PETRAN/ELFTRAN which generated this file
TUint32 iTimeLo;
TUint32 iTimeHi;
TUint iFlags; // 0 = exe, 1 = dll, 2 = fixed address exe
TInt iCodeSize; // size of code, import address table, constant data and export dir
TInt iDataSize; // size of initialised data
TInt iHeapSizeMin;
TInt iHeapSizeMax;
TInt iStackSize;
TInt iBssSize;
TUint iEntryPoint; // offset into code of entry point
TUint iCodeBase; // where the code is linked for
TUint iDataBase; // where the data is linked for
TInt iDllRefTableCount; // filling this in enables E32ROM to leave space for it
TUint iExportDirOffset; // offset into the file of the export address table
TInt iExportDirCount;
TInt iTextSize; // size of just the text section, also doubles as the offset for the
// iat w.r.t. the code section
TUint iCodeOffset; // file offset to code section, also doubles as header size
TUint iDataOffset; // file offset to data section
TUint iImportOffset; // file offset to import section
TUint iCodeRelocOffset; // relocations for code and const
TUint iDataRelocOffset; // relocations for data
TUint16 iProcessPriority; // executables priority
TUint16 iCpuIdentifier; // 0×1000 = X86, 0×2000 = ARM
};
class E32ImageHeaderComp : public E32ImageHeader
{
public :
TUint32 iUncompressedSize; // Uncompressed size of file
// For J format this is file size - sizeof(E32ImageHeader)
// and this is included as part of the compressed data :-(
// For other formats this is file size - total header size
};
class E32ImageHeaderV : public E32ImageHeaderComp
{
public :
SSecurityInfo iS;
// Use iSpare1 as offset to Exception Descriptor
TUint32 iExceptionDescriptor; // Offset in bytes from start of code section to Exception Descriptor,
// bit 0 set if valid
TUint32 iSpare2;
TUint16 iExportDescSize; // size of bitmap section
TUint8 iExportDescType; // type of description of holes in export table
TUint8 iExportDesc[1]; // description of holes in export table - extend
};
我将逐一解释上面这些字段。从下面的列表中,左端的16进制数表示这个字段在文件中的偏移量,例如iUid2的位置是从文件头部开始0×04的位置。换句话说,如果你把可执行文件用二进制编辑器打开,然后找到偏移量0×04的位置你就可以找到iUid2。注意E32Image使用小端格式(little-endian order)。
E32ImageHeader :
0×00: iUid1 ,可执行文件的第一个UID。这个UID可被看作是一个系统级别的标识符,例如Dlls是 0×1000 0079 ,可执行程序是 0×1000 007A 。如果你想更多的了解Symbian OS UID相关信息,可以 访问这里 。
0×04: iUid2 ,可执行文件的第二个UID。当两个对象拥有同一个UID1的时候,就需要用这个UID来区分它们,例如 0×1000 39CE 表示多态接口Dll( polymorphic interface DLLs ), 0×1000 008d 表示静态接口( static interface (shared library) )。
0×08: iUid3 ,可执行文件的第三个UID。 每个程序的UID3都不同。下面这段不翻译了,贴个图看图识字吧^_^。
It is unique for each application. Developers have to request this UID from Symbian Signed service. Symbian OS 9 applications usually have UIDs in the range of 0×200 0000 and 0×2FFF FFFF. Examples from Symbian OS SDKs, like S60 SDK or UIQ SDK, have the UIDs in the range of 0xA000 0000 and 0xAFFF FFFF. There are also some UID available for testing, which can be chosen from the range 0×0100 0000 to 0×0FFF FFFF.

0×0C: iUidChecksum ,校验前面提到的3个UID。Symbian SDK中提供了一个uidcrc.exe工具完成该功能;下面的例子展示了如何生成这三个UID: 0×1000 007A , 0×1000 39CE 和 0xA000 017F 的校验码:
C:\>uidcrc 0×1000007A 0×100039CE 0xA000017F
0×1000007a 0×100039ce 0xa000017f 0×1e7cca07
0×10: iSignature ,E32文件唯一签名( unique signature )。值统一为“EPOC”。图示如下:
![]()
0×14: iHeaderCrc ,整个头信息的完整校验,使用 CCITT CRC-32 算法。
0×18: iModuleVersion ,可执行版本号。该信息用于链接过程(linking process)。在S60 3 rd , iModuleVersion 值为10( 0×0000 000A )(Cxt注:大牛这里可能笔误了,iModuleVersion是T u int32,占4个字节,这个值应该是0×000A 0000)。
![]()
0×1C: iCompressionType ,一个UID,表示使用哪种压缩算法压缩了可执行文件。如果值为0就说明未压缩。就我目前所看到的,只使用了一种压缩算法:由RFC 1951定义的 Deflate/Huffman 算法。它的UID是 KUidCompressionDeflate ( 0×101F 7AFC )。需要注意的是,未来也许会使用其他压缩算法。
![]()
0×20: iToolsVersion ,生成这个可执行文件的 ELFTRAN 工具版本。
0×24: iTimeLo ,时间戳。 the lowest word of the timestamp when the file is created.
0×28: iTimeHi ,时间戳。 the lowest word of the timestamp when the file is created.
0×2C: iFlags ,可执行文件的一个标记,比如 KImageDll , KImageNoCallEntryPoint 等。这些标记定义在f32image.h中。f32image.h中有一些函数定义用来解释这些标记的意思。例如:如果flag值为 0×1200 002A ,我们可以把它看作: 0×1000 0000 + 0×0200 0000 + 0×0000 00020 + 0×0000 0008 + 0×0000 00002 ,结合f32image.h中的常量声明,我们可以发现:
· 0×10000000 = KImageImpFmt_PE ,可执行文件使用ELF-derived入口。
· 0×02000000 = KImageHdrFmt_V ,header的版本支持。
· 0×00000020 = KImageEpt_Eka2 ,EKA2可执行文件。
· 0×00000008 = KImageABI_EABI ,可执行文件为EABI image file。
· 0×00000002 = KimageNoCallEntryPoint ,no call to entry point。
0×30: iCodeSize , is the size of code section, import address table, constant data and export dir.
0×34: iDataSize , size of initialised data.
0×38: iHeapSizeMin , the minimum size of the heap.
0×3C: iHeapSizeMax , the maximum size of the heap.
0×40: iStackSize , the size of the stack.
0×44: iBssSize , the size of the un-initialized data section.
0×48: iEntryPoint , offset into code of entry point.
0×4C: iCodeBase , where the code is linked for.
0×50: iDataBase , where the data is linked for.
0×54: iDllRefTableCount , the number of DLLs imported by this program.
0×58: iExportDirOffset , offset into the file of the export address table.
0×5C: iExportDirCount , the offset of the export address table.
0×60: iTextSize , size of just the text section, also doubles as the offset for the iat w.r.t. the code section.
0×64: iCodeOffset , file offset to code section, also doubles as header size.
0×68: iDataOffset , file offset to data section.
0×6C: iImportOffset , file offset to import section.
0×70: iCodeRelocOffset , relocations for code and const.
0×74: iDataRelocOffset , relocations for data.
0×78: iProcessPriority , executables priority.
0×7A: iCpuIdentifier , the identifier of CPU. Look at the following constant for all possible values:
enum TCpu
{
ECpuUnknown=0,
ECpuX86=0×1000,
ECpuArmV4=0×2000,
ECpuArmV5=0×2001,
ECpuArmV6=0×2002,
ECpuMCore=0×4000
};
E32ImageHeaderComp :
开始之前,我们需要知道2个结构:定义在e32cmn.h的 ScapabilitySet 和 SSSecurityInfo 。Symbian OS 9可以看到它们,它们保存了平台安全相关信息,比如能力(capability)、安全标识符(secure identifier)和厂商标识符(vendor identifier)。
struct SCapabilitySet
{
enum {ENCapW=2};
TUint32 iCaps[ENCapW];
};
struct SSecurityInfo
{
TUint32 iSecureId;
TUint32 iVendorId;
SCapabilitySet iCaps; // Capabilities re. platform security
};
好了,让我们回到 E32ImageHeaderV :
0×80: iS.iSecureId ,可执行文件的安全ID(secure ID)。就是可执行文件的UID3。
0×84: iS.iVendorId ,可执行文件的厂商ID(vendor ID)。对于第三方应用程序,值为0。
0×88: iS.iCaps.iCaps ,运行可执行文件需要的能力。Symbian OS所有的能力定义可以在 e32capability.h 文件中找到。
enum TCapability
{
ECapabilityTCB = 0,
ECapabilityCommDD = 1,
ECapabilityPowerMgmt = 2,
ECapabilityMultimediaDD = 3,
ECapabilityReadDeviceData = 4,
ECapabilityWriteDeviceData = 5,
ECapabilityDRM = 6,
ECapabilityTrustedUI = 7,
ECapabilityProtServ = 8,
ECapabilityDiskAdmin = 9,
ECapabilityNetworkControl = 10,
ECapabilityAllFiles = 11,
ECapabilitySwEvent = 12,
ECapabilityNetworkServices = 13,
ECapabilityLocalServices = 14,
ECapabilityReadUserData = 15,
ECapabilityWriteUserData = 16,
ECapabilityLocation = 17,
ECapabilitySurroundingsDD = 18,
ECapabilityUserEnvironment = 19,
};
能力以bit的形式表示,例如 ECapabilityTCB 意味着最低有效位(LSB),如果设置了最低有效位,可执行文件就具有TCB能力。
0×90: iExceptionDescriptor , is offset in bytes from start of code section to Exception Descriptor, bit 0 set if valid
0×94: iSpare2 , reserved.
0×98: iExportDescSize , size of bitmap section.
0×9A: iExportDescType[1] , type of description of holes in export table.
0×9B: iExportDesc[1] , is description of holes in export table.
Example:
下面我们来看一个例子,来帮助你更好的理解上面说的那些字段。我们将使用标准的Symbian OS build command来编译3.0 SDK提供的Helloworld Basic例子。
C:\Symbian\9.1\S60_3rd\S60Ex\helloworldbasic>bldmake bldfiles
C:\Symbian\9.1\S60_3rd\S60Ex\helloworldbasic>abld build gcce urel
现在切换到 \epoc32 \r elease\gcce\urel 目录,使用 Elf2E32 工具读取E32 image头信息。做法如下:
C:\Symbian\9.1\S60_3rd\S60Ex\helloworldbasic>elf2e32 –e32input=helloworldbasic.exe
然后你将会看到 helloworldbasic.exe 的头信息;输出的过程可能会很长,所以一个比较好的方法是将他转储到文件,便于我们进一步分析:
C:\Symbian\9.1\S60_3rd\S60Ex\helloworldbasic>elf2e32 –e32input=helloworldbasic.exe > helloworldbasic.txt
打开 helloworldbasic.txt ,你将会看到头信息(我已经用绿色的注释标明了 E32ImageHeaderV 的字段):
E32ImageFile ’helloworldbasic.exe’
V2.00(505) Time Stamp: 00e0eb0a,d2525b80 // iTimeStampHi, iTimeStampLo
EPOC Exe for ARMV5 CPU // iCpuIdentifier = 0×20001 (ARMv5)
Flags: 1200002a // iFlags
Priority Foreground
Entry points are not called
Image header is format 2
Image is compressed using the DEFLATE algorithm // iCompressionType
Uncompressed size 0000b788
Image FPU support : Soft VFP
Secure ID: a000017f // iSecureId
Vendor ID: 00000000 // iVendorId
Capabilities: 00000000 00000000 // iSs.iCaps.iCaps
Exception Descriptor Offset: 00002561 // iExceptionDescriptor
Exception Index Table Base: 00012dfc
Exception Index Table Limit: 000130bc
RO Segment Base: 00008001
RO Segment Limit: 0000a77c
Export Description: Size=000, Type=01 // iExportDescSize = 000 iExportDescType = 01
Export description consistent
Module Version: 10.0 // iModuleVersion
Imports are ELF-style
ARM EABI
Built against EKA2
Uids: 1000007a 100039ce a000017f (1e7cca07)
// iUid1 = 1000007a, iUid2 = 100039ce, iUid3 = a000017f, iUidChecksum = 1e7cca07
Header CRC: 023aca0d // iHeaderCrc
File Size: 0000b788 // iUncompressedSize
Code Size: 0000b0bc // iCodeSize
Data Size: 00000000 // iDataSize
Compression: 101f7afc // iCompressionType
Min Heap Size: 00001000 // iHeapSizeMin
Max Heap Size: 00100000 // iHeapSieMax
Stack Size: 00005000 // iStackSize
Code link addr: 00008000 // iCodeBase
Data link addr: 00400000 // iDataBase
Code reloc offset: 0000b650 // iCodeRelocOffset
Data reloc offset: 00000000 // iDataRelocOffset
Dll ref table count: 10 // iDllRefTableCount
Offset Size Relocs #Relocs
Code 00009c 00b0bc 00b650 00007d +002504 (entry pnt)
// iCodeOffset = 00009c iCodeSize = 00b0bc iCodeRelocOffset = 00b650
Data 000000 000000
// iDataOffset iDataSize
Bss 000000 // iBssSize
Import 00b158 // iImportOffset
全都在这了!!希望你能够喜欢这篇文章!
本文来自:我爱研发网(52RD.com) 详细出处:http://www.52rd.com/S_TXT/2008_12/TXT13397.HTM
:(
52RD研发网】联发科(MTK)受到第4季度营收目标由原先下滑9~16%,修正到衰退30~33%影响,公司目前存在库存偏高压力,对此对此联发科官方表示,第4季度度将在库存跌价损失提留上,采取较积极作法,配合10号公报实施,将提列长期投资损失,预期这样动作将会让联发科本季度业外亏损扩大到新台币10亿~15亿元水平,较以往单极度不到5亿元水平大幅增加。
联发科预估将先提列长期投资损失约3亿元,例如对络达等其它投资公司的损失,加上其亦有雷曼兄弟连动债等部位,将额外提列投资损失约3亿元,至于最重要的库存跌价损失,因为第4季度营收目标下修幅度颇大,加上展望未来景气不是很乐观,因此,联发科已决定采取较积极提列方式,预期库存跌价损失将会较过去每季度平均3亿~4亿元水平增加数倍。
联发科过去提列库存跌价损失方式,是采用一般标准会计原则,若客户超过120天没有提货,则提列100%库存跌价损失,若90天没有提货者,则采取另外比例来提列损失。由于联发科第4季度营收最后数字,比原先预期差距颇大,因此,当季度库存跌价损失就会相对来得较大。另外,联发科为因应未来景气,第4季度会再多提留一点,造成库存跌价损失金额再加大。
根据联发科最新财测目标,单季度营收将介在190亿~195亿元间,在公司结算10、11月营收为153.87亿元后,联发科12月营收将约38亿~42亿元水平,虽然近期传出因大陆客户出现农历年前的提前拉货动作,可望让联发科12月营收数字较预期40亿元好一些,但初估单月营收仍无法突破50亿元大关。
此外,由于2009年第1季度景气看来仍是持续低迷,加上单季度工作天数原本就较少,2009年第1季度淡季度效应可能会出现加倍效果,迫使联发科下季度营收可能还较本季度下滑30%以上。至于本季度因联发科受到业外亏损将较以往大出许多的影响,单季度EPS可能会较第3季度6.5元水平下滑逾半,2009年第1季度EPS亦可能进一步再下滑,联发科将面临史上最严厉获利寒冬期。
Tags: mtk
一直以来对于ARM体系中所描述的RO,RW和ZI数据存在似是而非的理解,这段时间对其仔细了解了一番,发现了一些规律,理解了一些以前书本上有的但是不理解的东西,我想应该有不少人也有和我同样的困惑,因此将我的一些关于RO,RW和ZI的理解写出来,希望能对大家有所帮助。
要了解RO,RW和ZI需要首先了解以下知识:
ARM程序的组成
此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。
一个ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量
RW是程序中的已初始化变量
ZI是程序中的未初始化的变量
由以上3点说明可以理解为:
RO就是readonly,
RW就是read/write,
ZI就是zero
ARM映像文件的组成
所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。
Image文件包含了RO和RW数据。
之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。
Q:为什么Image中必须包含RO和RW?
A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。
ARM程序的执行过程
从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。
实际上,RO中的指令至少应该有这样的功能:
1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中
在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。
说了上面的可能还是有些迷糊,RO,RW和ZI到底是什么,下面我将给出几个例子,最直观的来说明RO,RW,ZI在C中是什么意思。
1; RO
看下面两段程序,他们之间差了一条语句,这条语句就是声明一个字符常量。因此按照我们之前说的,他们之间应该只会在RO数据中相差一个字节(字符常量为1字节)。
Prog1:
#include <stdio.h>
void main(void)
{
;
}
Prog2:
#include <stdio.h>
const char a = 5;
void main(void)
{
;
}
Prog1编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog2编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 61 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
以上两个程序编译出来后的信息可以看出:
Prog1和Prog2的RO包含了Code和RO Data两类数据。他们的唯一区别就是Prog2的RO Data比Prog1多了1个字节。这正和之前的推测一致。
如果增加的是一条指令而不是一个常量,则结果应该是Code数据大小有差别。
2; RW
同样再看两个程序,他们之间只相差一个“已初始化的变量”,按照之前所讲的,已初始化的变量应该是算在RW中的,所以两个程序之间应该是RW大小有区别。
Prog3:
#include <stdio.h>
void main(void)
{
;
}
Prog4:
#include <stdio.h>
char a = 5;
void main(void)
{
;
}
Prog3编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 1 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
可以看出Prog3和Prog4之间确实只有RW Data之间相差了1个字节,这个字节正是被初始化过的一个字符型变量“a”所引起的。
3; ZI
再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。
Prog3:
#include <stdio.h>
void main(void)
{
;
}
Prog4:
#include <stdio.h>
char a;
void main(void)
{
;
}
Prog3编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 97 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
编译的结果完全符合推测,只有ZI数据相差了1个字节。这个字节正是未初始化的一个字符型变量“a”所引起的。
注意:如果一个变量被初始化为0,则该变量的处理方法与未初始化华变量一样放在ZI区域。
即:ARM C程序中,所有的未初始化变量都会被自动初始化为0。
总结:
1; C中的指令以及常量被编译后是RO类型数据。
2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
3; C中的已被初始化成非0值的变量编译后市RW类型数据。
附:
程序的编译命令(假定C程序名为tst.c):
armcc -c -o tst.o tst.c
armlink -noremove -elf -nodebug -info totals -info sizes -map -list aa.map -o tst.elf tst.o
编译后的信息就在aa.map文件中。
ROM主要指:NAND Flash,Nor Flash
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
LRC概述
LRC 歌词是一种包含着“[*:*]”形式的“标签(tag)”的、基于纯文本的歌词专用格式。最早由郭祥祥先生(Djohan)提出并在其程序中得到应用。这种歌词文件既可以用来实现卡拉OK功能(需要专门程序),又能以普通的文字处理软件查看、编辑。当然,实际操作时通常是用专门的LRC歌词编辑软件进行高效编辑的。以下具体介绍LRC格式中的“标签”。
时间标签(Time-tag)
形式为”[mm:ss]“或”[mm:ss.fff]“(分钟数:秒数)。数字须为非负整数, 比如”[12:34.5]“是有效的,而”[0x0C:-34.5]“无效。
它可以位于某行歌词中的任意位置。一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。根据这些时间标签,用户端程序会按顺序依次高亮显示歌词,从而实现卡拉OK功能。另外,标签无须排序。
(如果我们的网站不错,请把“九九Lrc歌词网 =>www.99lrc.net”网址推荐给你的朋友使用,谢谢!)
标识标签(ID-tags)
其格式为”[标识名:值]“。大小写等价。以下是预定义的标签。
[ar:艺人名]
[ti:曲名]
[al:专辑名]
[by:编者(指编辑LRC歌词的人)]
[offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。
样例
[ar:unknown]
[ti:sample]
[al:none]
[by:me]
[01:02.355][00:00]This line should be sung twice
[00:05.7]And this one… once only.
开发标准(供程序员阅读参考)
以下列出了开发支持LRC格式的软件时应遵守的一些标准。
无论是否在行首,行内凡具有“[*:*]”形式的都应认为是标签。(注意:其中的冒号并非全角字符“:”)
昨天遇到一个十分麻烦的问题,我把MTK编译时遇到这样的错误:
Error : L6218E: Undefined symbol Image$$ZI$$Limit (referred from sys_stackheap.o).
Not enough information to produce a SYMDEFs file.
而没有这三个:
Not enough information to list image symbols.
Not enough information to list the image map.
Not enough information to list the image sizes and/or totals.
网上只有两种解释,是这样:
1. reimplement __user_initial_stackheap()
解决办法大概意思是重新装配__user_initial_stackheap()函数。
2.分配内存的时候,要分配内存的结构中使用了ARM不支持的数据类型.通常定义了结构体的指针,然后用
malloc分配空间时为结构体类型指针,而ARM不支持这种数据类型,所以会有这种错误。解决办法:用
typedef预定义这个结构类型,使得编译器识别这种类型。
第一种方法离我太远,应该不会涉及到;
第二种方法试过,没用,但我也大概知道方向是malloc函数的问题。
今天终于解决:
这个问题是由于代码或者Lib中调用了 C Lib的malloc或者类似于strdup,printf 这样的会调用malloc的
C Lib function 引起的。MTK Platform不支持 C lib的malloc,而用 Ctrl Buffer机制代替了malloc,
以便于调试memory leak问题。MTK中的Osl层有专门处理内存的函数,于是我想,我用malloc是跳过Osl层
直接分配内存,这样没有经过系统处理,危险性大。所以我用系统自带的OslMalloc和OslMfree来处理内
存空间,问题解决。
“常常有人问我,联发科在过去10年里迅速崛起,有什么秘诀?并且希望这些’秘诀’对正在快速发展中的中国大陆芯片企业有所启发.”给人印象深刻的一副标志性的大眼镜,从外表看,联发科技董事长蔡明介并不具备在商场上拼杀的枭雄造型.然而,他却是台湾赴美引进半导体技术的先驱,被誉为台湾集成电路设计“教父”.

1995年蔡明介领导台湾“联华电子”的集成电路设计部门独立创业,成立了“联发科技”的前 身多媒体小组,联华电子是仅次于台积电的全球第二大流片厂。IC行业流传着一句难以溯源的话———现在的芯片业,每个非大厂都不希望联发科涉及自己的领 域,那样自己就“离死不远了”。
并非危言耸听,经历10余年的发展,目前联发科已成为世界顶尖的IC专业设计公司,位居全球 消费性IC片组的领航地位。产品领域覆盖数码消费、数字电视、光储存、无线通讯等多个系列,是亚洲唯一连续六年蝉联全球前十大IC设计公司唯一的华人企 业,被美国《福布斯》杂志评为“亚洲企业50强”。
没有听说过联发科,只要是对手机产业链稍有了解的人,也会对MTK这三个字母的组合有些印象。MTK是联发科技股份有限公司的英文简称,英文全称叫MediaTek.
「杀手锏」一站式方案
或许有人不太喜欢这家公司,因为从早年的黑手机到现在的山寨机,草莽气息背后是MTK方案被广泛应用。波导、夏新,老牌手机厂商令人堪忧的一份份财务报告中总会附带“受到黑手机、山寨机冲击”的说明,MTK可被视作始作俑者之一。
但你不得不佩服MTK的创新和成功,甚至也找不出更多口诛笔伐的理由。因为MTK的存在,近年来国内手机产业进入了前所未有的蓬勃发展阶段。事实上MTK除了为山寨机提供方案,也同时为正规厂商提供全面解决方案,包括近两年迅速崛起的天语、金立等正规品牌。
“我们有个客户的工程师曾经很形象地打了个比方,他说MTK的解决方案更像是一本”烹饪手册 “,它只能保证厨师不会把鱼香肉丝做成京酱肉丝,至于这道菜做出来的色香味如何,那还是看终端厂商的水准。”联发科首席财务官兼新闻发言人喻铭铎曾向本报 记者表示,MTK提供的是一个把厂商开发过程中可能遇到的问题尽量提前考虑到的参考性的解决方案。厂商在开发过程中如果遇到一些原先技术能力较为薄弱的地 方,可以从这个参考性的解决方案中获得借鉴与补充,从而不断弥补原来的不足。
凭借这种“菜谱”方式,MTK在大陆市场上结结实实地击败了众多国际芯片厂商。操作软件,甚至液晶、摄像头等部件,MTK都可以“一站解决”,大大缩短了手机的生产环节并降低了研发成本。这正是Turnkeysolution(一站式方案)模式。
Turnkeysolution正是MTK手机平台的核心。在这套解决方案中,将手机芯片和 手机软件平台预先整合到一起。这种方案可以使终端厂商节约成本,加速产品上市周期。MTK公司的产品因为集成较多的多媒体功能和较低的价格在大陆手机公司 和手机设计公司得到广泛的应用。加上MTK的完工率较高,基本上在60%以上,这样手机厂商拿到手机平台基本上就是一个半成品,只要稍稍的加工就可上架出 货了。
数年前,德州仪器(TI)的LoCosto、OMAP平台,英飞凌的ULC解决方案,还一度是这些国际知名厂商广泛采用的模式。然而MTKTurnkey方案的巨大成功则证明了在这一领域平台技术不再是TI、英飞凌这些巨头的专属。
「性价比」到底到什么程度
1999年,当时的联发科在台湾科技行业中还仅是一家生产光存储和DVD芯片的二线厂商。然而当时,蔡明介看中了利润更加丰厚的手机芯片业务,恰逢中国手机市场进入第一次高速增长期,他的眼光自然而然地落在了中国大陆市场。
2001年开始,MTK开始在手机行业逐渐渗透Turnkeysolutio模式,这种模式内蕴着极高的性价比。在国内手机制造仍处于朦胧阶段的时刻,这种性价比着实让人难以抗拒。
号称2007年全年销量达到1500万台的天宇朗通便广泛采用了MTK的产品。“感谢 MTK,感谢台湾人。”天宇朗通销售总监卢伟冰在今年天津手机展新闻发布会上曾表示,中国拥有全球配套最完善的手机产业集群。都说手机没有核心技术,都说 在欧美人的手里,大家都觉得很失望。但是我们的台湾人做了很多基础芯片研究,MTK的问世把没有技术、没有解决方案这个历史进行了改写。
天宇朗通是近年来当之无愧的国产手机黑马。在其面向下游的销售模式中,和MTK的高性价比、 低价出牌也有着惊人的相似。本地一位零售商代表向记者表示,天宇朗通完全摒弃了过去手机企业的价保模式,取而代之的是以较低的价格出货给代理商,再由下游 定价。“一款产品我们的进货价若只有300元,定价600元销售也有可能,毕竟同等功能配置的洋品牌还要贵上至少100元。”
300元,只是手机厂商给予渠道的价格,他们从MTK手中获得的方案呢,性价比究竟能高到什 么程度?以行业内最普遍使用的联发科(MTK)低端方案6223为代表。可支持T-flash扩展卡,彩屏,具备自动应答留言机功能,键盘采用动感炫灯。 如量产二十万台,单价在14美金左右,不到人民币100元。
同样是在2001年,联发科在台湾上市,更素有“台湾股王”之称。直至2007年,其营业收 入已达到新台币804.09亿元,较2006年增长51%.根据最新市场资料显示,2007年联发科年手机晶片出货量高达1.5亿颗,全球市场占有率近 14%,仅次于德州仪器及高通。
「声誉」骂名、美名
联发科真正开始在大陆市场上崭露头角,2005年是关键年。在那个还没有取消手机核准制度的 年代,早前依靠贴牌韩国方案而迅速崛起的国产手机在洋品牌的全线反攻下,跌入了全线溃败的谷底。夏新、波导、TCL,大量的民族手机品牌惨遭洗牌,国产手 机也因此背上了“质量不好”的包袱。
然而就在这一时期,具有中国特色的黑手机跃上了手机产业的历史舞台。这些手机主要通过模仿知 名品牌的外形。以及低廉的价格来吸引消费者。黑手机一词诞生于联发科Turnkeysolutio模式广泛进入中国市场之前,但因为MTK的存在,黑手机 发生了“革命性”的变化。MTK也因此背上了“黑手机之父”的骂名。
早期的黑手机,在没有采用MTK方案之前规模并不大,以翻新、通过各种渠道寻找廉价方案为主 要表相。在MTK全面解决方案进入中国市场后,生产方一次过便可以获得包括基带芯片、操作软件,甚至液晶显示屏、摄像头等部件等全套或半套解决方案。一举 将原来的6-9个月甚至是一年的手机研发周期,缩短到了3个月以内。
直至国家取消手机牌照核准制度,黑手机这个称谓已不合时宜,源于MP3行业的“山寨”一词取而代之。不过MTK并未因此而被改称作“山寨机之父”。在国产手机产业发展到目前的规模,讨论MTK的成分变得丝毫没有意义。
可以说,黑手机、山寨机空前的泛滥与MTK有着必然联系,但无论从技术还是道德的范畴,如今 更为理性的观点大都认为不应该由MTK为这种泛滥负责。Turnkeysolutio模式的崛起、MTK的成功,背后更多体现的是手机行业新兴市场飞速发 展的带动和技术的成熟和全球化,技术门槛降低后的必然结果。
至于黑手机、山寨机偷税漏税等问题,更多体现的是法律环境的不健全。在知识产权保护、税收、产品质量、工人权益、生产和市场准入、走私控制等方面所存在的漏洞,而这些实际才是黑手机为生产、流通和销售创造优越环境的根本。
MTK从未为任何有关于自己的报道辩驳,但依然落入了尴尬的境地。这种情况目前随着越来越多的品牌厂商采用其方案而获得改善,MTK的模式更成为不少IC企业效仿的对象。
联发科发展的几个瞬间
1995年,成立……
蔡明介领导台湾“联华电子”的集成电路设计部门独立创业,成立了“联发科技”的前身多媒体小组,初始只有20余人。
2001年,钟情手机芯片
联发科在台湾成功上市,51岁的蔡明介则必须做出选择———创业前4年,联发科必须在CD- ROM、CD-RW以及DVD-ROM等几代芯片的稳定发展后找到下一个拳头产品,这可以说是决定联发科生死的问题。按照蔡明介经验,一定要符合市场足够 大;有一定的技术门槛和别人拉开差距;和联发科原来的开发技术有一定的相关性这三个标准。
经过内部激烈的讨论后,联发科决定投入手机芯片研发。当时台湾手机代工行业逐渐兴起,作为上游,联发科最终决定跟着客户走,杀入手机芯片领域。
2003-2004年,成长的低谷
是联发科颇为难熬的一段日子,手机行业的前景在哪里?当时,处于中游的台湾手机代工企业活得并不好,选定道路的联发科则不得不加大研发费用的投入。
此时,联发科之前的光存储和DVD产品市场已经逐渐饱和,价格开始下滑。手机芯片虽然在 2003年底开发成功,却迟迟无法打开市场。更重要的是,想“跟着”的那些客户并没有预想的成功,联发科必须去寻找新的客户。2004年四季度,代工行业 的连锁反应落在了联发科身上,其毛利率大跌40%%,在投资人会议上,蔡明介甚至亲自拿出采用自家芯片的手机进行演示,希望能够给投资人以信心。
据统计,当年全球IC设计产业总体增长率为27%,而联发科营收仅增长了5.23%.
2004年,猛虎出笼……
最终台湾一家叫达智的手机制造小公司因为无力采购国际IC设计巨头的产品,在实属无奈的情况 下采用了联发科的方案。由于当时手机IC设计领域并没有重视媒体功能的植入,大多仍采用双芯片分别控制通话和多媒体的设计,成本较高。而联发科因为此前做 CD、DVD芯片的开发,有这方面基础,迅速打开了市场。
2008年,跻身世界前三
手机芯片部门的收入已经占到联发科营收的50%以上,联发科也仅次于德州仪器及高通,跻身世界前三大IC手机设计厂商。
文/《南方都市报》
以400RMB从中关村某JS那入手一部铁三角AD300, 属于Hifi耳机中入门级别的。
缺点很多,第一是太重,第二是声音有点混人声不太好(不过比之前PHILIPS的好多了),看下官方介绍吧。
ATH-AD300 空气动圈耳机
·三维翼支撑提供更大的舒适性并更加贴服
·强大的钕磁体确保声音具有强劲效果
·耳垫使用柔软顺滑的布织物
·舒适、完全适耳的设计可减轻紧固感
·40mm直径驱动器专为开放式耳机设计,提供强劲的声音
·高纯度OFC输入耳线 (3.0m)
·玻璃钢强化外壳
型号类别 开放空气动圈型
单元直径 Ø40 mm
驱动单元 钕磁铁
频率响应 20 ~ 25,000Hz
最大功率 300 mW
灵敏度 96 dB/mW
阻抗 32 ohms
重量 250 g
连接端 Ø6.3 / Ø3.5 mm 镀金立体声插头
导线 OFC/ 3.0 m (单侧导线)
再来张图,外型还凑合,能虎虎不识货的(包括我在内)~

Tags: 耳机
转至:http://www.cnblogs.com/hpunix/articles/355758.html
整个过程为:
创建文件系统–》下载文件–》加载模块–》查找符号地址并执行
以下为演示该过程的一个简易实现文件,有使用或者引用的话,也
打个招呼,或者给评论一下:
==============START OF THE FILE=============
/*********************************************************
* 版权所有 NP系统工作室。
*
* 文件名称: \FilseSystemCreate.c
* 文件标识:
* 内容摘要: 创建文件系统,在系统启动之后保存文件
* 其它说明:
* 当前版本: S 1.0
* 作 者: william
* 完成日期: 2005-10-12 17:04
* 当前责任人-1:william
*
* 修改记录1:
* 修改日期:2006-3-21 12:00
* 版 本 号:S 1.0
* 修 改 人:william
* 修改内容:创建
* 修改记录2:
**********************************************************/
#include <taskLib.h>
#include <vxWorks.h>
#include <stdio.h>
#include <string.h>
#include <ioLib.h >
#include <symLib.h >
#include <loadlib.h >
#include “dosFsLib.h”
#include “ramDrv.h”
#include “usrLib.h”
typedef int STATUS;
#define ERROR -1
#define OK 0
#define DIAG_RAM_DISK_SIZE (0×100000*10) /* 64M */
#define DIAG_RAM_DISK_NAME “/selfdev/”
STATUS FileSystem_Init()
{
BLK_DEV *pBlkDev;
char *pFileSysRamDiskBase = NULL;
ramDrv();
pFileSysRamDiskBase = malloc(DIAG_RAM_DISK_SIZE);
if(NULL == pFileSysRamDiskBase)
return ERROR;
bzero(pFileSysRamDiskBase,DIAG_RAM_DISK_SIZE);
pBlkDev = ramDevCreate( pFileSysRamDiskBase,
/* start address */
512,
/* sector size */
64,
/* sectors per track */
(int)(DIAG_RAM_DISK_SIZE/512),
/* total sectors 64 MBytes */
0);
/* offset */
if(NULL == pBlkDev)
{
free(pFileSysRamDiskBase);
return ERROR;
}
if(NULL == dosFsMkfs (DIAG_RAM_DISK_NAME, pBlkDev))
{
free(pFileSysRamDiskBase);
return ERROR;
}
return OK;
}
extern SYMTAB_ID sysSymTbl ;
void runModule()
{
STATUS status=ERROR;
int fd;
MODULE_ID hModule ;
FUNCPTR taskEntry = NULL ;
SYM_TYPE * pType ;
if ((fd = open(”/selfdev/youown.o”, O_RDONLY, 0)) < 0)
{
printf(”\nCannot open memory device.\n”);
goto done;
}
if ((hModule=loadModule(fd,LOAD_ALL_SYMBOLS))==NULL)
{
printf(”loadModule error = 0x%x.\n”,errno) ;
goto done;
}
status = symFindByName(sysSymTbl,
“willian_test”,
(char **)&taskEntry,pType ) ;
if (status==ERROR)
{
printf(”symFindByName error=%d\\n”, errno) ;
goto done;
}
else
{
printf(”taskEntryr=0x%x, type=%d\n.”,
(int)taskEntry,(int)*pType);
status = taskSpawn(”test1″,
100,
0,
30000,
taskEntry,
0,0,0,0,0,0,0,0,0,0) ;
if (status==ERROR)
{
printf(”taskSpawn error=%d\n”,errno) ;
goto done;
}
}
done:
if (fd >= 0)
close(fd);
}
int downLoadModules(
char *hostName,
char *srcfileName,
char *destfileName,
char *usr,
char *passwd)
{
………
/*实现下载远端PC模块到本地机*/
return (filesize);
}
STATUS test_dynamic_download()
{
STATUE status = OK;
int fileLenth = 0;
status = FileSystem_Init();
if(status == ERROR)
{
printf(”\nerror occured during init file system\n”);
return ERROK;
}
fileLenth = downLoadModules(”winner2″,
“youown.o”,
“/selfdev/youown.o”,
“target”,
“target”);
if(ERROR == fileLenth)
{
printf(”\nSome error occured when download files\n”);
return ERROK;
}
else
{
if(0 == fileLenth)
{
return OK;
}
}
runModule();
}
=============END OF THE FILE==========
几个需要注意的问题:
1:在文件系统初始化中使用的是MALLOC,该部分是使用的实际是
BSP的空间在实际申请的过程中一定要根据自己可能拷贝的文
件大小和实际的BSP的空间
2:在创建文件系统之后,可以查看该目录的文件列表,或者执行
COPY,前提是必须在TORNADO中选择初始化文件系统组件;
3:在下载文件的过程中,有的BSP初始化已经可以直接通过COPY
在远端FTP SERVER直接拷贝文件,比如:
copy “youown.o”,”/selfdev/youown.o”
这种情况没有必要再调用downLoadModules,至于如何使能,
大家关注的话可以在后边对其专门论述。
4:加载模块的过程可以调用如下代码:
if ((hModule=loadModule(fd,LOAD_ALL_SYMBOLS))==NULL)
同样也可以如下实现:
ld(1,0,”/selfdev/youown.o”);
至于各个参数什么意思,参考HELP就可以找到结论了!
5:在 status = symFindByName(sysSymTbl,
“willian_test”,
(char **)&taskEntry,pType ) ;
中,有可能实际返回错误,但是通过lkup 是可以实际找到的,
对于这个问题,我看网上答案比较少,原因是这样的:
假如你在主机运行shell,默认的远端目标机是没有符号表
(symbol table)的,只有在主机的shell上,你运行LKUP的
时候,才能找到这个符号,不信你可以这样尝试一下,打开两
个SHELL,一个通过串口,一个通过TORNADO的网口,你在其中
一个中能够使用LKUP找到,而在另外一个则不能找到,是不是
呀??
接下来继续解释,我们的symFindByName是在目标机器上
执行的,所以自然的就返回错误了,也是没有找到了!
解决方法是这样的:在目标机中包含符号表,同时将其和
主机保持同步。
Wind River told us to include all this defines in the
config files for the target:
#define INCLUDE_SYM_TBL_SYNC
#define INCLUDE_LOADER
/* object module loading */
#define INCLUDE_NET_SYM_TBL
/* load the symbol table by whatever means */
#define INCLUDE_SHELL
/* target-based shell */
#define INCLUDE_SHOW_ROUTINES
/* optional target-based system utilities */
#define INCLUDE_STAT_SYM_TBL
/* optional target error status routines */
#define INCLUDE_SYM_TBL
/* symbol table package */
#define INCLUDE_UNLOADER
/* optional object module unloading */
#define INCLUDE_WDB
转至:http://blog.csdn.net/pengzhenwanli/archive/2008/04/23/2319412.aspx#875603
之前有一篇文章是关于嵌入式单地址空间实现动态加载的想法,里面描述的是我根据相关资料进行猜测的地方,以及从技术上来说,可能需要的技术,最近难得有空闲时间,我实现了一下动态加载的。目前已经成功实现,下面说一下实现的过程。
先说一下实现此技术需要的平台:
CPU:ARM7+cache
Baseband:VT3406
ADS1.2
说一下这些东西的来历,Nucleus是实时嵌入式单地址空间操作系统,CPU是介于ARM7与ARM9之间的CPU,BB芯片是VIA出的,这些东西目前都已经收掉不再使用,我也正好离职,从而有时间去实现一下动态加载的问题。
从理论上来说,动态加载很简单,只需要把当前的PC指针指向下一句执行的语句即可。也就是使用如下的ASM就可以实现:
MOV PC, Address
这样就可以顺利执行程序,在我实现的时候,考虑如下问题,程序执行如何返回,参数如何传递,程序执行完毕返回到哪里。
这些问题的解决,看起来比较复杂,其实很简单,程序的返回是放在LR中,这样在上一个函数调用的时候,只要LR的值不变,这样可以在下一个函数调用的时候,同样使用LR,这样就可以顺利返回。关于参数传递,由于ARM中使用r0-r3传递参数,这样只要不更改r0-r3,就可以顺利传递参数。这个地方想明白,我用了好久,特别是返回地址的问题,程序如何执行,应该返回哪里。解决方法如下
实现DynamicLoader(UINT8 *pAddress)
MOV PC, R0
这个地方一定要用ASM实现,否则无法完成需要的功能。由于在调用此函数时,已经把函数的返回地址放到LR中,具体ASM如下:
MOV r0,address
BL DynamicLoader
由于DynamicLoaer的实现问题,没有实际的返回,也就是不需要使用
BX lr
这样来做为函数的返回。这是由于R0所指向的一个函数的开始地址,从DynamicLoader开始,其实执行的是另一个函数,这里的DynamicLoader只是起到了一个跳转的作用。但是又必须使用函数调用的方式来进行,而不能直接跳转,否则函数没有办法返回。由于BL的时候填充了LR,这样在下一个由于实际调用不是使用的B指令,因为并没有设置LR,这样仍旧是在调用DynamicLoader时的LR,因此可以正确返回。
函数可以正确调用并返回,这是程序很大的一个进步,这样就可以实际构造可以运行的程序了。
下面说一下ADS编译为二进制可执行文件的问题,使用编译器如果一开始把所有的数据都放好,这样包括全局变量和静态变量,以及函数的执行地址等,都已在LINK的时候根据指定规则确定实际的运行地址,也就是说所有的函数的实际运行地址在LINK的时候已经确定。这样对于动态运行来说是不可行的,因为既然要动态加载,就需要所有地址都是静态的,因为每次对于读入内存的数据,起始地址是不缺定的,因此不能再LINK时把所有的地址固定死。
解决这个问题有两种方式,一种是在scatter loader中把程序的可执行地址固定好,在LINK时不LINK实际的数据,而在系统启动的时候,把这部分可执行文件拷贝的固定的地址,这样可以作为一个整体运行。但是这种方式由问题,就是应用的大小什么的都是固定死的,不能太灵活,不能根据应用实际调整。
这里使用另外一种方式,选择程序不在LINK的时候把所有的地址固定死,而是使用相对独立的函数调用方式。如下:
原来的方式可能使这这样
BL 0×10008;
而使用相对的地址程序如下:
ADD r5,pc,#18
BL r5
这样虽然多了一句,但是可以做到函数的运行地址是动态指定的,而不是编译为固定的地址。
其实ADS提供了把函数编译为独立地址的方式,
COMPILER使用如下的参数/ropi/rwpi
LINK使用如下的参数-rwpi –ropi
就可以把编译的程序做到运行时地址是独立的。
从上面来开,编译时地址的问题,还有运行时加载的问题,都已经顺利解决。但是这里还有一个问题,就是如何确保动态应用如何每次在使用的时候,都从固定的入口进入的问题。也就是说,虽然有了内存中的运行地址,但是如何保证每次都从固定的函数开始执行呢?如果每次都从编译的可执行文件0地址开始执行,没有办法保证每次调用的是同一个函数。
这个可以通过LINK来保证每次是同一个函数在0地址,使用如下的参数
-first DyanmicAppEntry
这样就可以保证DyanmicAppEntry的入口地址为可执行文件的开始了。
上面的文章解决了动态编译和加载的问题,下面说一下动态应用的问题。如果要使一个应用有价值,比然需要提供本地的功能调用,而且对于手机来说,系统已经基本上实现了大多数的功能,如果在动态应用中再重复实现一些功能,可以说既浪费了空间,又浪费了时间。而且对于硬件相关的功能,必须通过本地调用来进行,这样就需要如何把本地调用传入动态应用中。如上文所说,本地的调用地址都是在LINK的时候确定的没有办法直接在动态应用中使用,这样需要在运行时把本地调用传入动态应用,由于动态应用的入口还有好几个参数可以使用,这样就可以构造一张系统调用的表,在运行的时候传入动态应用,这样可以通过表来调用系统功能,这样就解决了系统本地调用的问题。
这里要特别说一下安全的问题,由于动态应用是直接更改PC指针运行的,这样,如果应用出错,系统可能就CRASH了,无法再继续运行,而且由于可以调用本地系统调用,可能做许多意想不到的功能,这样就可以在系统调用的时候
增加一个中间层,一些核心功能,必须满足一定的权限在可以调用。