CeGCC cross compiler for PocketPC

This document describes how to cross-compile Windows CE applications using the tools provided by the CeGCC project. Part of this document is dependent on the host environment, another part is generic.

What does that sentence mean ? The host environment is the environment in which you compile your sources to produce an executable that can run on Windows CE. The CeGCC project actively supports two such environments :

Getting started

In this chapter, we'll show how to compile a very simple source.

Simplistic example

You can type or copy-and-paste this source :

#include <windows.h>

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
          MessageBoxW(0, L"HELLO!", L"H3LLO!", 0);

We've created a file named dialog.c that contains this code. To compile it, run this command :

arm-wince-cegcc-gcc -o dialog.exe dialog.c

Beware - you'll need to have the CeGCC DLLs installed on your PDA if you want to run this application. Read about that in the DLL document.

Commands provided

All the cross-commands have the names that are formed by using their target platform as a prefix, and the tool name as a suffix. For our case, the target platform is called arm-wince-cegcc which means we're building for an ARM processor, running the wince operating system, and producing pe executable files to be linked with the CEGCC library. An example : the C compiler is called arm-wince-cegcc-gcc .

PE stands for Portable Executable file format, and it's Microsoft's implementation of COFF (the Common Object File Format). ARM is a RISC processor family which is found in many embedded systems, also implemented by Intel as their Xscale processor line. Wince is shorthand for the Windows CE operating system. Windows CE is the technology base for environments such as PocketPC, Smartphone, and Windows Mobile.

In an apparent attempt to complicate matters, we implement not only the arm-wince-cegcc target (see above), but also the arm-wince-mingw32ce target. MinGW is a free software project implementing a Minimalist GNU for Windows. We're using this target to build applications that merely use the Windows CE API, not our CeGCC (newlib based) library. (Note that this is neither a full nor an official port of MinGW.)

We used to have a target called arm-wince-pe but renamed that to arm-wince-cegcc for consistency.

arm-wince-cegcc-addr2linearm-wince-mingw32ce-addr2lineConvert addresses into source program file/line number
arm-wince-cegcc-ararm-wince-mingw32ce-ararchive library management tool
arm-wince-cegcc-asarm-wince-mingw32ce-asthe GNU assembler
arm-wince-cegcc-c++arm-wince-mingw32ce-c++the C++ compiler
arm-wince-cegcc-c++filtarm-wince-mingw32ce-c++filtC++ name demangler
arm-wince-cegcc-cpparm-wince-mingw32ce-cppthe C preprocessor
arm-wince-cegcc-dlltoolarm-wince-mingw32ce-dlltoola tool to create DLLs
arm-wince-cegcc-gccarm-wince-mingw32ce-gccthe C compiler
arm-wince-cegcc-gccbugarm-wince-mingw32ce-gccbugBug reporting script for the C compiler
arm-wince-cegcc-gcovarm-wince-mingw32ce-gcovTest coverage analysis tool
arm-wince-cegcc-gdbarm-wince-mingw32ce-gdbGNU Debugger
arm-wince-cegcc-gdbtuiarm-wince-mingw32ce-gdbtuiGNU Debugger
arm-wince-cegcc-gprofarm-wince-mingw32ce-gprofProfiling analysis tool
arm-wince-cegcc-g++arm-wince-mingw32ce-g++the C++ compiler
arm-wince-cegcc-ldarm-wince-mingw32ce-ldthe GNU linker
arm-wince-cegcc-nmarm-wince-mingw32ce-nmthe tool to inspect symbols in load modules
arm-wince-cegcc-objcopyarm-wince-mingw32ce-objcopyCopy and manipulate object files
arm-wince-cegcc-objdumparm-wince-mingw32ce-objdumpa tool to print data about object files
arm-wince-cegcc-ranlibarm-wince-mingw32ce-ranlibtool to add or refresh the library directory
arm-wince-cegcc-readelfarm-wince-mingw32ce-readelfNot very useful, we don't produce ELF files
arm-wince-cegcc-sizearm-wince-mingw32ce-sizePrint the size of executable contents
arm-wince-cegcc-stringsarm-wince-mingw32ce-stringsPrint a list of strings in an executable
arm-wince-cegcc-striparm-wince-mingw32ce-stripStrip debugging symbols and name tables from an executable
arm-wince-cegcc-windresarm-wince-mingw32ce-windresWindows resource compiler

How to build a Makefile that works

A Makefile is a description of how to build something that depends on other sources. We're using it for building executables from program sources, which is its most common use.

A sample Makefile

ARCH=		arm-wince-cegcc
CC=		${ARCH}-gcc ${CFLAGS}
WINDRES=	${ARCH}-windres

CFLAGS=		-g -D_WIN32_IE=0x0400

menu-resource.exe:	menu-resource.o menu-resource.rsc
	${CC} -o $@ menu-resource.o menu-resource.rsc -lcommctrl

menu-resource.o:	menu-resource.c
	${CC} -c $?

menu-resource.rsc:	menu-resource.rc menu-resource.h
	${WINDRES} $? $@

	-rm -f *.o *.exe *.rsc

To give you something to play with, all of the files for this example have been put online :

The executable should work on any suitable PDA. With the menu, you should be able to create a simple line drawing and quit the application.

How does it work

Well, we're not going to explain how Make works, you can read stuff like this make tutorial for that. In your Makefile, you'll have to specify that the arm-wince-cegcc tools need to be used. Mixing your host platform's tools with these ones will have surprising results, you'll need to make sure that you cover all the tools required. For instance, that's the reason why I've overriden the CC variable: all calls to the C compiler will be dealt with in this way. An obvious hole is that any C++ source would trigger the C++ compiler, which is probably still g++, not arm-wince-cegcc-g++, because we've not specified

