Preface
There are currently 3 different compilers available for BREW: ARM RealView Compilation Tools for BREW ( RVCTB ), ARM Developer Suite ( ADS ) and GNU Compiler Collection ( GCC ).
The following development procedure is suited to RVCTB. The same procedures may be applied to both ADS and GCC.
ARM RealView Compilation Tools for BREW
Restrictions on RVCTB 1.2
RVCTB 1.2 does not support many C++ standards such as exception handling, RTTI ( RunTime Type Information ) and name space.
Alternative Solutions for Exception Handling
Since exception handling cannot be used with RVCTB, if an error occurs within a class-defined operator, and the information associated with that error is saved, it can be handled outside the class.
The "new" Operator
In standard C++, if the "new" operator fails to allocate memory, the exception handling function does not allow the code below the new operator to execute. (See list 1)
* List 1 : Safe coding using exception try { _buffer = new char[128]; // If memory allocation fails // code after this line // will not be executed. _buffer[0] = 0; // OK } catch (...) { // NG }
If exception handling can not be used ( See list 2 ), the new operator can not throw exceptions.
When the operation fails, the new operator can only return NULL and the block of code below it will still be executed.
* List 2 : Hazard prone coding _buffer = new char[128]; // Even if memory allocation fails // execution will still continue _buffer[0] = 0;
If exception handling can not be used, It is necessary to implement safety procedures according to the return value of the new operator. (See list 3)
* List 3 : Coding with error handling _buffer = new char[128]; if (_buffer != NULL) { // Normal process } else { // Error handling }
Generally, the new operator allocates memory and boots up the constructor. In standard C++, when new operator's allocation fails, the generated code does not boot up a constructor. The return value of new operator is assigned to NULL.
Overloaded Operators
The lack of exception handling will be very inconvenient when dealing with operators like the assignment operator in the string class, whose return value cannot be compared to NULL.
As shown in list 4, the return value of an assignment operator refers to itself, thus it cannot return error values.
* List 4 : General assignment operator my_string& my_string::operator=(my_string const& param) { // Return value is itself. // Does not return error value. return *this; }
Therefore, the error value must be saved within the class and should be handled outside.
( See list 5 )
* List 5 : Saving the error value my_string& my_string::operator=(my_string const& param) { if (...) { // When an error occurs, save it in _my_exception _my_exception = true; } return *this; } int main(void) { my_string data; my_string temp; temp = data; // Check for error if (temp._my_exception) { // Handle error } return 0; }
* Porting something like a huge collection class onto BREW, will be very labor intensive since exception handling must be manually injected throughout the class.
Alternative Solution for RTTI
In RVCTB, the "dynamic cast" operator can not be used to down cast a variable of an abstract parent class into one of a concrete child class. In addition, the "typeid" operator for accessing runtime type information is not available either. ( list 6 - 7 )
* List 6 : Coding with RTTI void function(base* p) { if (dynamic_cast<derived_a*>(p) != NULL) { // Variable p is a pointer for derived_a class } else if (dynamic_cast<derived_b*>(p) != NULL) { // Variable p is a pointer for derived_b class } return; }
* List 7 : Coding without RTTI void function(base* p) { if (dynamic_cast<derived_a*>(p) != NULL) { // This condition is always true } else if (dynamic_cast<derived_b*>(p) != NULL) { // This condition is always false } return; }
The dynamic_cast Operator
Code relying on type information using the dynamic_cast operator should be avoided as much as possible. ( See list 8 )
* List 8 : Inferior design class base { }; class derived_a : public base { public: void do_a(void); }; class derived_b : public base { public: void do_b(void); }; void function(base* p) { if (dynamic_cast<derived_a*>(p) != NULL) { dynamic_cast<derived_a*>(p)->do_a(); } else if (dynamic_cast<derived_b*>(p) != NULL) { dynamic_cast<derived_b*>(p)->do_b(); } return; }
It would be better to use virtual functions, rather than conditional branches relying on type information. ( See list 9 )
* List 9 : Designing with virtual function class base { public: virtual void do(void) = NULL; }; class derived_a : public base { public: virtual void do(void); }; class derived_b : public base { public: virtual void do(void); }; void function(base* p) { p->do(); return; }
If dynamic casting at runtime is not required, use templates. (See list 10)
* List 10 : Designing with templates class independent_a { public: void do(void); }; class independent_b { public: void do(void); }; template <typename T> void function(T* p) { p->do(); return; }
The typeid Operator
The "typeid" operator in RCVTB is only supported for use with variables of primitive types and non-abstract ( concrete ) classes. (See list 11)
* List 11 : Code not supported by RVCTB class base { public: virtual ~base(void) {} }; class derived : public base { }; int main(void) { base* p = new derived; printf("%s\n", typeid(*p).name()); delete p; return 0; }
To get the type information of an abstract class, use either a variable in the base class that retains type information, or a virtual function that returns it. ( List 12, 13 )
* List 12 :Code with variable in base class class base { private: int _typeid; protected: base(int id); public: int get_typeid(void) const; }; class derived_a : public base { public: derived_a(void); }; class derived_b : public base { public: derived_b(void); }; base::base(int id) : _typeid(id) { return; } int base::get_typeid(void) const { return _typeid; } derived_a::derived_a(void) : base(0x0000000A) { return; } derived_b::derived_b(void) : base(0x0000000B) { return; }
* List 13 : Code with virtual function class base { public: virtual int get_typeid(void) const = NULL; }; class derived_a : public base { public: virtual int get_typeid(void) const; }; class derived_b : public base { public: virtual int get_typeid(void) const; }; int derived_a::get_typeid(void) const { return 0x0000000A; } int derived_b::get_typeid(void) const { return 0x0000000B; }
Other
RVCTB does not support complex templates and name spaces.
Restrictions on BREW
The biggest restrictions on BREW are that global and static variables cannot be used.
Reasons
The preconditions of a BREW application are the following,
- BREW application functions as a plug-in
- Multiple BREW applications share a single memory space
- The execution code of a BREW application is allocated in Read-Only Memory ( ROM )
A static variable is allocated to an area of read/write memory. This area may not overlap with the code area, thus a relative address from the code area cannot be used to access the static variable.
The following diagram represents BREW memory allocation:
Notes:
A BREW application is dynamically loaded onto memory, absolute addresses cannot be used since this address varies each time.
Moreover, a BREW application cannot use the SB register, because it is already occupied by the BREW environment.
The RVCTB compiler has an option to change the SB register. ( See list 14 )
* List 14 : How to use __global_reg struct my_global { int i; }; __global_reg(8) my_global* sb; int get(void) { return sb->i; } int main(void) { sb->i = 0; return get(); }
This method cannot be used to access a static variable via a register chosen by the programmer, since some callback function may require the register in question.
* Sophia Cradle offers a special tool for using static variable in BREW. See svHacker.
Available Static Variables
The following are static variables:
- A global variable
- A static variable within a class
- A static variables within a function
Generally, a BREW application cannot use these static variables.
* List 15: Static variables that cannot be used class my_counter { public: static int _counter; }; int my_counter::_counter = 0; // NO int global_variable = 0; // NO int main(void) { static int local_variable = 0; // NO return 0; }
However, global or static variables can be used if they are constants of primitive types. Since they will not be updated at run time, they may be allocated in the code area.
* List 16: Static variables that can be used class my_counter { public: static int const _counter; }; int const my_counter::_counter = 0; // OK int const global_variable = 0; // OK int main(void) { static int const local_variable = 0; // OK return 0; }
Static Variable Repercussions
Static Variables are required for certain functions in the ARM compiler, as well as the ANSI C standard library. Without static variables, these functions and libraries are not open to BREW applications. (See list 17 )
* List 17: Functions that can not be used int main(void) { void* buffer; buffer = malloc(128); // NG free(buffer); // NG return 0; }
The function in list 18 adds two floating-point numbers. This function will be transformed into the _fadd function when compiled in FPU mode.
* List 18: Functions included in compiler int main(void) { double a; double b; double c; a = 1.0; b = 2.0; c = a + b; // Transformed into _fadd return 0; }
A BREW application cannot use floating point operations because they will be transformed into compiler functions.
Instead, BREW has helper functions emulating ANSI C standard library and functions for floating point operations.
Here are some examples.
Function Name | Definition |
---|---|
MALLOC | Allocates memory |
REALLOC | Re-allocates memory |
FREE | Frees memory |
STRSTR | Searches for string |
F_ADD | Adds floating points |
F_SUB | Subtracts floating points |
These helper functions are generally used when developing BREW applications. ( See list 19 - 20 )
* List 19: Floating point operation available int main(void) { double d; float f; int i; d = 1.0; f = 1.5; d += f; i = 2; d += i; return 0; }
* List 20 : Floating point operation not available int main(void) { double d; float f; // Error int i; d = 1.0; f = 1.5; // Error d += f; // Error i = 2; d = F_ADD(d, FASSIGN_INT(i)); //Helper function used return 0; }
BREW cannot use variables of type "float".
Using Floating Point Operations in BREW Application
ANSI C standard libraries and ARM compiler functions can be customized to the developer's environment using a method called "re-target".
If the RVCTB library is re-targeted for BREW environment, then ANSI standard functions and floating point operations can be used.
Re-target floating point operation library
If compiled with RVCTB in software FPU mode, all the floating-point operations of a BREW application will be transformed into compiler functions included in rt_fp.h. ( See list 21 )
* List 21 : A part of floating point operation function <rt_fp.h> /* * Single-precision arithmetic routines. */ extern __softfp float _fadd(float, float); extern __softfp float _fsub(float, float); extern __softfp float _fmul(float, float); extern __softfp float _fdiv(float, float);
* Some ARM compiler functions contain static variables.
ARM's Floating Point Operations ( FPO ) library expects an initializing function, generated automatically by the compiler, to be executed on startup prior to main function. ( See list 22 )
* List 22 : Initializing function <rt_fp.h> /* * Call this before using any fplib routines, if you're trying to * use fplib on the bare metal. */ extern void _fp_init(void);
Re-target for BREW environment
To re-target the floating point operations library, relocate debug codes or functions with static variables, then move the initializing functions to a proper place. ( See list 23 )
The floating-point operations library can be re-targeted for the BREW environment using C for the start-up code. In case of C++, the extern "C" keyword must be used.
* List 23 : Functions to be re-targeted <rt_misc.h> /* * Redefine this to replace the library's entire signal handling * mechanism in the most efficient possible way. The default * implementation of this is what calls __raise (above). */ void __rt_raise(int /*sig*/, int /*type*/); <errno.h> extern __pure volatile int *__rt_errno_addr(void); <rt_fp.h> /* * This returns a pointer to the FP status word, when it's stored * in memory. */ extern unsigned *__rt_fp_status_addr(void);
Due to the lack of main function in a BREW application, the initializing function for floating point operation library, _fp_init, should be called manually from AEEClsCreateInstance function.
Three functions to be re-targeted must be replaced by the modified implementation.
Two of the functions must return the address of the variable pool they use. These variable pools can be customized freely by the developer. In BREW applications, it is suggested to reserve variable pools somewhere in the AEEApplet structure.
The __rt_raise function, invoked when exceptions occur within the library, may be replaced by a dummy function when applied to BREW applications.
List 24 shows the start up code for a BREW application with re-targeted part for floating point operations library included.
* List 24 : Start up code for using floating point operation #if defined __arm #include <rt_misc.h> #include <errno.h> #include <rt_fp.h> #endif // Insure that SWI instruction will not be linked #if defined __arm #pragma import(__use_no_semihosting_swi) #endif typedef struct SoftFPApplet { AEEApplet aee; // Variable pool for retargetting #if defined __arm int volatile _errno; unsigned int _fpstatus; #endif } SoftFPApplet; #if defined __arm void __rt_raise(int signal, int type) { return; } int volatile* __rt_errno_addr(void) { SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE(); return &ap->_errno; } unsigned int* __rt_fp_status_addr(void) { SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE(); return &ap->_fpstatus; } #endif int AEEClsCreateInstance(AEECLSID ClsId, IShell* pIShell, IModule* po, void** ppObj) { *ppObj = NULL; if (ClsId == AEECLSID_SOFTFP) { if (AEEApplet_New(sizeof(SoftFPApplet), ClsId, pIShell, po, (IApplet**)ppObj, (AEEHANDLER)SoftFP_HandleEvent, NULL) == TRUE) { // Initialize floating point operation library #if defined __arm _fp_init(); #endif // Add code for application return AEE_SUCCESS; } } return EFAILED; }
BREW applications using the start-up code in list 24, may use floating-point operations anywhere without errors. List 25 presents the same floating point add operation, after re-targeting.
* List 25: After re-targeting void function(void) { double d; float f; // OK! int i; d = 1.0; f = 1.5; // OK! d += f; // OK! i = 2; d += i; // OK! return; }
Although most ANSI C standard library functions can be used in BREW applications, they exist inside the application, and will increase application size. BREW helper functions exist apart of the application and are called dynamically; they will not increase the application size.