×
Namespaces

Variants
Actions

Symbian Foundation Knowledgebase Q&As

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Article
Created: hamishwillee (25 Aug 2011)
Last edited: hamishwillee (26 Jul 2012)

Note.pngNote: This article contains "knowledgebase" entries migrated from the Symbian Foundation knowledgebase (and formerly from the Symbian Ltd. Knowledgebase). It has been migrated primarily to ensure that numerous links within the other migrated documents remain satisfied.

Contents

Platform Security

How do I debug Platform Security API policy violations?

A Platform Security API policy violation occurs when a process calls an API for which it does not have a required capability. In most cases the API will return (or Leave) with the error code KErrPermissionDenied (-46). The error may propagate up to the UI in a standard error dialog box (UI dependent).


Memory Management & Cleanup

Can I TRAP an 'LC' method?

No. A 'LC' method/function that pushes objects on the CleanupStack before returning cannot be trapped using TRAP or TRAPD - unlike a 'L' method that can simply leave. As an example the code snippet below calls a NewLC() method in the intialization method. The line with the trap will cause a E32USER-CBASE 71 panic if the NewLC method returns normally i.e. it does not leave.

void CMyClass::Init() {
TInt err = KErrNone;
TAny* pointerToSomething = NULL;
 
TRAP(err, pointerToSomething = MyObject::NewLC()); // This will cause a panic !
 
if (err != KErrNone)
TRAP(err, SomethingElseL()); // No panics here !
else
{
err = pointerToSomething->SomeMethod();
CleanupStack::PopAndDestroy(pointerToSomething );
}
}

The panic happens due to the way trap handling works with the CleanupStack framework. Every time TRAP(D) is invoked a trap frame (TTrap) is added to mark a point from where execution should continue when an exception is thrown (User::Leave). The frame not only marks the point of return but the context from which the exception should recover.

If an exception is raised, all objects on the CleanupStack in the current level are destroyed before the context is reinstated. However, if a leave did not transpire and the 'LC' method returns normally, the exception handler is 'untrapped' to unmark (disassociate) the current cleanup level and the TRAP frame. The CleanupStack mandates that at this point all objects should have been popped; thus, the above panic is raised if any objects are found on the current level before unmarking of the level is done. This is normal behaviour as it is expected that upon normal return all objects should have been accounted for (i.e. either popped and/or destroyed) in the called method.

Note that the same principle applies to a leaving ('L') method. The above panic is also raised if an object is left on the cleanup stack erroneously. In this case the unmatched push would be difficult to trace as the panic might appear sometime after the offending method returns.

Is it possible to reference an object on the stack during a leave ?

Due to the TRAPing mechanism it is possible to perform operations on objects on the stack, for example using CleanupClosePushL() in the process of an exception being handled. This works because the objects on the CleanupStack are handled first before the context is reconfigured. Once the context is changed the stack frame does not reflect the current execution state; hence, all objects on the stack are lost (cleared).


Writeable Static Data (WSD)

Does Global writeable static data in DLLs work when projects are compiled with GCC-E v3.4.3?

Not properly. There is a defect in GCCE version 3.4.3 (release) (CodeSourcery ARM Q1C 2005) that if any global object's destructor code cannot be optimised away, the loading executable will panic when the DLL is loaded. This defect has been fixed in later versions of GCC-E but at time of writing these versions have not been validated for use with the Symbian platform.

Developers have the following options:

  • Use the ARM RVCT 2.2 Compiler
  • Build libraries as static LIBs rather than as DLLs and incorporate directly into your EXE. This may be acceptable if the library code is not widely shared.
  • Do not include static data in your projects
  • It may be possible to use later versions of the compiler than Q1C 2005. Note that at time of writing other versions have not been validated.

Further Information


How can I find the "uninitialised data" in my DLL?

The post-linker (elf2e32) will warn you if your DLL contains uninitialised data when you build for the device:

elf2e32 : Error: E1028: ELF File \...\DllWithStaticData.dll contains uninitialized writable data.

The Symbian platform allows global writeable static data (WSD) for DLLS that have EPOCALLOWDLLDATA in their MMP file. As there are costs associated with the use of WSD, this error is raised to catch the case of accidental usage. Note that the compilers used for Emulator builds tend to create 'accidental' WSD even when it is not explicitly required by the DLL code (more info here): therefore this error is not raised on Emulator builds.

Consider this section of C++ code, added to a file QSORT.CPP which is part of ESTLIB.DLL.

// variables
 
struct div_t uninitialised1; // in .BSS
static struct div_t uninitialised2; // in .BSS
struct div_t initialised1 = {1,1}; // in .DATA
static struct div_t initialised2 = {2,2}; // in .DATA
 
// constants
 
const struct div_t const1 = {3,3};
const static struct div_t const2 = {4,4};
 
const TPoint none(-1,-1);
 