CXX=	${ARCH}-g++

Failure to do so could produce this :

dannypc: {136} make t.exe
g++ -O   -c -o t.o t.C
arm-wince-cegcc-gcc -g -D_WIN32_IE=0x0400 -o t.exe t.o
t.o: file not recognized: File format not recognized
collect2: ld returned 1 exit status
gmake: *** [t.exe] Error 1
rm t.o

That's because this generated a t.o file meant for (in my case) a Linux Pentium system, and the arm-wince-cegcc linker can't cope with that.

An important thing to know about Windows is that applications consist of program sources (in languages such as C or C++), but also of resource files. The traditional linker command line for UNIX and its lookalikes would only gather a bunch of .o files and libraries to combine them into an executable file. On Windows, there's a binary form of resource files that needs to be put in the exe files in the same way as the .o files. As you can see in the sample Makefile,

The actual run of a make command might look like this :

% make menu-resource.exe
arm-wince-cegcc-gcc -D_WIN32_IE=0x0400   -c menu-resource.c
arm-wince-cegcc-windres menu-resource.rc menu-resource.rsc
arm-wince-cegcc-gcc -D_WIN32_IE=0x0400   -o menu-resource.exe menu-resource.o menu-resource.rsc -lcommctrl

Using the arm-wince-mingw32ce target

The only difference with all the above is the name of all the tools. You can still call e.g. the compiler in the same way, it just has a slightly different name. The Makefile that you can download (see above) is actually slightly larger than described here. Issuing a simple make command will also run compile commands such as :

arm-wince-mingw32ce-gcc -o hello-small.exe hello-dialog.c
which creates an executable which only calls Windows CE's coredll, not the CeGCC DLL (cegcc.dll).

Things you want to know when porting sources

This needs to be expanded, please provide feedback with your experience so this chapter grows. The address for feedback is our mailing list : cegcc-devel@lists.sourceforge.net .

Use of Unicode

Many applications are currently still coded to cope with one-byte characters. Several operating systems have been providing support for both that traditional way of working, and a more general approach. Windows CE is special in that it only supports Unicode in many of its API's. This means that an application that wants to create a file must pass the file name as a Unicode string. Obviously this means that either the application must be converted to use Unicode throughout, or to convert strings to and from Unicode when necessary. In the latter case, standard APIs such as mbstowcs and wcstombs can be used.

An important warning : the locale definition is not the same on all systems. If you write applications that cooperate with each other across system boundaries, be sure to exchange data only in formats that are truly portable. An example : the wide character encoding on my PDA has two bytes per character, on my Linux system it has four. GDB (actually the arm-wince-cegcc-gdb cross-debugger) and its stub are each working on one of those systems, and they're exchanging data as wide characters. That's rather silly off course, the multibyte encoding is better at exchanging data. For the tools to work together, it was necessary to create special functions that convert strings to and from the PDA format. This was not necessary for the same to work between the PDA and Cygwin (I'm guessing that desktop Windows uses the same format as Windows CE).

Notion of current directory

According to the Microsoft website, Windows CE doesn't support the current directory functionality.

However, some of the XCE functions in our newlib library do support such functionality. (Only in the arm-wince-cegcc target.) The startup code (see src/newlib/newlib/libc/sys/wince/startup.c) sets up an environment variable PWD which it presets to either the current directory from the parent process, or the path extracted from the executable file we're running. Similarly a PATH variable is also put in the environment.

More information about this is in The unix-like layer provided by CeGCC.

Standard C functions that aren't supported

The functions supporting the long double type aren't supported by Windows. strtold is an example of that, replace it by strtod if you can. more info ..

Apparently broken include files

In the MinGW environment (our arm-wince-mingw32ce target), some include files are meant to be missing. That's because the environment mimics the Windows CE platform as closely as possible. This can lead to unexpected situations, because standard C include files (some that are part of the ISO C Standard) will appear not to work.

An example of this is <errno.h> : this include file is part of several well document standards, see e.g. this description by the Open Group. This functionality is not part of Windows CE, so a development environment that closely mimics the platform API should not contain this include file or implement its contents.

One way to implement this is to leave <errno.h> out of our mingw implementation. This is not what we did. We do have this file, but it contains a construction which will make its use always fail. This is because the <errno.h> contains this

#ifdef __COREDLL__
#include_next <errno.h>
#else /* __COREDLL__ */

When you try to compile a source which includes this file, it'll fail.

Pedro says he has virtually deleted these files : they're there but you can't use them any more. The reason for this approach is that this type of approach might allow us to keep our sources in sync with the maintainers (of MinGW, in this case).

Note that the arm-wince-cegcc target has different characteristics than arm-wince-mingw32ce target. The <errno.h> file is an example of such a difference : it does exist, and works, in arm-wince-cegcc.

Mixing coredll.dll and cegcc stdio

When you're using the arm-wince-cegcc tools, the unix-like layer kicks in : the cegcc.dll library provides you with all of newlib, a library originally written to offer a cheap C library for embedded environments.

Newlib comes with its own implementation of stdio, functions like fopen, fprintf, fclose, scanf, printf, and many more.

The trouble is that coredll.dll, Windows CE's runtime library, also comes with these functions, and the two libraries are incompatible with each other.

Getting in trouble with this is not all that easy, so we hope you don't go down this road. Should you ever find yourself in a position where you find that stdio functions mysteriously fail on you, then think about this. The way to figure out which functions your application is using is to use the arm-wince-cegcc-objdump utility. It'll tell you from which library each function is obtained. All of stdio should come from the same library.