Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries. Thanks for all your past and future contributions.

Revision as of 07:38, 27 July 2012 by hamishwillee (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

A Guide To P.I.P.S.

From Wiki
Jump to: navigation, search
Article Metadata
Code Example
Installation file: Media:Zsh telnetd 1 0 SS.sis
Created: hamishwillee (10 May 2011)
Last edited: hamishwillee (27 Jul 2012)
Editing-A-Guide-To P.I.P.S.-front-cover.jpg
At the time Symbian OS was designed, the C++ APIs and language standards were immature and did not cater to the particular constraints of mobile or embedded systems. Consequently, Symbian OS created its own idioms to address these constraints.

In time, existing standards such as POSIX (IEEE 1003) grew in scope and were implemented across various platforms, and a large body of 'portable' software was developed that conformed to these standards. Recently, to enable such software to be easily ported to Symbian OS, we have introduced frameworks such as P.I.P.S. and Open C that overlay native Symbian OS APIs with standards-compliant wrappers. These enable you to reuse your existing (standards-compliant) code when developing software for Symbian OS.

P.I.P.S. and Open C are primarily porting aids that help you port POSIX-based software to Symbian OS, quickly and efficiently.


What is P.I.P.S. and Open C?

P.I.P.S. (a recursive acronym for P.I.P.S. Is POSIX on Symbian OS) is a set of standard libraries that enable you to write and build POSIX-compliant code on Symbian OS v9.x. Available for both the S60 3rd Edition and UIQ 3 platforms, it comprises the four 'base' libraries - libc, libm, libpthread and libdl - and related programming constructs (several of these are emulated only, and you should read the following sections carefully to understand the extent of the support available). Despite some limitations, P.I.P.S. can substantially reduce the time and effort required to port POSIX-based software to Symbian OS.

Open C is a set of additional libraries built on P.I.P.S. and supplied by Nokia for the S60 3rd Edition platform only. The libraries provided are libssl, libz, libcrypto, libcrypt and libglib. These libraries are ported from their open source versions and provide the same interfaces.

P.I.P.S. add-ons are libraries and tools that extend P.I.P.S.. The first of these is the libz port for UIQ 3 platforms; other libraries that debuted in Open C will follow. Utilities such as telnetd and zsh are also part of this set and are discussed later in this manual.

Figure 1 is a block diagram that shows the P.I.P.S. and Open C environment architecture.

File:P.I.P.S. and Open C environment architecture.jpg
Figure 1: P.I.P.S. and Open C environment architecture

The P.I.P.S. libraries and some add-ons are included in Symbian OS from version 9.4 onwards, and so will be available on the device (S60 devices based on S60 3rd Edition, Feature Pack 2 and later). For earlier Nokia devices, based on Symbian OS v9.1 – v9.3, please refer to Nokia Developer to acquire the Open C/C++ plugins. For UIQ devices, you can download and install the libraries using a SIS file that is available from here.

This wiki page describes the state of P.I.P.S. and Open C in August 2008, but may mention features expected to be released after that date. It confines itself to the Standard C and POSIX features in the frameworks and does not cover Open C++, which is the new Standard C++ library introduced by Nokia to complement Open C. Documentation for Open C and Open C++ is available at the Open C website (

The Premise of P.I.P.S.

What P.I.P.S. Can Do For You

The premise of P.I.P.S. is twofold. Firstly, it provides engineers with a framework that substantially reduces the cost of porting applications to Symbian OS. Existing standards- compliant code will work on Symbian OS with some minor changes and a rebuild.

Secondly, it provides a more familiar programming interface for developers new to the Symbian platform. It gives them something easier to cut their teeth on, before tackling the fairly steep learning curve of native Symbian C++ programming - which is required if you wish to access most device-specific features such as multimedia or telephony.

In general, if you were to divide your application code into the front end (UI), the back end engine (where you can process events and data, interact with other processes, etc.) and the code that accesses peripherals (to interact with hardware such as the camera or the microphone), P.I.P.S. can usually be used to write or port across the back end engine only. UI libraries such as the X Window System or libncurses are not provided by P.I.P.S..

Figure 2 shows the P.I.P.S. back end.

File:P.I.P.S. back end.jpg
Figure 2: P.I.P.S. back end.

What P.I.P.S. is Not

P.I.P.S. is not a Unix application runtime environment on Symbian OS. You cannot run an application you have compiled for your Unix desktop on Symbian with P.I.P.S.. You will, at the very least, need to recompile your application using the Symbian OS toolchain.

P.I.P.S. attempts to comply with the POSIX.1 (core services) and POSIX.1c (thread extensions) standards as far as possible. However, full compliance is not possible owing to constraints imposed by the underlying architecture of Symbian OS. For example, Symbian OS does not support the fork-and-exec paradigm as it does not duplicate the calling process when creating a new one. There is no fork that returns ‘twice’ – once in the parent and once in the child – to allow you to distinguish in the same source what code runs as parent and what as child. Signals and system calls are not natively supported and can only be logically approximated.

Where possible, we have documented deviations from the standards and outlined workarounds. You may find this information in documentation for the individual APIs or in manuals such as this one.

P.I.P.S. does not, as yet, provide a mechanism to interact with the various phone peripherals (for example, the camera, microphone, etc.) or UI. But you can always invoke relevant native APIs to do this. We suggest that you isolate these operations into separate source files (as these need to be in C++) and link them together with the rest of your P.I.P.S.-based code (which can be in either C or C++) to create a ‘complete’ application.

For a practical example, see File:Porting+TightVNC+to+Symbian+OS+using+P.I.P.S. 1 1.pdf which is a whitepaper about porting a VNC Viewer to Symbian OS with P.I.P.S.

estlib and P.I.P.S.

estlib is an old, partially-compliant, limited implementation of POSIX APIs on Symbian OS, which is now deprecated. Initially written to support a JVM implementation on Symbian OS, estlib does not support threading or IPC (inter-process communication), has limited process creation support, is relatively inefficient and makes poor use of underlying operating system features. P.I.P.S., on the other hand, is much more complete, up-to-date and standards-compliant. Symbian recommends that you use P.I.P.S. instead of estlib, which will eventually be retired.

Please note that estlib and P.I.P.S. are not compatible with each other. This is, by far, the most common cause of problems when working with P.I.P.S.. Mixing estlib’s include paths or libraries with P.I.P.S. may lead to random build or runtime errors.

P.I.P.S. Libraries

P.I.P.S. provides the following standard libraries on Symbian OS:

libc - Standard C and POSIX APIs

  • stdio, including printf(), scanf() and their variants
  • file IO, both buffered and raw, including fopen, open, fread, read, etc.
  • stdlib APIs, including environment variable support
  • string and character manipulation APIs
  • C/POSIX locale
  • search, sort and pattern matching APIs, including glob
  • IPC mechanisms, including pipes, named FIFOs, message queues, semaphores and shared memory
  • process creation APIs, including popen(), popen3(), posix_spawn and system
  • socket and networking APIs
  • wide character variants of most of the above APIs.


  • mathematical and arithmetic functions.


  • thread creation and control
  • synchronization primitives.


  • dynamic load and unload of DLLs
  • symbol look-up by name (on Symbian OS v9.3 onward) or ordinal.

Open C Libraries

Open C provides the following libraries:


The OpenSSL crypto library implements a wide range of cryptographic algorithms. These are used in OpenSSL's implementations of various cryptographic protocols such as SSL, TLS and S/MIME and applications such as SSH and OpenPGP.


The OpenSSL ssl library implements the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols.


Provides functions to encrypt and decrypt data blocks and to hash passwords.


A general-purpose utility library (from the GTK+ project) that provides many useful data types, macros, type conversions, string and file manipulation routines, and an event loop abstraction.


The zlib compression library provides functions for in-memory compression, decompression and verification of compressed data.

Using P.I.P.S. and Open C

Build Notes

A Symbian OS application that uses main() or wmain() as the entry point is a P.I.P.S. application. For these applications, P.I.P.S. provides a static library to serve as glue code between the native E32Main() entry point and the user-specified main() or wmain().

When building a P.I.P.S. application on Symbian OS versions 9.1 and 9.2, ensure that your MMP file[1] contains:

  1. /epoc32/include/stdapis as a SYSTEMINCLUDE. This is where the P.I.P.S. header files are found.
  2. libc.lib in the LIBRARY section. This is required even if you don't use the libc API in your application.
  3. The glue code (libcrt0.lib or libwcrt0.lib) in the STATICLIBRARY section.

Note.pngNote: on Symbian OS v9.3 onwards, this build time configuration is generated and used implicitly by the build system when you set a target type of STDEXE, STDDLL or STDLIB for your P.I.P.S. application, DLL or static library (see Table 1). You do not need to specify them in your MMP file.

libcrt0 is the glue code that you need to use for applications that start with main(). libwcrt0 is the glue code that you need to use for Unicode applications that start with wmain().

Warning.pngWarning: You may find similarly-named header files in /epoc32/include/libc, but these are supplied by estlib and they should not be used for P.I.P.S.-based applications.

Create MMP files

Typical Unix and Linux applications use the make build system. To build the application for Symbian OS, you need to create MMP files from the corresponding Makefiles.

Use the following basic guidelines when preparing an MMP from a Makefile.

  1. Create one MMP file for each target binary you wish to build.
  2. Specify the target name and type (for example, whether it’s an executable, DLL or static library) in the TARGET and TARGETTYPE statements respectively.
  3. Specify a SECUREID and set CAPABILITY for your application or DLL. (Refer to the guide linked from the ‘Platform Security’ section for information on what these are and how to set them.)
  4. Add the path[2] to your source files in the SOURCEPATH statement.
  5. List all of your source files (.c and .cpp) in one or more SOURCE statements.
  6. Add the path to P.I.P.S. header files as a SYSTEMINCLUDE. Add paths to any other header files you wish to use (for example, /epoc32/include, the path to the native headers) in other SYSTEMINCLUDE statements.
  7. Add the path to your project header files in one or more USERINCLUDE statements.
  8. Add the DLLs you wish to link against in the LIBRARY statement, and the static libraries in the STATICLIBRARY statement.
  9. Use the MACRO directive to define any preprocessor defines for your project.
  10. Use the OPTION keyword to specify custom compiler options.

You can find a sample MMP file in the ‘A Simple HelloWorld’ section later in the text. You can also find a detailed description of the MMP file format and the various options available to customize your build in the Build tools reference, under the Tools and Utilities section of the Symbian Developer Library documentation that comes with your SDK.


P.I.P.S. introduced three new target types to Symbian OS v9.3: STDEXE, STDDLL and STDLIB. The Carbide.c++ IDE supports these target types.

Table 1.
Target Type What this does
STDEXE Links in libcrt0.lib or libwcrt0.lib
STDEXE or STDDLL Links in euser.lib and libc.lib
STDEXE or STDDLL or STDLIB Adds /epoc32/include/stdapis as a SYSTEMINCLUDE path

libcrt0.lib is linked in by default when you use STDEXE as the target type. To link in libwcrt0.lib instead, specify the WCHARENTRYPOINT keyword in your MMP file. The libcrt0 and libwcrt0 static libraries provide a default E32Main() for your application and invoke the main() or wmain() function, mimicking the familiar Unix programming pattern. You cannot write your own E32Main() when you use the STDEXE target type.

With an STDDLL, all functions and data with external linkage (global data) are exported by default and therefore do not need explicit IMPORT_C or EXPORT_C declarations.

In all three target types, symbol tables are generated and stored in the binaries, thereby enabling symbol lookup by name. For C++ code, symbol names are mangled if extern ‘C’ linkage is not specified. You need to use the mangled names when looking up symbols by name.

Note.pngNote: The version of your Nokia Codewarrior Compiler (which is also called Nokia x86 Compiler and is used for emulator builds) needs to be at least v3.2.5 Build 465 in order to use these new target types. You can verify this by invoking ‘mwccsym2 --version’ on the command prompt.

P.I.P.S. Concepts

The glue code

The libcrt0 or libwcrt0 library is called the glue code and must be linked statically into P.I.P.S. applications. If STDEXE is the target type, then this happens automatically. The glue code configures the environment, inherits file descriptors, hooks up parent-child pipes and sets up command-line re-direction, before calling main() or wmain().

Although you can launch native and hybrid applications with the process creation APIs provided by P.I.P.S. (as discussed in the following section), these do not inherit file descriptors and therefore cannot be communicated with using the implicit stdio pipes in popen() and popen3().

Hybrid applications

Hybrid applications are Symbian OS native applications that invoke P.I.P.S. or Open C APIs. These can use all P.I.P.S. or Open C features, just like P.I.P.S. applications, but you need to:

  • Remember that these, when launched with the process creation APIs provided by P.I.P.S., do not inherit file descriptors or the environment from the parent and cannot be communicated with using stdio pipes. They can still use the IPC mechanisms provided by P.I.P.S. to talk to other hybrid or P.I.P.S. applications.
  • Be careful about interleaving native APIs with P.I.P.S. APIs when operating on the same resource (see the ‘Interleaving P.I.P.S. and native APIs’ section), and avoid making assumptions about the implementation of P.I.P.S. APIs or data structures.

The stdioserver

The stdioserver allows both P.I.P.S. and hybrid applications to redirect stdio to a console, file or COM port. stdin and stdout media are set in the user-modifiable stdio.ini (previously, config.ini) file in c:\system\data.[3] Note that this file is system-wide and any change affects all processes that subsequently establish a session with the server.

There is currently no provision for configuring stdio per-application[UR]. P.I.P.S. applications connect to the server during launch, and hybrid applications do so on the first operation on stdin/out (for example, printf()). Use of the stdioserver is implicit in all libc's stdio APIs, such as printf(), scanf(), getchar() and their variants.

The stderr file descriptor does not use the stdioserver, unless explicitly dup-ed to the stdout descriptors.

When you install P.I.P.S., stdio.ini contains some default settings for media. Check the file to see if these work for you.

When a console is set as stdin/out media, a console screen pops up and overlaps your application's screen when you perform a stdin/out operation, for example, printf().

Developer Notes

Current working directory

The current working directory for a P.I.P.S. application is ?:\private\<SecureId>[4], where ? is the drive from which the application was launched. This has implications for applications that are launched from ROM, whose working directory is on the read-only Z: drive.

If you wish to set up a different working directory, simply use chdir() at the start of your application.


A write to stderr in P.I.P.S. defaults to an RDebug::Printf(). On the emulator, the output appears in the epocwind.out file. On a development board, the output appears on the debug COM port. You can always redirect (dup2) stderr to another file descriptor to override this behavior.

Temporary files

Temporary files created by libc APIs are stored in c:\private\<SecureId>\tmp.


P.I.P.S. only supports the time of last data modification (st_mtime) on files. The time of last access and the time of last status change (st_atime and st_ctime) are not supported and are always set to st_mtime.


Heaps on Symbian OS can be specified per-thread, unlike in Unix systems where a heap is always process-wide. The native class for threads on Symbian OS is RThread, and it is documented in the Symbian Developer Library found in your SDK. Threads on Symbian OS can be created with their own heap, can share the heap of the creator thread, or can use a separate heap through an RHeap object. If you use an RThread with a separate heap in your application, you cannot malloc() some memory in one thread and simply pass the pointer to another. The latter thread crashes when it attempts to access that pointer or free the memory. This constraint is abstracted away when you use pthreads in your application. pthreads are wrappers over RThreads, but are configured to share the same heap (the heap of the main or creator thread[5]) in a process, thereby ensuring that the malloc-and-share-pointer scenario works as expected.

If you wish to share dynamically allocated memory between two threads that use different heaps, you must create a new shared heap object (an instance of Symbian OS native RHeap class), and allocate memory on that heap using RHeap::Alloc(). The malloc() and free() functions supplied by P.I.P.S. implicitly use the current thread’s heap. If you wish to use these functions with a custom RHeap, you need to make that the default heap of your thread by using User::SwitchHeap().

Internal allocations

Internal objects and buffers allocated by P.I.P.S. APIs are on a private heap and do not affect the heap(s) available to user code. This heap is automatically cleaned up on application exit.

Out of Memory errors

Ported code must be aware that it is running in a memory-constrained environment. Out of Memory situations can occur and need to be handled gracefully. By default, your process is restricted to a maximum of 1MB of heap. You may increase this limit by using the EPOCHEAPSIZE directive in your MMP. See the Symbian Developer Library Build tools reference for further details about using this option.

In very low memory conditions, your P.I.P.S. application may crash at launch with a STDLIBS-INIT panic. This indicates that the P.I.P.S. subsystem was unable to initialize its core components and cannot function. For similar reasons, hybrid applications may encounter this panic when they invoke a P.I.P.S. API.


File IO in P.I.P.S. libc is buffered. This usually implies a faster than native performance for small reads and writes, at the small risk of data loss in the event of battery removal or other emergency power-down situations. You can use the setvbuf() API to disable buffering on a particular FILE.

Pipes created with P.I.P.S. APIs are 512 bytes in size. Therefore, writes that are larger than 512 bytes are blocked until a reader reads some of the data.


  • There is a limit of 256 open file descriptors (including those reserved for stdin, stdout and stderr) per process. This limit applies irrespective of whether you use system calls (such as open()) or stdio APIs (such as fopen()) to open files. The limit of 20 open FILEs implied by FOPEN_MAX in stdio.h is misleading.
  • Outside of these enforced constraints, the size of your heap (and the size of the P.I.P.S. private heap) limits the number of files you can have open.
  • File and directory names in P.I.P.S. can be up to 256 bytes in length.
  • The key_t type in P.I.P.S. is a signed integer. When specifying key_t variables as IPC identifiers (in msgget(), semget() and shmget()), ensure that their values are less than INT_MAX.
  • Files of more than 2GB in size are not supported in P.I.P.S..
  • P.I.P.S. does not provide sysconf()[UR]. You need to look up relevant header files or peruse manuals like this one to determine resource limits.
  • IPC resources (for example, message queues, shared memory and semaphores) are created on the heap of the IPC server. Therefore this heap is, in effect, shared by all processes that use the IPC facilities provided by P.I.P.S.. The system-wide number of IPC objects you can have open is limited by the size of the server’s heap.

Stack size

The default stack size per thread on Symbian OS is 8KB. This default imposes a maximum of 128 threads per process. These limits apply to pthreads as well.

You can use pthread_attr_setstacksize to set a custom stack size before calling pthread_create, but remember that this proportionally reduces the number of threads you can create in that process. For example, with stack sizes of 16KB each, you can create only 64 threads per process.

Stack overflows

Some P.I.P.S. APIs in libc's stdio use a large number of stack variables. If you use these APIs in your application and encounter panics that you cannot isolate[6], try increasing the default stack size using the EPOCSTACKSIZE directive in the MMP file.

Inter-process communication (IPC)

P.I.P.S. supports pipes (both named and unnamed), message queues, semaphores, shared memory and local file sockets (AF_UNIX sockets). All IPC mechanisms in P.I.P.S. (except for pipes and local file sockets) are implemented using a secure server (in its own separate process), and may incur a slight memory and runtime speed overhead.

While setup of the shared memory area involves the server, the actual reads and writes are direct to memory and remain fast. Shared memory areas are always read-write, irrespective of permissions used during creation. This is a constraint of the native interface used to implement them.


Pipes, FIFO special files and AF_LOCAL sockets in P.I.P.S. are implemented using the native Symbian C++ RPipe interface. RPipe is available on Symbian OS v9.3+ although, at the time of writing, the RPipe interface is not public. User code should not use RPipe APIs directly.

For v9.1 and v9.2, P.I.P.S. and Open C installers bundle an embedded SIS that installs RPipe on these platforms.

Memory mapped files

There is limited support for memory maps in P.I.P.S.. PROT_NONE, PROT_EXEC and MAP_FIXED flags are not supported. Code that depends on memory maps with these properties cannot be ported to Symbian OS.

COM ports

When opening COM ports with open, use COM:x as the path, where x ranges from 1-n. COM:1 refers to Symbian OS serial port O.

COM ports can only be accessed from the thread that opened the port [UR]. Attempting to access the port from another thread results in a panic. If the COM port must be shared between threads, you must write a proxy server in a separate thread that opens the COM port and reads or writes to it on request from other threads. You may also use the stdioserver as this proxy server by specifying a COM port as media for stdin and stdout and using stdio API for COM reads and writes.

Socket options

P.I.P.S. supports a subset of standard socket options and IOCTL flags. Please refer to the API documentation for the specifics. setsockopt(), getsockopt() and ioctl() will fail with ENOSUP for unsupported options or flags.

P.I.P.S. supports a number of 'non-standard' socket options that enable access to native networking features. You can use these options to enumerate and query network interfaces or Internet Access Points (IAPs), choose a particular IAP, add/edit/delete routes, start or stop interfaces, configure multicasting, and so on. Here again, details are available in the relevant API documentation.

Error handling

P.I.P.S. maps errors from native APIs to their nearest POSIX equivalents. In cases where such translation would be meaningless, errno is set to outside the usual range: greater than _EMAXERRNO or 124. To retrieve the Symbian C++ error code, use the formula: Symbian OS Error Code = - (errno - __EMAXERRNO).

P.I.P.S. APIs never leave (the Symbian C++ equivalent of throwing an exception). Leaves from the underlying native routines are trapped by P.I.P.S. and are returned instead as errors.

Link files

P.I.P.S. emulates symbolic links on Symbian OS; both link() and symlink() create symbolic links. Hard links are not supported.

Execute permissions

Symbian OS has no concept of execute permissions, and therefore the execute bit in file permissions is ignored in P.I.P.S.. If that is the only bit set in the perm parameter, open(), create(), mkdir() and similar functions fail with EINVAL.

On a related note, the PROT_EXEC flag is not supported and specifying that alone as access type in mmap results in an EINVAL.

Access permissions for IPC objects

The P.I.P.S. IPC Server enforces some access restrictions for IPC objects. The permissions you specify when creating an IPC object must contain at least the USER bits set. Otherwise, even you (the creator) will not be able to operate on the object. If you wish to allow access from other processes (as is usual), ensure that you set the OTHER bits in the perm flag. The GROUP bits are ignored.


The atexit() function does not work on the emulator. It accepts function registrations but does not invoke them on process exit. This is due to a constraint with the build toolchain.

Sparse files

Symbian OS does not support the notion of sparse files. If you seek beyond end-of-file and write some data, the gaps in the file are filled with NULL values that contribute to the file size.

IAP (Internet Access Point) name restrictions

For reasons of backward compatibility, IAP names in P.I.P.S. are restricted to 49 bytes in ASCII. For names in languages with a multi-byte character set, you are restricted to 24 characters for 2-byte and 16 characters for 3-byte representations. All lengths exclude the terminal NULL character.

Users and groups

P.I.P.S. does not support users, groups, process groups, sessions or pseudo-terminals.

Build support APIs

Certain APIs in P.I.P.S. are stubs, provided only to enable builds of existing code. They either return ENOSUP or return 0 but do nothing. For example, setgid(), setuid(), setpgid() and related functions return 0, but do nothing. Likewise, the getters (getgid(), getuid(), etc.) return 0, but that return value means nothing (see the ‘Users and groups’ section).


libm APIs do not use FPU hardware on devices that possess them (many devices do not have them anyway).


Symbian OS versions prior to v9.3 only support symbol lookup by ordinal number (dlsym("1")).

In v9.3 and later versions, symbol lookup by name (dlsym(“foo”)) is supported on executables and DLLs with target types STDEXE and STDDLL respectively. There is, however, a binary size penalty associated with these target types. All symbols are stored as ASCII strings which can cause a noticeable increase in binary size, especially for libraries with a large number of symbols.

Native constraints

P.I.P.S. APIs may reflect issues and limitations with the native interfaces used to implement them; for instance, limits are usually a native constraint. Before reporting a problem with P.I.P.S., we suggest that you attempt the same task natively. If the code succeeds, it implies an issue with the P.I.P.S. implementation or your use of it, so please feel free to post a query here:

Interleaving P.I.P.S. and native APIs

Interleaving calls to P.I.P.S. APIs with functionally equivalent native APIs, when operating on the same resource, may result in undefined behaviour. P.I.P.S. APIs may maintain internal state across functions, which is not updated if you call a native API, resulting in incorrect or unexpected behaviour on a subsequent invocation. For example, mixing RThread APIs with pthreads is usually a bad idea, as is interleaving libc’s stdio APIs with native RFile methods on the same file.

Platform Security

All platform security restrictions (including data caging rules) that apply to native interfaces also apply to applications that use P.I.P.S. or Open C. For details on platform security and the implications it has for your project, please refer to pages about platform security on this wiki.

Signing your application

P.I.P.S. applications must be signed just like native Symbian OS applications by using Symbian Signed. The same rules and procedures apply. Refer to the guide at for a description of the process involved.

Common P.I.P.S. Idioms

Writable static data in DLLs

Writable static data (WSD) refers to any per-process variable that exists for the lifetime of the process. Typically, these are globally scoped variables, static class members or function scoped static variables. WSD is a widely used programming construct on other platforms. Symbian OS fully supports WSD in EXE, but there are issues with the use of WSD in DLLs and the practice is generally discouraged[7].

On target hardware (or phones), DLL WSD works correctly if you explicitly use the EPOCALLOWDLLDATA directive in your DLL’s MMP file. However, you are limited to 4KB of WSD per DLL. Also, a process that attaches to the DLL incurs a cost of 4KB (one chunk), even if the actual size of WSD variables in the DLL is smaller. If the DLL is linked to by several processes, this cost can be significant.

On the emulator, WSD in a DLL is shared between all processes that attach the DLL, and is not per-process as you would expect. This implies that changing the value of a WSD variable in one process updates that variable for every other process using that DLL.

P.I.P.S. provides a library called ewsd.dll to enable per-process WSD in DLLs on the emulator. To use ewsd.dll, you must do the following:

1. If you have the following two global variables in your DLL:
int gref = 0;
int gerr = 0;

create a #ifdef __WINSCW__ section and capture the WSD variables into a struct, which in the example below is named Globals. The #ifdef __WINSCW__ ensures that these workarounds are compiled in for emulator builds alone.

#ifdef __WINSCW__
struct Globals
int gref;
int gerr;

2. Include pls.h and define a getter for your global data (for example, GetGlobals()) that returns a pointer to the struct. The process local storage routine, Pls(), is a templated function that creates and returns a singleton reference to your WSD variables and ensures a copy per process.
#include <pls.h>
TInt InitializeGlobals(Globals* pglobals)
pglobals->gref = 0;
pglobals->gerr = 0;
// Initializer must return KErrNone to indicate success.
// Else the Pls routine panics.
return KErrNone;
struct Globals* GetGlobals()
return Pls<Globals>(KDllSecureId,&InitializeGlobals);
In this example, KDllSecureId is the SecureId (UID3) of your DLL and InitializeGlobals() is an optional initialization routine for your global data; you can pass in NULL instead.
3. In your code, replace the definition of each WSD variable with code snippets such as the following:
#ifdef __WINSCW__
// Define getters for each WSD variable
int* get_gref()
return &(GetGlobals()->gref);
int* get_gerr()
return &(GetGlobals()->gerr);
// Define macros to make the getters opaque to the rest of your code
#define gref (*get_gref())
#define gerr (*get_gerr())
You can return C++ references from your getters, in lieu of pointers, but that may not be an option if your DLL code is written in C.
4. In the MMP file, link in ewsd.lib as a dependency for your project, again guarded by a #ifdef __WINSCW__. Ensure that EPOCALLOWDLLDATA is not set for emulator builds; you should guard this directive, which is required for ARMV5 builds, with an #ifndef __WINSCW__.

The InitializeGlobals() routine is invoked from within the Pls() function if it is the first time a reference is made to the global data. At that point, the pointer is not yet set to the newly created object. This can lead to issues when WSD variables are dependent on each other’s initial state. For example, if the initialization of one variable (say, A) requires a reference to another (say, B), then the initialization of A causes the getter of B to be invoked. This in turn invokes Pls() and, because the pointer is not set, this invokes the initialization routine once again, causing recursion. Your routine must therefore account for dependencies between WSD variables and order the initialization accordingly.

Process creation APIs

Symbian OS does not support the separate fork-and-exec model of process creation. Consequently, P.I.P.S. does not provide either fork() or exec(). If your project uses the return value from fork() to distinguish between code that runs as parent and code that runs as child, you must refactor that section of code.

To create child processes in P.I.P.S., use one of popen(), popen3(), system() or posix_spawn(). Between them, they cover most typical process creation semantics:

  • popen() and popen3() allow you to setup stdio pipe(s) between parent and child
  • system() performs a no-frills process launch
  • posix_spawn() enables you to perform operations (open,close,dup) on the child's file table before it 'starts'
  • both popen3() and posix_spawn() allow you to specify a custom environ for the child.

If the child processes are P.I.P.S. applications, they inherit file descriptors and environment variables from the parent.

You can use wait() and waitpid() to await termination of child processes and to reap their exit status.

File descriptor inheritance

P.I.P.S. applications that were launched by use of a P.I.P.S. process creation API inherit certain open file descriptors from the parent. These include all regular files and pipes.

Named pipes (FIFOs), sockets and regular files in the private directory of the parent process are not inherited.


P.I.P.S. does not support timer APIs or SIGALRM[UR]. One possible workaround is as follows:

  1. Where you would register a timer, create a thread (TimerThread) with a higher than standard priority. In the thread function, create a RTimer and set an alarm using RTimer::After() and a TRequestStatus variable.
  2. The TimerThread now waits on the TRequestStatus variable while the main thread continues.
  3. When the timer triggers, the TimerThread exits the wait. It then issues a Suspend() on the main and other threads in the process and invokes the registered handler. Once the handler returns, it resumes all threads and exits itself.

Please note that there are some caveats:

  • Context switches to and from the TimerThread may introduce some latency. Code that relies on timer resolutions of this magnitude cannot use this solution.
  • The TimerThread executes the handler in its context. As such, the handler cannot access resources owned by other threads (including the one that registered the handler) without prior and explicit sharing. Even with sharing enabled, deadlocks may ensue when the handler attempts to access resources locked by one of the suspended threads. Also, if the threads are RThreads using separate heaps, then the handler cannot access memory allocated by other threads.

If you need to set a number of timers or a periodic timer in your code, and do not want the overhead of creating a thread every time, create a server thread at process startup and establish a session to it from your main thread. Then, all timer registrations are messages sent to the server which creates a timer, as in step 2 above, and acts on the timeout, as in step 3 above.


P.I.P.S. does not support signals [UR]. When porting code that uses signals, you need to modify the code to use some native asynchronous construct that approximates signaling in context. For example, you could extend the concept described in the timers workaround, provided in the previous section, to all signals.

To enable signaling from other processes, the handler thread must set up an IPC facility that sender processes can use to deliver the signal. The facility needs a name that associates it with the recipient process and it also needs to be policed in order to:

  • ensure that arbitrary processes cannot send ‘terminal’ signals (SIGKILL/SIGQUIT) to other processes
  • verify that the sender process is who it claims to be
  • guard against malicious programs tapping into the facility from outside the framework and sending spurious signals.

Export of static data from an STDEXE or STDDLL

As described previously, specifying a target type of STDEXE or STDDLL causes all global functions and data in the executable or DLL to be exported. However, the import of data is not supported on Symbian OS and so you will run into linker errors if you refer to one of these data variables from your application code (that is, any code outside the module defining the global data). If you use symbol lookup (dlsym()) to access these data imports, your code will build, but an attempt to dereference the pointer returned from dlsym() will cause a panic.

We recommend that you use the following workaround:

  • If you are working on the emulator, use ewsd.dll for your WSD variables (as described in the ‘Writable static data in DLLs’ section).
  • If you are working on the target hardware, define getter functions for your global data variables and export these from the module.

For example, if you wish to allow access to a global variable gerrno from outside your DLL, do the following:

1. For the variable int gerrno, define a getter routine:
int* __gerrno() { return &gerrno; }
2. Define a macro in a public header to make this opaque to the user:
#define gerrno (*__gerrno())
3. Application code can now include the header and use gerrno transparently:
gerrno = 32
printf(%d”, gerrno)
With this change (and assuming you want to allow 'import' of gerr and gref), the code example from the ‘Writable static data in DLLs’ section should now be as follows:
// Define getters for each WSD variable
EXPORT_C int* get_gref()
#ifdef __WINSCW__
return &(GetGlobals()->gref);
return &gref;
EXPORT_C int* get_gerr()
#ifdef __WINSCW__
return &(GetGlobals()->gerr);
return &gerr;
// In a public header
// Define macros to make the getters opaque to the application code
#define gref (*get_gref())
#define gerr (*get_gerr())

Remember that, on hardware, you can access these global variables directly from within the binary module that defines them.

Changes in the Development Version of P.I.P.S.

P.I.P.S. is under active development. The below is a list of changes in P.I.P.S. that are being worked on at the time of writing. These will feature in a future release.

Bluetooth Sockets

P.I.P.S. supports Bluetooth RFCOMM sockets. In the socket API, specify AF_BLUETOOTH as the address family or domain and BTPROTO_RFCOMM as protocol. Utility functions strtoba() and batostr() convert Bluetooth addresses from string to native format and vice-versa.


The eselect() function is an extended version of the standard select(). Alongside the usual fd_sets, you can also ‘select’ on a number of arbitrary TRequestStatus variables. eselect() returns as soon as one or more of the file descriptors are ready (with the specified events) or one or more of the TRequestStatus variables are completed. A timeout can be specified as well.

eselect() bridges Symbian native events and P.I.P.S. operations, enabling you to support use cases such as waiting simultaneously for a network event and a key press.

Configuring the stdioserver per-application

To use a custom stdioserver configuration on an application level, create a stdio.ini file in the application’s private folder following the format of the system stdio.ini. Your settings override the system defaults for your application.

Serial Port Access

P.I.P.S. includes an RComm proxy server in the back end, allowing you to access an open serial port transparently from any thread in the process.

Debugging P.I.P.S. Applications

P.I.P.S. applications can be debugged just like native ones; for example, print-lining with libc’s printf() (that uses the stdioserver) or RDebug::Printf() works as expected. However, you may encounter issues on target hardware if you interleave the two and use the same COM port for both APIs.

P.I.P.S. does not provide a logging API; you must roll your own or port one.


P.I.P.S. provides developers with a telnet daemon and a shell (zsh) on the phone. To use telnetd, the phone requires a valid IP address assigned to an interface, for instance, BT PAN, Wi-Fi, Ethernet over USB, and so on.

Simply start telnetd on the phone and telnet to the phone’s IP address from a PC. You can then launch applications, although you can only see their output or interact with them if they are P.I.P.S. applications, and not hybrid or native applications.

The Shell

zsh has no platform security capabilities and cannot access the private directories of applications that you launch from it. To enable access to a particular 'private' file after the return to shell prompt (for example, a log file), your application should link in the copydatafile.lib static library and invoke CopyDataToPublic(<filename>) before exiting. This copies the file to c:\shellpub\<SecureId>. You can then cd to that directory from the prompt and access the file. If the file is intended to be modified from the prompt (a configuration file, for instance) and you need the updated copy when your application runs next, invoke CopyDataFromPublic(<filename>) at the start of the application. This copies the file to your application's private directory.

The file in c:\shellpub\<SecureId> is public, which means that all applications (and not just zsh) can access it.

zsh can browse non-restricted parts of the file system.

zsh requires the stdioserver for its console interface. This dependency may be removed in future releases.

Zsh and Telnet daemon

Zsh (Z shell) is a powerful command interpreter ported on Symbian OS using P.I.P.S.

The combination of Zsh and Telnet daemon running on device/emulator allows users to launch a Unix-like shell from a PC running a generic Telnet client, over a pre-established TCP/IP connection between them. Zsh is created by the Zsh project - more information can be found here and an introduction here.

A ready-made shell and a console interface will make a significant contribution in reducing porting costs, by using a familiar command line interface and by re-use of shell scripts. Allowing the developer to use a host PC as remote terminal greatly improves productivity. Now, developers can work with Symbian OS and its resources more closely than ever before with its plug-in feature and static library for developing their own external commands to enrich their tool box.

See Zsh-Telnetd guide for further details.

"Zsh and Telnetd daemon" v1.0 features a basic port of open source Zsh command interpreter v4.2.6 with set of over 50 commands, support for shell scripts, plug-in library for external commands and a generic telnet console interface. This version supports SymbianOS v9.1 and above.

The latest Zsh-Telnetd files - alpha release

Zsh Telnetd README (.PDF)

Zsh Telnetd SDK Patch for UIQ 3.x and S60 3.x SDK Patch for UIQ 3.x and S60 3.x

Zsh Telnetd .SIS

A Simple HelloWorld

To illustrate how P.I.P.S. can help you write simple applications easily, consider the ubiquitous HelloWorld program. Writing this natively on Symbian OS involves a cleanup stack, a CConsoleBase instance, their methods and a TRAPD.

P.I.P.S. abstracts several of these interfaces and internally sets up a skeleton framework that works well for most applications. This allows simple programs to remain simple. Here is a P.I.P.S. HelloWorld:

// HelloWorld.c/cpp
// Copyright (c) 2008 Symbian Ltd.
// All rights reserved.
#include <stdio.h>
int main(int argc, char** argv)
printf("Hello World\n");
return 0;

And here is its MMP:

With P.I.P.S. on Symbian OS v9.1/9.2:

// HelloWorld.mmp for P.I.P.S.
// 9.1/9.2
// Copyright (c) 2008 Symbian
// Software Ltd.
// All rights reserved.
TARGET HelloWorld.exe
UID 0 0x0f123456
SOURCE HelloWorld.cpp
SYSTEMINCLUDE \epoc32\include
SYSTEMINCLUDE \epoc32\include\stdapis
// LIBRARY libc.lib
// STATICLIBRARY libcrt0.lib

With P.I.P.S. on Symbian OS v9.3 and later:

// HelloWorld.mmp for P.I.P.S.
// 9.3+
// Copyright (c) 2008 Symbian
// Software Ltd.
// All rights reserved.
TARGET HelloWorld.exe
UID 0 0x0f123456
SOURCE HelloWorld.cpp
// LIBRARY libc.lib
// Not required (implicit with


Like all tools, P.I.P.S. and Open C on Symbian OS address a certain niche. Your success with P.I.P.S. largely depends upon whether your problem belongs to that niche. For porting (especially of C/POSIX code), P.I.P.S. and Open C are demonstrably useful. For new code, you could choose to use P.I.P.S., but know that you may need to ‘go native’ at some point. P.I.P.S. allows you some flexibility in choosing when that point will be.

However, a word of warning: P.I.P.S. and Open C abstract away some default idioms (such as the cleanup stack) and enable you to program ‘non-natively’ on Symbian OS. This can make your application prone to the sort of problems that native idioms (such as descriptors) were designed to solve. Buffer overflows, careless cleanup of allocated memory or objects on abort, dead-locked code, busy loops and synchronous high-latency operations that affect program responsiveness are all possibilities. You need to actively guard against these in your code. This is especially true when porting code from platforms that are more accommodating of poor programming or bad design.

And finally, it only remains for me to wish you good luck with your porting.


  1. MMP files specify build instructions on Symbian OS, similar to Makefiles; see the ‘Create MMP files’ section for more information. You can also find a description of their format and how to use them in the Build tools reference, found in the Tools and Utilities section of the Symbian Developer Library documentation.
  2. All paths must be relative either to your EPOCROOT or the location of your MMP file. For example, if your EPOCROOT is / (as is typical), your SYSTEMINCLUDE path will be /epoc32/include/stdapis.
  3. Please note that all references to the C: drive in this manual refer to the system drive on your device. The system drive, as documented in the Symbian Developer Library, is a Read/Writable, internal and non-volatile drive on the device, which may not always be called C:.
  4. Secure_Id (UID3) is your application’s unique identifier, set with either the SECUREID or UID option in the MMP file.
  5. Note the ‘or creator thread’ clause. If you create an RThread with a separate heap and invoke pthread_create from that thread, the new pthread shares a heap with the RThread you created and not with the main thread of the process.
  6. Panics occur when Symbian OS threads exit abnormally. They are equivalent to a thread-level abort on other platforms. If the thread that panics (aborts) is a process-critical thread (either the main thread in the process or a subsequently created thread that is set to ‘Critical’), the process dies as well. Each panic has a category and a reason code that help to identify the cause of the panic. The Symbian Developer Library provides a system panic reference.
  7. For further information on WSD see here and here.

Further Information

Other important documents on this topic are:

Original version

View this document on

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 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 27 July 2012, at 07:38.
74 page views in the last 30 days.