The "MyDll.DLL as (un)initialized data" Error from PETRAN

The Symbian OS architecture does not allow DLLs to have a data segment (static data, either initialized or uninitialized). There are problems with deciding what such a data segment would mean:

  • Do all users of the DLL share it?

  • Should it be copied for each process that attaches to the DLL?

  • In addition, there are significant run-time overheads in implementing any of the possible answers.

However, because the WINS/WINSCW emulator uses the underlying Windows DLL mechanisms, it can provide per-process DLL data using "copy-on-write" semantics. This is why the problem goes undetected until the code is built for an actual Symbian OS device.

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.

The associated .map file contains information that helps to track down the source file involved.

Look in Symbian OS\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 ..\..\Symbian OS\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 ..\..\Symbian OS\BUILD\STDLIB\BMMP\ESTLIB\ARM4\UREL\ESTLIB.in(QSORT.o)
    0x10018008    uninitialised1
 *(COMMON)
    0x10018018    __bss_end__=.

So the DLL has 0 x 18 bytes of uninitialized data (.bss) and 0 x 40 bytes of initialized 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 initialized data). The static variables do not get named in the .map file.

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

  1. Declaring C++ objects as const does not help if they have a constructor. The 8 bytes of uninitialized data are allocated to hold the TPoint object, but it will not become const until after the constructor has completed.

  2. The declaration const TText* says that the TText values may not be altered, but it does not make the pointer a constant as well. The 48 bytes of initialized 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 an 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