Question:
PETRAN warns me that my DLL contains uninitialised data - this wasn't a problem on WINS. How do I track down this uninitialised data and why is it a problem on ARM4 but not WINS?
Answer:
The Symbian OS architecture does not allow DLLs to have a data segment (initialised or uninitialised). There are problems with deciding quite what such a data segment would mean (Is it shared by all users of the DLL? Should it be copied for each process which attaches to the DLL?) and significant runtime overheads in implementing any of the possible answers. EPOC32 does provides a mechanism whereby a DLL can manage private storage on a per-thread basis: see the description of the Dll class, in particular Dll::SetTls() and Dll::Tls().
A WINS build uses the underlying Windows DLL mechanisms that can provide per-process DLL data using copy-on-write semantics: this is why the problem goes undetected until the code is built for target hardware.
Consider this section of C++ code, added to a file QSORT.CPP which is part of ESTLIB.DLL.
- // variables
struct div_t uninitialised1; // in .DATA
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")
};
When this code is built, the messages from PETRAN look something like:
- PETRAN - PE file preprocessor V01.00 (Build 170)
WARNING: Dll 'ESTLIB[10003B0B].DLL' has initialised data.
WARNING: Dll 'ESTLIB[100002C3].DLL' has uninitialised data.
- Look in epoc32/release/arm4/urel/dllname.map
- Search for ".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 (and puts them both in the initialised data). 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. 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")
};
Further information on NewLC.com show you how to find out which particular variables in the source file are causing the problem.
Corresponding Instructions for ER3 and ER5
The GCC compiler used for ER3 and ER5 produces a different MAP file format.
- Look in /Epoc32/Release/Marm/Rel/dllname.MAP
- Search for "from *(.bss)", or "from *(.data)", both of which will be some way after "LINK EDITOR MEMORY MAP"
In the example, this finds:
- .bss 10009580 10 0 2**2 alloc
10009580 00000000 __bss_start__ =.
from *(.bss)
.bss 10009580 10 10 2**2 pe-arm-little [../../EPOC32/BUILD/STDLIB/MARMD/REL/ESTLIB.in]QSORT.o(overhead 4056 bytes)
from *(COMMON)
10009590 00000010 __bss_end__ =.
.data 10009590 40 0 2**2 load alloc contents
10009590 00000000 __data_start__ =.
from *(.data)
.data 10009590 40 40 2**2 pe-arm-little [../../EPOC32/BUILD/STDLIB/MARMD/REL/ESTLIB.in]QSORT.o(overhead 4056 bytes)
10009590 initialised1
100095a0 uninitialised1
from *(.data2)
100095d0 00000040 __data_end__ =.
from *(.data$)
The explanation and analysis are the same as for Symbian OS v6.0 onwards.