static const TText* plpPduName[12] =
{
_S("Invalid"), _S("DataFlowOff"), _S("DataFlowOn"),
_S("ConnectRequest"), _S("ConnectResponse"), _S("ChannelClose"),
_S("Info"), _S("ChannelDisconnect"), _S("End"), _S("Delta"),
 
_S("EndOfWrite"), _S("PartialWrite")
};

The associated .MAP file contains information which helps to track down the source file involved. Look in epoc32\release\gcce\udeb\dllname.dll.map in sections ".data" or ".bss"

In this example, we find:

.data 0x10017000 0x200
0x10017000 __data_start__=.
*(.data)
.data 0x10017000 0x40 ..\..\EPOC32\BUILD\STDLIB\BMMP\ESTLIB\ARM4\UREL\ESTLIB.in(QSORT.o)
0x10017000 initialised1
*(.data2)
*(SORT(.data$*))
0x10017040 __data_end__=.
*(.data_cygwin_nocopy)
 
.bss 0x10018000 0x18
0x10018000 __bss_start__=.
*(.bss)
.bss 0x10018000 0x18 ..\..\EPOC32\BUILD\STDLIB\BMMP\ESTLIB\ARM4\UREL\ESTLIB.in(QSORT.o)
0x10018008 uninitialised1
*(COMMON)
0x10018018 __bss_end__=.

So the DLL has 0x18 bytes of uninitialised data (.BSS) and 0x40 bytes of initialised data (.DATA), all of which comes from QSORT.o The variables "initialised1" and "uninitialised1" both have global scope, so the MAP file lists them by name. The static variables don't get named in the .MAP file.

Removing the first four lines of code shown above leaves just variables which are declared as const, but only reduces the .BSS to 0x08 bytes and the .DATA to 0x30 bytes. There are two problems remaining:

Declaring C++ objects as const doesn't help if they have a constructor (see [#Don't be misled by const static data that isn't really const]). The 8 bytes of uninitialised data are allocated to hold the TPoint object, but it won't become const until after the constructor has been run.

The declaration const TText* says that the TText values may not be altered, but it doesn't make the pointer a constant as well. The 48 bytes of initialised data are the 12 pointers in the plpPduName array. To make the pointers constant as well as the values they point to, the declaration needs and additional const after the TText*.

static const TText* const plpPduName[12] =
{
_S("Invalid"), _S("DataFlowOff"), _S("DataFlowOn"),
_S("ConnectRequest"), _S("ConnectResponse"), _S("ChannelClose"),
_S("Info"), _S("ChannelDisconnect"), _S("End"), _S("Delta"),
 
_S("EndOfWrite"), _S("PartialWrite")
};

Removing the TPoint global variable and adding the extra const to the plpPduName array finally removes the last of the offending .BSS and .DATA.

Further Information

Don't be misled by const static data that isn't really const

Declarations with the following forms all fail to define const static data:

const TPoint KSomeConstPoint=...;
const TPtrC KSomeConstPtr=...
const TRgb KSomeConstCol=...
const TChar KSomeConstChar=...

Instead, because the class involved has a non-trivial constructor, the memory for the object cannot be anywhere truly const, e.g. in a code segment. Therefore this will be global writeable static data.

Further Information


Can I have writeable static data in my LDD, PDD, or kernel extension?

Yes. LDDs, PDDs and kernel extensions are allowed to have writable static data. Note that since there can only ever be one instance, and it is owned by the kernel, ROMBUILD will just add this data to the kernel's static data.

You should avoid using writable statics - and unless you are porting code you can probably always avoid it - but just in case you DO need it, here's how.

  1. Write code with the static data
  2. put the keyword EPOCALLOWDLLDATA in the MMP file
  3. Declare it in your OBY file using the "device" or "extension" keyword, e.g.
device[ABCD1234]=\epoc32\release\???\urel\thing.ldd system\libs\thing.ldd
extension[ABCD1234]=\epoc32\release\???\urel\my_kernel_extension.dll system\libs\extension.dll

Do not use "file=" - if you do then ROMBUILD will not fixup the static data and your kernel will probably panic when it tries to load the LDD/PDD/extension.


How can I use Thread Local Storage (TLS) to Maintain Static Data in a DLL?

Note.pngNote: Note: TLS is a legacy method for supporting global writeable static data. It may be used in cases where the tradeoffs of Symbian Platform Support for Writeable Static Data in DLLs are unacceptable.

What data structure should I use?

A single static variable can be used, but this can be difficult to extend should access to additional variables become necessary. A better approach is to encapsulate all the static data variables into a single Struct or class. Extra data variables can easily be added at a later time.

class TMyStaticData
{
public:
TInt iData;
TInt iMoreData;
};

How should I create and destroy my static data?

The DLL should have a function to initialise TLS, and another to destroy it. The initialisation data should be called just before using any of the DLL's functionality. This function will take care of creating the structure that contains all global data on heap and setting up the Dll's Tls. The code might look something like this:

