extern "C" and Symbian DEF files

This article gives a general idea about the Symbian DEF file. Whenever developers write any DLLs that export a set of APIs to the user, there will be an entry for each exported API in the DEF file created. The developer will use:

  • IMPORT_C while declaring exported APIs

  • EXPORT_C while defining such APIs

After building the application for the first time, it lists all the newly exported APIs. After doing a freeze (for which the abld freeze winscw and abld freeze armv5 commands are used), the DEF files will be created under BWINS and EABI folders within the project directory. These DEF files are used to create .lib files for DLLs. In the DEF file, for each exported API, there will be mapping between its prototype and a number will be present.

Let's assume that directory structure used for implementing a library is as shown below:

  • LibraryName\group -- bld.inf and LibraryName.mmp (all build-related files)

  • LibraryName\inc -- All project-specific headers (user include headers)

  • LibraryName\src -- All project-specific source files

If a developer is building the library for the first time, the developer should follow these steps and give the following commands from LibraryName\group:

  1. Create the abld.bat file for the project.

    LibraryName\group>bldmake bldfiles
  2. Build the project for the default platform (which will be WINSCW and ARMV5).

    LibraryName\group>abld build
  3. Create the DEF file for the default platform (LibraryName\BWINS\LibraryNameu.def and LibraryName\EABI\LibraryNameu.def).

    LibraryName\group>abld freeze
  4. Create .lib files for the DLL using DEF file entries. (LibraryName.lib)

    LibraryName\group> abld build

The final step is required since applications that link with this library (LibraryName.DLL) will link with LibraryName.lib during link time. If the developer fails to perform the final step, the application will get a linker error because there is no LibraryName.lib present.

Rule for using extern "C"

When porting or writing libraries that export C APIs to the user, care should be taken to ensure that extern "C" is used properly if those APIs are implemented in a C++ source file. Otherwise these libraries may either not be built for the target platform (ARMV5) or the user of such library will get a linker error when using exported APIs of such library.

If extern "C" is declared in the header and not defined in the source, then after building the library there will be a similar entry in DEF files. The compiler will not give external linkage to these APIs. This can be observed when trying to use these APIs from some other C source file. To overcome this, extern "C" must be present in both declarations and definitions.

Example code

/*File: SampleDLL.h*/
/*extern "C" is not used for declaration!*/ 
#ifndef SAMPLEDLL_H
#define SAMPLEDLL_H

typedef int MyInt;
typedef int (*MyFunPtr) ();

IMPORT_C int MyLibLibFun1(MyInt aParam);
IMPORT_C int MyLibLibFun3(MyFunPtr);
IMPORT_C int MyLibLibFun2(int (*MyFun1)());

#endif /*SAMPLEDLL_H*/



//File: SampleDLL.cpp
#include "SampleDLL.h"
// extern "C" is used for definition
extern "C"  {

EXPORT_C int MyLibLibFun1(MyInt aParam)
   {
   return aParam + 10;
   }

EXPORT_C int MyLibLibFun2(int (*MyFun1)())
   {
   return MyFun1();
   }

EXPORT_C int MyLibLibFun3(MyFunPtr aPtr)
   {
   return aPtr();
   }
}

Building for WINSCW

This code will get compiled for WINSCW. The DEF file for WINSCW will have the following entries:

EXPORTS
     ?MyLibLibFun1@@YAHH@Z @ 1 NONAME ; int MyLibLibFun1(int)
     ?MyLibLibFun2@@YAHP6AHXZ@Z @ 2 NONAME ; int MyLibLibFun2(int (*)(void))
     ?MyLibLibFun3@@YAHP6AHXZ@Z @ 3 NONAME ; int MyLibLibFun3(int (*)(void)) 

If the user comments extern "C" from the above SampleDLL.cpp file, the DEF file entries will be the same.

Building for ARMv5

If the user try's the above code without extern "C" in SampleDLL.cpp, the code will get built and the DEF file entry for ARMv5 will be:

EXPORTS
     _Z12MyLibLibFun1i @ 1 NONAME
     _Z12MyLibLibFun2PFivE @ 2 NONAME
     _Z12MyLibLibFun3PFivE @ 3 NONAME  

If the user keeps the extern "C" in SampleDLL.cpp, this code will not get compiled for ARMv5 and will result in the error given below:

"..\src\Sampledll.cpp", line 7: Error:  #337: linkage specification is incompatible with previous "MyLibLibFun1" (declared at line 9 of "..\inc\SampleDLL.h")
   EXPORT_C int MyLibLibFun1(MyInt aParam)
                ^ 
"..\src\Sampledll.cpp", line 17: Error:  #337: linkage specification is incompatible with previous "MyLibLibFun3" (declared at line 11 of "..\inc\SampleDLL.h")
   EXPORT_C int MyLibLibFun3(MyFunPtr aPtr)
               ^

The reason for the errors is that MyLibLibFun1 and MyLibLibFun3 use typedef datatypes. There is no issue with MyLibLibFun2() since it does not use typedef datatypes. To overcome these errors, the user must keep both the declaration and the definition within extern "C" linkage.

/*File: SampleDLL.h*/    
#ifndef SAMPLEDLL_H  
#define SAMPLEDLL_H  
/*extern "C" is used for declaration also*/

typedef int MyInt;  
typedef int (*MyFunPtr) ();    

#ifdef __cplusplus  
extern "C" {  
#endif //__cplusplus    

IMPORT_C int MyLibLibFun1(MyInt aParam);  
IMPORT_C int MyLibLibFun3(MyFunPtr);  
IMPORT_C int MyLibLibFun2(int (*MyFun1)());    

#ifdef __cplusplus  
}  
#endif //__cplusplus    

#endif /*SAMPLEDLL_H*/ 

When everything is within extern "C" (extern "C" in header and implementation), the DEF file for ARMv5 and WINSCW looks as follows:

EXPORTS
    MyLibLibFun1 @ 1 NONAME
    MyLibLibFun2 @ 2 NONAME
    MyLibLibFun3 @ 3 NONAME 

So, as one can observe in the above section where different DEF file entries are created with and without extern "C", the user must make sure that the rule is followed. extern "C" has to be used in both declaration and definition: otherwise the user will either, not be able to build it for all the platforms or, it will not be possible to use those exported C APIs in other applications.