|
1 <?xml version="1.0" encoding="utf-8"?> |
|
2 <!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. --> |
|
3 <!-- This component and the accompanying materials are made available under the terms of the License |
|
4 "Eclipse Public License v1.0" which accompanies this distribution, |
|
5 and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". --> |
|
6 <!-- Initial Contributors: |
|
7 Nokia Corporation - initial contribution. |
|
8 Contributors: |
|
9 --> |
|
10 <!DOCTYPE concept |
|
11 PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd"> |
|
12 <concept id="GUID-5BDE6462-E6CA-5738-A587-C7D875574789" xml:lang="en"><title>Static |
|
13 Data</title><prolog><metadata><keywords/></metadata></prolog><conbody> |
|
14 <p>Symbian developers used to PC operating systems, such as Windows, may be |
|
15 used to using writeable static data (typically meaning global variables) in |
|
16 DLLs. On the Symbian platform, this is possible, but not recommended, because |
|
17 it is expensive in memory, and has limited support in the Symbian platform |
|
18 Emulator. </p> |
|
19 <p>This page describes writeable static data (WSD), the alternatives to using |
|
20 WSD, and the costs and issues associated with its use. </p> |
|
21 <section><title>Global writeable static data</title> <p>Global writeable static |
|
22 data (WSD) is any per-process variable which exists for the lifetime of the |
|
23 process. In practice, this means any globally scoped data: data that is declared |
|
24 outside of a function, struct, or class, and function scoped static variables. </p> <codeblock id="GUID-C7F90E06-286B-5E64-B94A-7683AA0DA0FB" xml:space="preserve">TBufC<20> fileName; //WSD |
|
25 void SetFileName() |
|
26 { |
|
27 static Tint iCount; //WSD ... |
|
28 }</codeblock> <p>Sometimes writeable static data appears in a non-obvious |
|
29 way. It is not always the case that <codeph>const</codeph> global variables |
|
30 are read-only data rather than global writeable static data. This is true |
|
31 for <codeph>const</codeph> objects with trivial constructors, such as integers. |
|
32 However if a <codeph>const</codeph> class has a non-trivial constructor, the <codeph>const</codeph> object |
|
33 will require a real variable and must be stored as WSD. For example: </p> <codeblock id="GUID-897D2E68-5AF6-5C69-A5AA-276A9FB89DEC" xml:space="preserve">const TInt myVariable=…; //OK – truly const |
|
34 const TPtrC KSomeConstPtr=...; //NOT OK – non trivial constructor |
|
35 const TRgb KSomeConstCol=...; //NOT OK – non trivial constructor</codeblock> </section> |
|
36 <section><title>Support for global writeable static data on Symbian</title> <p>The |
|
37 Symbian platform supports global writeable static data in EXEs on all versions |
|
38 and handsets. </p> <p>Versions of Symbian OS based on the EKA2 kernel (8.1b |
|
39 and later) support WSD in DLLs on target hardware. Versions 8.1a and earlier, |
|
40 based on the EKA1 kernel, do not support global WSD in DLLs. </p> </section> |
|
41 <section><title>Clean up of global writeable static data</title> <p>Your program |
|
42 must make sure that global writeable static data that the program allocates |
|
43 is cleaned up. </p> <p>The clean up rules are as follows: </p> <ul> |
|
44 <li id="GUID-1D25BA8E-1A8E-5502-8FD9-7F74DBB39EF4"><p>Your program must clean |
|
45 up WSD objects that are defined in the process EXE or its statically-loaded |
|
46 DLLs. When a process exits, the Symbian platform does not automatically call |
|
47 the destructors of these WSD objects. </p> </li> |
|
48 <li id="GUID-7F1EB6D6-2EAD-5318-A066-DC9128A48EAF"><p>Your program does not |
|
49 need to clean up WSD objects defined in DLLs that the process has dynamically |
|
50 loaded using <xref href="GUID-25327159-83D6-3507-B187-09EA4BB3727F.dita"><apiname>RLibrary</apiname></xref>. The Symbian platform automatically |
|
51 calls destructors of these objects. </p> </li> |
|
52 </ul> </section> |
|
53 <section><title>Alternatives to using global writeable static data</title> <p>Native |
|
54 Symbian platform C++ code rarely uses WSD. </p> <p>However, code ported from |
|
55 other operating systems may contain large amounts of static data. For example, |
|
56 code written in the C programming language often makes use of WSD as the "glue" |
|
57 between C function calls. </p> <p>In EKA1, WSD is not supported, so there |
|
58 is no choice but to use the alternative mechanisms provided by the Symbian |
|
59 platform to port such code. Even in EKA2 where global data is supported, Symbian |
|
60 recommends that it only be used as a last resort. </p> <p>The following sections |
|
61 describe the alternatives that can be used to port code that makes use of |
|
62 global writeable static data. </p> <p><b>Use |
|
63 thread-local storage (TLS)</b> </p> <p>Thread Local Storage (TLS) is a single |
|
64 per-thread word that can be used to simulate global writeable static data. </p> <p>All |
|
65 the static data in the DLL is grouped into a single struct or class. On creation |
|
66 of the thread, an instance of the thread is allocated on the heap and a pointer |
|
67 to this data is saved to TLS (using <codeph>Dll::SetTls()</codeph>). On destruction |
|
68 of the thread, the data is destroyed. Throughout the DLL the code references |
|
69 the TLS data (using <codeph>Dll::Tls()</codeph>) rather than the original |
|
70 global writeable static data. </p> <p><b>Wrap |
|
71 in a server</b> </p> <p>The Symbian platform supports writeable global static |
|
72 data in EXEs. A common porting strategy is therefore to wrap the code in a |
|
73 Symbian server (which is an EXE), and expose its API as a client interface. </p> <p><b>Move global variables into your classes</b> </p> <p>With relatively small |
|
74 amounts of code, it may be possible to move most global data inside classes. </p> </section> |
|
75 <section><title>Enabling global writeable static data</title> <p>In order |
|
76 to enable global writeable static data, simply add a statement <codeph>epocallowdlldata</codeph> (case |
|
77 insensitive) to the project's MMP file: </p> <codeblock id="GUID-0E673C33-569E-5F7B-9E51-55C2FC233D70" xml:space="preserve">TARGET my.dll |
|
78 TARGETTYPE dll |
|
79 EPOCALLOWDLLDATA |
|
80 … </codeblock> </section> |
|
81 <section><title>Costs and limitations</title> <p><b>Emulator |
|
82 only allows a DLL with WSD to load into a single process</b> </p> <p>The Symbian |
|
83 platform EKA2 Emulator only allows a DLL with WSD to be loaded into a single |
|
84 process. </p> <p>This is a very serious restriction. If you have a shared |
|
85 DLL with WSD, then the second process that attempts to load it in the emulator |
|
86 will fail with <codeph>KErrNotSupported</codeph>. </p> <p><b>Emulator allows WSD by default</b> </p> <p>The Emulator will allow WSD |
|
87 in DLLs even if <codeph>epocallowdlldata</codeph> is not declared in the mmp |
|
88 file. However the data will be truly global: there will be one copy for the |
|
89 entire emulator, rather than one copy for each emulated process. The only |
|
90 restriction is that if the data's initialisers call any Symbian platform kernel |
|
91 functions (i.e. executive calls), then the emulator will fault. </p> <p><b>RAM usage for WSD data chunk </b> </p> <p>When a process loads its first |
|
92 DLL containing WSD, it creates a single chunk to store the data. The data |
|
93 for subsequent WSD-enabled DLLs is loaded into the same chunk. </p> <p>The |
|
94 data chunk will occupy at least one 4K RAM page (the smallest possible RAM |
|
95 allocation), irrespective of how much static data is required. Any memory |
|
96 not used for WSD will be wasted. Since the memory is per-process, the memory |
|
97 wastage on the machine is: </p> <codeblock id="GUID-005105EF-A09A-5FF7-8056-7316AFCE1894" xml:space="preserve">(4Kb - WSD Bytes) × number-client-processes</codeblock> <p>It is very easy for a developer to add a few words of WSD to their DLL thinking |
|
98 that's all the memory that they are using. However, the cost is actually 4K |
|
99 for every process if a DLL with WSD has not already been loaded into the process. |
|
100 If for example the DLL is used by 4 processes, that's potentially an "invisible" |
|
101 cost of 16K. </p> <p><b>Chunks |
|
102 are a finite resource</b> </p> <p>EKA2 has a hard coded limit of 16 chunks |
|
103 per process; a limit that is required to ensure real-time behaviour. Every |
|
104 process loading WSD-enabled DLLs uses a chunk to hold the data, reducing the |
|
105 number of chunks available for other uses. </p> <p><b>ARM architecture 4 and 5 specific costs and limitations</b> </p> <p>There |
|
106 are other significant costs that apply only to DLLs that link against "fixed |
|
107 processes". Fixed processes are a feature of ARM v4 or v5 architecture only; |
|
108 the following behaviour does not apply to devices based on the ARMv6 architecture. </p> <p> <b>Case |
|
109 1: Non Execute-in-place DLLs</b> </p> <p>For non-execute-in-place (non XIP) |
|
110 DLLs, an additional code chunk is required for every fixed process which links |
|
111 against the DLL. </p> <p>So imagine a 20Kb DLL (with a few bytes of WSD) that |
|
112 is loaded into 4 normal "non-fixed" processes, and 2 fixed processes. The |
|
113 (static) memory consumed is: </p> <ul> |
|
114 <li id="GUID-57F9041D-240C-5B75-BC0C-EFA66AB411B6"><p>Code chunk shared by |
|
115 all moving processes = 20 Kb </p> </li> |
|
116 <li id="GUID-1FCC1ED4-0A60-5983-AF5A-2F7C8622BC80"><p>Code chunk for each |
|
117 fixed process loading the DLL = 40 Kb </p> </li> |
|
118 <li id="GUID-6CD0DBDD-E406-5F7D-9207-8E55B4043779"><p>Data chunk for each |
|
119 process loading the DLL = 6×4Kb = 24 Kb </p> </li> |
|
120 </ul> <p>So to allow a few bytes of WSD there is a 64Kb increase in consumed |
|
121 memory. Note that the 20Kb code chunk shared by all processes is consumed |
|
122 whether or not WSD is enabled. </p> <p> <b>Case 2: Execute-in-place DLLs</b> </p> <p>For |
|
123 XIP DLLs, there is no additional RAM cost other than the size of the WSD itself, |
|
124 rounded up to the next multiple of 4KB. However: </p> <ul> |
|
125 <li id="GUID-8B1EDEA4-8082-5DB1-87A8-E7B0FE5BB802"><p>An XIP DLL can be loaded |
|
126 by any non-fixed process, OR it may be loaded by a single fixed process (and |
|
127 therefore cannot be loaded by any other processes whatsoever) </p> </li> |
|
128 <li id="GUID-BA2D82B1-A036-5281-95EB-A46A7976605B"><p>The ROM build fails |
|
129 if a DLL with static data links to a fixed process <i>and</i> any other process. </p> </li> |
|
130 </ul> <p> <b>A few specific DLLs cannot have WSD</b> </p> <p>DLLs that are |
|
131 required to initialise the file server cannot have WSD, e.g. HAL.DLL, EUSER.DLL, |
|
132 EFSRV.DLL. </p> </section> |
|
133 <section><title>Frequently asked questions</title> <p><b>How |
|
134 does Symbian's WSD implementation work ?</b> </p> <p> <b>Case 1. For moving |
|
135 processes:</b> </p> <p>The Symbian platform supports a moving memory model, |
|
136 in which the data for a process is moved into a fixed virtual address space |
|
137 (the "run section") when the process is active, and then back into the processes |
|
138 "home" section when another process is active: </p> <ul> |
|
139 <li id="GUID-26E98921-9094-5260-BA99-2F7FA4F7D6A7"><p>The data of processes |
|
140 in the "home section" is uniquely addressed both in terms of physical RAM |
|
141 pages, and in the MMU virtual address space. The data is protected from other |
|
142 processes, except for the Kernel. </p> </li> |
|
143 <li id="GUID-BBE7C17A-6FF7-5329-91C5-DEAF88A61B69"><p>The data of processes |
|
144 in the run section occupies the same virtual address space as all other processes |
|
145 when they run (the MMU moves the physical RAM pages into the appropriate virtual |
|
146 address space). </p> </li> |
|
147 </ul> <p>When a DLL with WSD is loaded (or at ROM build time), its code is |
|
148 fixed to point to static data at specific addresses. In order to ensure that |
|
149 only a single copy of the DLL code is required, Symbian's moving-process WSD |
|
150 implementation ensures that the virtual addresses of static data is the same |
|
151 across every running process. </p> <p>The way this works is: </p> <ol id="GUID-66A040C2-7B0F-56E0-A634-501D3E133A12"> |
|
152 <li id="GUID-964A78E1-D86E-50D0-8AA0-D3D9346A43E7"><p>A specific address space |
|
153 is reserved for a per-process static data chunk. This chunk is used to hold |
|
154 all the static data for all the DLLs loaded into the process. </p> </li> |
|
155 <li id="GUID-0A528F28-55B2-5B8B-A337-8B2377241CF0"><p>At ROM build time the |
|
156 kernel reserves specific addresses within the static data chunk for all the |
|
157 WSD in all the ROM loaded DLLs. The addresses for ROM based DLLs are reserved |
|
158 from the top of the static data chunk address space to the bottom. Note that |
|
159 static data addresses for RAM based DLLs are reserved when the DLL is first |
|
160 loaded into any process. In this case, addresses are reserved from the bottom |
|
161 of the static data chunk address space. </p> </li> |
|
162 <li id="GUID-1898478C-2E24-53B4-BDEB-765FEEDC539C"><p>When the first DLL with |
|
163 WSD is loaded into a process, a static data chunk is created to hold the static |
|
164 data for all DLLs that are loaded into the process. </p> </li> |
|
165 <li id="GUID-7A88C9EB-31F9-559E-8DBD-221848CA2A46"><p>Any global static data |
|
166 in the DLL is written to its specific reserved addresses. Note that addresses |
|
167 are reserved for that particular static data across all processes; if the |
|
168 DLL is loaded into another process, any static data will get the same virtual |
|
169 address. </p> </li> |
|
170 </ol> <p> <b>Case 2. For fixed processes:</b> </p> <p>A fixed process is |
|
171 one in which the process data does not move; code is run on process data stored |
|
172 in the process "home section". </p> <p>Since the static data for every fixed |
|
173 process is uniquely addressed, and a DLL can only point to a single address |
|
174 for its data, the implication is that a separate copy of the DLL code is required |
|
175 for every fixed process that loads the DLL. </p> <p>The restrictions listed |
|
176 above for ARM architecture 4 and 5 directly result. </p> <ul> |
|
177 <li id="GUID-125EDB37-4540-5EDB-9C19-6F92A307627F"><p>For XIP based devices |
|
178 the DLL code chunk address is fixed at ROM build time, and there can only |
|
179 be one copy of the DLL code. Therefore the DLL code can address the data in |
|
180 either a single fixed process or the virtual address used by all moveable |
|
181 addresses. </p> </li> |
|
182 <li id="GUID-BC66EA5A-252D-5E37-80F6-BEC82C03DABA"><p>For non-XIP based devices |
|
183 the DLL is run from RAM, and the loader is able to fix-up the address that |
|
184 a DLL expects its data at load time (rather than ROM build time). Therefore |
|
185 in this case the loader creates a separate copy of DLL code for each fixed |
|
186 process that loads the DLL and a single copy shared by all moving processes. </p> </li> |
|
187 </ul> <p>Notes: Fixed processes are not supported or required on ARM v6 architectures. |
|
188 This discussion only applies to devices based on ARMv4 or v5. On ARM architecture |
|
189 6 each DLL with WSD has a reserved address, similar to the ARMv5 moving process |
|
190 case. However there is no 'home section' and memory is not relocated between |
|
191 low and high addresses on a context switch. Instead, each process uses its |
|
192 own set of page tables for the bottom half of virtual address space. </p> <p><b>Why does the EKA2 Emulator only allow a DLL with WSD to load into a single |
|
193 process?</b> </p> <p>The Symbian platform permits a DLL with global data to |
|
194 be loaded into only one Symbian platform process on the emulator. This limitation |
|
195 is a result of the way the Emulator is implemented on top of the Windows process |
|
196 model. </p> <p>On EKA2, separate Symbian platform processes are emulated within |
|
197 a single Windows process. To preserve Symbian platform semantics there should |
|
198 be one copy of the global data for each emulated process. However, this is |
|
199 not possible since a DLL on the emulator is just a Windows DLL; Windows gives |
|
200 it a single copy of any global data it needs when it is loaded. </p> <p><b>What happens if epocallowdlldata isn't declared for a DLL with WSD?</b> </p> <p> <b>Case |
|
201 1: On the EKA2 Emulator:</b> </p> <p>Most constant data should be treated |
|
202 by the compiler as read-only rather than writeable static data (the exception |
|
203 is when the const data has a non-trivial constructor, so a real variable is |
|
204 required during initialisation). </p> <p>Unfortunately, different compilers |
|
205 sometimes treat const data as WSD. For example, CodeWarrior puts it in writeable |
|
206 data and initialises it at run time. MSVC generally puts it into read-only |
|
207 data, but occasionally puts it into writeable data. </p> <p>As most DLLs have |
|
208 const data, this means that the compilers have "accidentally" created WSD |
|
209 in almost every DLL. Symbian cannot therefore rigorously enforce the "single |
|
210 process can load a DLL with WSD" rule, as the Emulator would not work. </p> <p>On |
|
211 the EKA2 Emulator, the workaround Symbian have implemented is to recognise |
|
212 two types of DLL global data: </p> <ul> |
|
213 <li id="GUID-6AD2E89F-6361-5F1F-8EC1-A203801BCC8F"><p>'Deliberate' global |
|
214 data is where the programmer specifies that they want DLL global data (using |
|
215 the <codeph>epocallowdlldata</codeph> keyword in the MMP file. In this case |
|
216 any global data in the DLL is assumed to be deliberate, and the "DLL loaded |
|
217 into one-emulated-process" rule applies. </p> </li> |
|
218 <li id="GUID-225E44BE-434E-5C2A-8503-318E31B17EF0"><p>'Accidental' global |
|
219 data is the data introduced by the compiler with no encouragement from the |
|
220 programmer. If <codeph>epocallowdlldata</codeph> is absent, global data is |
|
221 assumed to be accidental and the rule does not apply. Note that the global |
|
222 data includes both const and non-const static data; there is no way to tell |
|
223 the compilers to only apply it to non-const data - if we could do that then |
|
224 we could force correct handling of const static data. </p> </li> |
|
225 </ul> <p>In order to prevent abuse of this workaround, there are restrictions |
|
226 on what can be done with accidental global data; specifically, the Emulator |
|
227 will fault if any of the global data's initialisers attempt to call the Symbian |
|
228 platform kernel functions (i.e. executive calls). </p> <p>Note that there |
|
229 is only one copy of the global data. Therefore it is possible for two processes |
|
230 to write to the same 'accidental' global data (causing undefined behaviour). |
|
231 The "DLL loaded into one-emulated-process" rule prevents this being a problem |
|
232 for deliberate global data. </p> <p> <b>Case 2: On the EKA1 Emulator</b> </p> <p>The |
|
233 EKA1 emulator has no concept of separate Symbian platform processes. Global |
|
234 data is allowed on the Emulator as there is only one copy of any global data. </p> <p>Symbian |
|
235 developers often have problems when porting to real hardware, which does not |
|
236 support this type of data. This is discussed in the following section. </p> <p> <b>Case |
|
237 3: On native target builds</b> </p> <p>On either EKA1 or EKA2, target compilers |
|
238 will fail the build with an error indicating that the code has initialised |
|
239 or un-initialised data. </p> <p>The error message does not specify the symbol(s) |
|
240 that are causing the problem. The <filepath>.map</filepath> files output by |
|
241 the linker can however be helpful in finding the problem symbol. The RVCT |
|
242 map files provide a list of global symbols and the object files in which they |
|
243 occur. FAQ-0329 on the <xref href="http://developer.symbian.org/wiki/index.php/Symbian_C++_Knowledgebase_Q&As#How_can_I_find_the_.22uninitialised_data.22_in_my_DLL.3F" scope="external">DevNet knowledgebase</xref> works through an example of using |
|
244 a map file using the GCC compiler used on older Symbian platform releases. </p> <p><b>Can Kernel DLLs have WSD ?</b> </p> <p>Yes, WSD is supported for kernel |
|
245 DLLs in both EKA1 and EKA2 (through alternative mechanisms to those described |
|
246 here). </p> <p>Of course, kernel DLLs are guaranteed to be loaded into only |
|
247 one process, so the per-process multiplication of RAM usage does not apply. |
|
248 EKA2 will work correctly with global data in any kernel DLL. However EKA1 |
|
249 does not call constructors for global C++ objects in kernel extensions or |
|
250 device drivers, and does not call destructors for global C++ objects in device |
|
251 drivers at driver unload time. </p> </section> |
|
252 </conbody></concept> |