TInt InitialiseTls()
{
if ((Dll::Tls()== 0) //Check that TLS is not already initialised.
User::Panic(/*some category and number*/));
TInt err=KErrNone;
TMyStaticData* pMyData;
pMyData=new TMyStaticData;
 
if (pMyData)
{
err = Dll::SetTls(pMyData);
if (err==KErrNone)
{
pMyData->iData = 100;
pMyData->iMoreData = 200;
}
else
{
delete pMyData;
pMyData=NULL;
}
}
else
{
err=KErrNoMemory;
}
return err;
}

Note that the code that created and launched this thread will presumably be waiting on a Rendezvous (or Logon prior to EKA2). On error you might simply call User::Exit(error) so the calling thread is signalled with the error code.

The function to free up the TLS resources should be called when the thread is no longer using the DLL. The function might look something like this:

TInt DestroyTlsLibrary()
{
TInt err=KErrNone;
//Get the Dll's Tls
TMyStaticData* pMyData;
pMyData = static_cast<TMyStaticData*>(Dll::Tls());
if ( pMyData )
{
//Do global variable specific cleanup activity if required (not in this case)
Cleanup( pMyData );
delete pMyData;
err=Dll::SetTls(NULL);
}
}

When should I initialise and destroy the TLS?

In EKA2, DLLs don't have 'entry' nor 'exit' points that correspond to loading/unloading (from a particular exe). There are several options for implementing the required behaviour:

  • If you are using ECOM, then you are giving your host code an object instance. It is a common pattern for that object to privately use TLS to store a shared singleton object, and for that singleton to be reference-counted. The destructor tidies up if it is the only user of the singleton.
  • If you are not using ECOM, but are using RLibrary::Load directly, then you have some code that loads the library and some code somewhere else that unloads it. Those could additionally call known function ordinals for initing and destroying the library's singleton.
  • If you are being statically linked in, then you can still use the 'instance allocates singleton on ConstructL, and it is reference counted' pattern. Or you could put your singleton into a CCoeStatic (if your host process has a CCoeEnv, as all 'apps' do).

How can I access my data?

Cast and cache the TAny pointer contained within Thread Local Storage and access the static variables in the normal way. It may be useful to create a utility function to make the process simpler and more maintainable:

inline TMyStaticData* GetStaticData()
{
return static_cast<TMyStaticData*>(Dll::Tls());
}
 
...
 
TMyStaticData* pData = GetStaticData();
if (pData)
PData->iData = 300;


Process local storage: How can I use TLS to implement writable global data which is shared between threads in the same process?

Note.pngNote: Note: TLS is a legacy method for supporting global writeable static data. It may be used in cases where the tradeoffs of Symbian Platform Support for Writeable Static Data in DLLs are unacceptable.

Thread Local Storage (TLS) allows you to associate a pointer to some global data G which will be returned each time Dll::Tls() is called. The pointer returned is stored on a per DLL, and per thread basis. If your Dll code runs in main thread A and calls SetTls(G), then future calls to Tls() will return a pointer to G. If another worker thread is spawned, and calls Tls() - it will return NULL since no data has been associated with this thread.

To make G a unique instance for all threads created by the DLL, a scheme such as the following can be used:

  1. Master thread creates a global object pointer G, and calls SetTls(G):
  2. Each time the master thread creates a worker thread, it passes G into the thread creation function.
  3. At the start of the worker thread function, the worker calls SetTls(G);

Each time the global data needs to be accessed from the DLL, Tls()->G->iThing is referenced. It may help to define a macro for convenience: GLOBAL->iThing.

Arrays

Why would using RArray cause an EExcAlignment exception on target hardware?

RArray::Append() may crash on hardware with an ARM alignment h/w exception because RArray only supports word aligned classes - meaning that it will not work for items smaller than 4 bytes. No compile time error is emitted when using the class with a TInt16 or char data types and so the problem manifest itself as a ARM alighment h/w exception at runtime.

Code such as the following may generate a EExcAlignment exception:

RArray<TInt16> array;
array.Append(0x1212);
array.Close();

Rational For performance reasons, RArray stores objects internally in word (4 byte) aligned blocks. This means a TInt16 array offers no performance benefits over a TInt array when running on a 32bit word machine. In fact, RArray<TInt> has a special implementation which makes it more efficient than array of other objects, and so it's best to use this where appropriate.

Having said that, the real issue is with RArray::Append() (and Insert()) because these methods assume that the object passed to them is word aligned and will cause an alignment exception if they aren't. This is documented behavior and the fact that RArray is used for performance critical kernel code means that RArray will not be changed to support smaller types - use a different data structure instead.

In summary: use RArray<TInt> for arrays of keys/numeric values (even if 8bit or 16bits would suffice), or use RPointerArrays or CArrays for arbitrary sized data structures.

Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 26 July 2012, at 08:03.
71 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×