BREW中使用静态变量

之前有提到内存管理器,并未提到如何使用;长期以来,由于BREW执行文件格式的限制,无法使用静态变量和全局变量,关于如何打破这一限制,可能也有提到过。
首先来说静态变量的必要性,如果可以使用的话,我们就可以不用为程序中的某个变量该放在内存中的哪个位置而发愁了,我们可以集中力量解决某个问题,而不用考虑变量可不可用的问题。
理想总是完美的,目前所谓的解决办法是两个,一个是完美的,一个是不完美的。
先说说完美的解决办法,就是换编译器,将平时使用的ADS编译器,换成gcc编译器,通过编译脚本的控制,可以使用c++中的try..catch机制,静态变量,浮点运算等等,几乎没有限制,可以说是完美的,这种方法的一个缺点就是编译出来的容量太大,在我这边不管我如何的修改编译参数,我都无法使它保持和ADS差不多类似的容量。项目到最后需要减小容量的时候,最无奈的就是减小执行文件的容量。

再说不完美的解决办法,我们还是使用ADS的编译器,这样可以使可执行文件保持小的容量,无法使用try…catch机制,浮点运算等等,如果你不在意这些的话,解决的方法是使用const变量来解决,也就是所谓的常量,这样就有两个问题,常量是无法修改的(语法的定义)和常量是否有地址。关于后一个问题,常量在内存中有地址,并且,BREW中常量的地址和执行文件执行代码的地址是在一个段中。前一个问题,常量是无法修改的,这个状况要分两种,在模拟器上,常量被保存在一个特殊段中,这个段不可以被更改,而在真机中,常量和代码段在一起,虽然编译的时候,这个段指明不可被改写,但在运行时,并没有该限制。不过,模拟器中并没有不可使用静态变量等限制,因此,我们可以使用下面的宏来控制:

       #ifdef _WINS_
       #define _CONST_
       #else
       #define _CONST_ const
       #endif
       

使用的方法,类似下面的用法:

       static _CONST_ int int_var[] = {0};
       void fun()
       {
       int* p_int_var = (int*)int_var;
       *p_int_var = 5;
       int p2 = int_var[0]; //这是不安全的
       }
       

上面的写法不是漂亮的,但可以解决实际的问题。就像一把双刃剑,你必须时刻知道你在强制的改写常量,而且,这种方法可能会带来一些麻烦,比如上面的p2,你可能希望它的值是5,但不幸的是,你得到的可能是0,因为编译器帮你优化了(对于上面的写法,我目前没有发现这一问题,其他写法则发现存在,比如_CONST_ int* int_var = 0)。
最普通和繁琐的方法是将变量保存到一个结构中,而这个结构我们可以在任意时刻获得它的内存地址,通过强制转换或者其他手段,再获得变量,于是,我们瞄准了AEEApplet a;在注释中,它必须在程序入口结构的第一个位置(原因以后会说到),也就是说,它的地址就是程序入口结构的地址。我们在程序中可以在任何地方调用GETAPPINSTANCE()来获得它的地址,如果我们将变量放到它的后面,也可以获得改变量的地址,于是就有了下面的写法:

       class test_Instance
{
public:
        AEEApplet a; //!< First element of this structure must be AEEApplet
        int int_var;
};
void fun()
{
int* p_int_var = &(((test_Instance*)GETAPPINSTANCE())->int_var);
*p_int_var = 5;
int p2 = ((test_Instance*)GETAPPINSTANCE())->int_var; //这没有任何问题
}
       

上面的写法是安全的,不管使用的是模拟器还是真机,都是一样的。
对于ADS的两种解决办法,我没有一个统一的定论,我在项目中,两种方法都使用到,如何方便安全如何使用吧。从根本上讲,编译器支持才是王道。

7条评论

    1. 最新的ADS工具(我这边没有)可能已经包含了这一部分,这些年很少去高通的网站了,不太清楚最新的变动。改天去看看。记忆中elf2mod好像是针对gcc编译器的。我们这边要求又多,改天试试再更新这篇文章。谢谢提醒!

    2. 这些工具确实可以使用。不过,高通把这些工具只对相关开发者提供。尤其是哪个floats.o,够无耻,其实就是实现几个浮点相关的函数。明天更新文章。

    1. 我不是什么高手;不是非常明白意思,在我看来,正常程序在获得全局数据段的起始地址和大小没有什么意义。我没有使用过BREW系统的线程函数,所以不知道。如果你使用的是我提供的线程函数的话,Thread::_pStack就是栈基指。对于全局数据段的运行起始地址的问题,我个人建议研究下elf文件

      1. :)谢谢高手的回复,我现在在移植一个gc到brew,所以需要用到这些信息,现在有点进展了,通过编译器链接时的特殊符号(也就是elf里的)我已经可以得到全局数据段的信息了,主栈基地址使用一个dummy变量算是也拿到了,只是不太准确,谢谢你的回复,希望看到你更多brew方面的文章

回复 admin 取消回复

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据