Developing PACE Native Objects
Ben Combee
combee@techwood.org
Version 0.1
2003 January 13
Global Variable Usage
In standard ARM code, the R9 register is used to access global
variables. However, in Palm OS 5, R9 is used by the operating system
and cannot be changed. In its place, the ARM compilers that target
Palm OS use the R10 register as a global pointer.
The way R10 is used is different between CodeWarrior and prc-tools
(GCC).
In CodeWarrior, the ARM tools treat the PNO as if it were a standalone
program that is linked at address 0, with code coming first, and data
following it immediately in memory. The code is compiled as
position-independent code where jumps are done using PC-relative
instructions. This allows the code to be moved anywhere in memory
without having to change any of the instructions.
In this model, R10 points to the start of the PNO, and the code that
computes addresses of global variables always adds R10's value to the
computed address. This lets you change where the global variables are
located by changing the value of the R10 register.
CODE (storage heap) <-- R10 points here
DATA (storage heap)
Basic PNOs, as supported in CW for Palm OS V9.0, have one contiguous
code and data region stored in a resource. The startup code at the
top of the PNO computes R10 by getting the current PC value and
subtracting the offset from the beginning of the function to the
current instruction. This allowed read-only data, but since the data
region was stored in a resource, you couldn't modify any global
variables.
The PNO runtime library in V9.2 added a PNO header and second entry
point that supported having the caller supply a value for R10. This
allowed you to write a loader that would allocate the data on the
storage heap, and give you read/write global variables. This is
discussed in more detail in the section on PNOLoader. When you use
this model, R10 directly points to a random spot of memory. However,
computing R10 plus the size of the PNO's code gives you a pointer to
the chunk of heap that holds a copy of the PNO's global variables.
CODE (storage heap)
...
(codesize bytes before DATA) <-- R10 points here
DATA (dynamic heap)
Embedded Relocations
The C data model allows the compiler to define data that has embedded
pointers. Since PNOs can be located anywhere in memory, you need a
mechanism to alter this data to point to the real locations where
things are before you run your code.
CW for Palm OS 9.2 added this capability using relocation lists.
These are special lists of offsets into the PNO that the linker
identifies as having pointers to other parts of the PNO.
The CW linker emits two different lists. The first list is a list of
the addresses of data that points to items in the code section, while
the second list has addresses of data that points to other data. For
example, the code
int (*gFuncP)() = MyIntFunc;
would allocate a global variable that held the address of the
MyIntFunc function. The location of that variable would be in the
code relocation list. Similarly,
int gImportant;
int *gImportantP = &gImportant;
would add an entry to the data relocation list that matches
gImportantP.
The reason to have separate lists is that code and data might be
located at different addresses, so the code that applies the
relocations needs to use different values.
A relocation is applied by reading the original value, adding the
address of the start of the PNO for code, or the R10 value for data,
and writing that modified value back to memory. All relocations in CW
are for 32-bit values.
Relocation lists are supported by the PnoLoader library and by the
Tapwave Native Model loader code.
Function Pointers
CW for Palm OS 9.0 and 9.2 didn't allow you to easily compute the
address of a function in code in all circumstances. The compiler
would compute the address by taking the offset to the function and
adding R10 to it. This worked when you had a PNO where code and data
were contiguous. However, if you split the code and data sections,
this would compute an address falling in no man's land.
In 9.2, the ARMlet_Runtime library supplied a helper function,
__ARMlet_Take_Func_Addr__. This function would take a value, subtract
R10 from it, and add in the start of the PNO, fixing the function
pointer to the correct value.
In 9.3, the code generation was changed to a true position-independent
code model where function addresses are computed by adding an offet to
the current program counter value, and the helper function is no
longer needed.
C++ and PNOs
to be written
Tapwave Native Applications (a PNO application)
to be written
Floating Point Math
In 68K applications, the compiler would make system calls to perform
operations like add, subtract, multiply, divide, and comparison. For
ARM code, this isn't practical. CodeWarrior supplies a software
floating point library where those operations are done by function
calls. The CW for Palm OS V9.2 release shipped with a faulty version
of the floating point and extended math library; this was corrected in
V9.3.
Limited Stack Space
When a PNO is invoked by PceNativeCall, the R13 register points to a
limited amount of stack space, on the order of 4K. This may be
sufficient for small routines, but it can quickly run out if you use
recursive code or lots of local variables.
The easiest way to get more stack space is to allocate a new chunk of
memory and change the stack pointer to point to it. If your PNO has
global variable support, the code might look like this:
const UInt32 stackSize = 12 * 1024;
MemPtr *gNewStackP = MemPtrNew(stackSize);
gOrigStack = ReplaceR13((UInt32)gNewStackP + stackSize);
// ... call your code
ReplaceR13(gOrigStack);
MemPtrFree(gNewStack);
Remember that the stack grows downward, which is why we replace R13
with a pointer to the end of the block.
The defintion of ReplaceR13 in CodeWarrior, using ARM code, looks like
static UInt32 asm ReplaceR13(UInt32 newValue)
{
add r1, r0, #0 // save newValue in R1 scratch register
add r0, r13, #0 // return original R13 value in R0
add r13, r1, #0 // move new value into R13
bx lr // return to caller
}
Thumb code is similar, but using MOV instructions to copy register
values.
You need to be careful when doing this. If your entry function
defines any local variables, they won't be available once you've
changed R13's value. If you use this technique, it is probably better
to move all your real code into a routine called by your ARMlet_Main
routine.
Calling 68K Code and Palm OS APIs
When a PNO is started, one of its parameters is a function pointer
that lets the PNO call back to the operating system, either to execute
more 68K code or to make a system call. This mechanism is described
in the document "Palm OS 5 ARM Programming.pdf", in the book "Palm OS
Reference.pdf" in the section on PceNativeCall, and in the Palm OS
Companion in the "Calling an ARM Function" section.
To call 68K code or the OS, you need to setup a region of memory that
will act as the argument stack for the calls. Arguments should be
placed in this region the same way that the 68K compiler would push
arguments on the stack, and values need to be byte-swapped if they
aren't already big-endian. You then jump to the callback function
with the 68K emulation state pointer as the first parameter, the
address of your 68K routine as the second parameter, and the pointer
to the argument stack as the last parameter. Since 68K code can
return values in either the D0 or A0 register, you also have to tell
the routine in what register to expect a return value.
Calling Palm OS APIs is very similar; rather than taking the address
of the API, you encode the trap number for the API call in the address
parameter, setting the high bit to tell the OS that this is a system
trap.
To call APIs where one trap is used for multiple entry points, you
need to modify the 68K emulation state to change the D0 register, and
then use the normal trap dispatch mechanism.
Fortunately, when using CodeWarrior, most of this work is encapsulated
in the PACEInterface libraries. It has macros that make creating a
68K call fairly easy, including all the byte swapping and calling
syntax.
PNOLoader Library
to be written
PACEInterface Class / PACEInterfaceLib Library
to be written
Cache Issues
to be written
Split Resource PNOs
to be written
Linking Your PNO into a 68K Application
to be written
Aligning 68K Data for PNO Access
to be written
Debugging your PNO using Palm OS Debugger
to be written
Setting up CodeWarrior to Launch Palm OS Debugger
In your 68K application target settings, go to the Target/Build Extras preference panel. Click on the "Use External Debugger" checkbox, turning this on. For the application, click "Browse..." and locate the PalmOSDebugger.exe program. In the Arguments box, enter the text "%targetFilePath". You can leave the working directory blank.
When you do this, hitting "Debug" from your 68K app will launch the Palm OS Debugger (POD), with your application as its target. POD cannot read the breakpoints you've set in CodeWarrior, so you'll have to set those manually in POD's file view.
Palm OS 5 Native Hacks
When Palm OS 5 was introduced, PalmSource said that you would need to use notifications to replace the trap patching mechanism used for 68K "hack" programs. This has worked to port some of the popular hacks from OS 4, but others cannot be supported by this mechanism.
Igor Nesterov has developed a native hack manager called "Yet Another Hack Manager" that allows loading ARM code to replace system traps on many OS 5 devices. This is using an unsupported mechanism, but seems to work on everything released throug the end of 2003. Details on the hack manager and ARM hack development are at http://yahm.palmoid.com/yahmv.htm and http://groups.yahoo.com/group/palmnest/. Igor's ARM static libraries of system call stubs works with both prc-tools and CodeWarrior for Palm OS.
Directly Calling Palm OS 5 APIs
to be written
Send feedback to combee@techwood.org
Copyright © 2004 Benjamin L. Combee
Palm OS is a registered trademark of PalmSource, Inc.
Metrowerks and CodeWarrior are registered trademarks of Metrowerks Inc.
The views expressed on this website/weblog are those of mine alone and do not necessarily reflect the views of PalmSource or Metrowerks.

qwertYAK / frobnovich
|