Peal: Palm ELF ARM Loader version 2005-4-14 Peal is a simple ELF postlinker and loader for Palm OS. It allows ARM code to use global variables and function pointers without restriction. http://www.sealiesoftware.com/peal/ FEATURES Features supported by Peal and Peal-loaded ARM code: * Global variables * Function pointers * Automatic handling of code segments larger than 64 KB * Multiple entry point functions * Access to global variables from m68k code * Minimum memory usage - code and read-only data are not copied to feature memory or the heap * Thumb and ARM/Thumb interworking LIMITATIONS * Peal is substantially UNTESTED. It may crash, generate incorrect ARM global data, or modify random memory locations at runtime. * Peal has some support for C++ code, but it is even less tested. * Peal does not provide support for arbitrary ARM-ELF code. Only code built with Peal's postlinker can be loaded by Peal. * Peal does not provide support for ELF shared libraries. * Peal uses the stack space provided by PceNativeCall(), which is only 4 KB by default. There are ways to increase the ARM stack size, but Peal does not use any of them. USAGE A synopsis of Peal usage follows. See the example/ directory for a simple demonstration, and the rest of this README for complete details. 0. Build the peal-postlink program in the postlink/ directory: `cd postlink; make` 1. Write ARM code that uses global data or function pointers. ARM functions intended to be called by m68k code may take a single parameter. ARM functions that need to call back into m68k code should use the variables gEmulStateP and gCall68KFuncP from arm/pealstub.h. unsigned long MyArmFunction(void *arg) { ... } 2. Write m68k code that uses the API in m68k/peal.h to load ARM code and call ARM functions: PealModule m = PealLoadFromResources('armc', 1000); void *address = PealLookupSymbol(m, "MyArmFunction"); unsigned long result = PealCall(m, address, myArg); PealUnload(m); 3. Compile m68k/peal.c along with other m68k code. 4. Add -fPIC when compiling to compile all ARM code as position-independent: `arm-palmos-gcc -fPIC ...` Compile arm/pealstub.c along with other ARM code. 5. Add --emit-relocs when linking ARM code to preserve relocation information: `arm-palmos-ld --emit-relocs ...` or `arm-palmos-gcc -Wl,--emit-relocs ...` 6. Run the Peal postlinker to generate ARM code resources: `peal-postlink -s 1000 -t armc -o postlinked-rsrcs.ro linked-binary.bin` 7. Add the resources generated by peal-postlink to your Palm program as usual. LARGE CODE Palm OS limits resources to somewhat less than 64 KB each. There are two ways to use Peal with code or initialized data larger than 64 KB. 1. Use ld's `--split-by-file` option to split the code into segments smaller than 64 KB each. Then use `peal-postlink -s ...` to place each segment into its own Palm resource. Finally, load the code with PealLoadFromResources(). `arm-palmos-ld --split-by-file=64000 ...` or `arm-palmos-gcc -Wl,--split-by-file=64000` This method is fully automatic with Peal. No manual segmentation or code reassembly is needed. This method also takes the minimum amount of memory, because PealLoadFromResources() does not need to copy the code into feature memory or the heap. Cross-segment function calls are somewhat less efficient that intra-segment calls. For maximum performance, order the files on the link line so files that call each other are close together. Alternatively, use __attribute__((section(...))) to segment by hand. For very large source files or large data segments, `--split-by-file` may still generate segments larger than 64000 bytes. In this case, Peal automatically splits the segment across multiple resources, and reassembles it during PealLoadFromResources(). Note that peal-postlink will warn you that the large segment will require more memory at runtime, because the reassembled section is allocated on the dynamic heap and the reassembly process itself temporarily uses additional heap memory. 2. Build the code and data as one big binary, using peal-postlink without the -s option. Split the binary into multiple code resources. Then reassemble the code resources into a single block in feature memory or on the heap. Finally, load the code with PealLoad(). This method consumes twice as much memory, and Peal provides no special support for this reassembly. C++ COMPATIBILITY Peal provides support for C++ static constructors and destructors. By default, the static constructors are run during the first PealCall(), and the static destructors are run during PealUnload(). If the C++ static constructors need to use m68K callbacks, you can set up the callback pointers before the first PealCall(). If you need to force constructors or destructors to run earlier, use PealCxxConstruct() and PealCxxDestruct(). The constructors and destructors are run only once, even if you call these functions multiple times. Support for other C++ code has received little testing. It may or may not work correctly. THUMB COMPATIBILITY Peal can be used with Thumb code and interworked ARM/Thumb code. Thumb code is usually substantially smaller than equivalent ARM code, but may run more slowly. PealCall() may be used with ARM functions or Thumb functions. Note that PealLookupAddress(myModule, "MyThumbFunction") returns &MyThumbFunction + 1. This address may be used as a function pointer in Thumb code or interworking-enabled ARM code. However, this address may not be passed to PceNativeCall(). Use PealCall() instead. PealArmStub() in pealstub.c may be compiled in ARM mode or in Thumb mode: If your code is ARM-only: Compile pealstub.c in ARM mode as usual. If your code is Thumb-only: Compile pealstub.c with `-mthumb -mcallee-super-interworking`. The latter option is necessary to allow PceNativeCall() to call PealArmStub() without crashing. If your code is mixed ARM and Thumb: Compile pealstub.c with either `-mno-thumb -mthumb-interwork` (ARM mode) or `-mthumb -mcallee-super-interworking` (Thumb mode). COMPATIBLITY WITH OTHER COMPILER OPTIONS Peal can be used with ARM or Thumb code compiled with `-msingle-pic-base`. PealArmStub() sets up register R10 as the PIC register. This option is recommended because it reduces code size and relocation count. Peal cannot be used with `-msingle-pic-base -mpic-register=`, unless the register used is R10. If you need -mpic-register with some other value, modify PealArmStub() in arm/pealstub.c to use the register you choose. Peal cannot be used with default stack checking, because Peal sets the stack limit register (R10) as the PIC register. If you need stack checking, specify a register other than R10 as the stack limit, or modify PealArmStub() in arm/pealstub.c to use a different PIC register or no PIC register. POSTLINKER The Peal postlinker transforms compiled and linked ARM code into the relocatable format expected by Peal, and then optionally packages the code into Palm OS resources. peal-postlink expects a single input file that * is an ELF executable. This is the usual format for ARM code built for Palm. * is position-independent. Compile with `arm-palmos-gcc -fPIC ...`. * still contains relocation information. Link with `arm-palmos-ld --emit-relocs ...` or `arm-palmos-gcc -Wl,--emit-relocs`. peal-postlink generates output in two formats: a resource file with each ELF section in its own resource (`peal-postlink -s ...`), or an unpackaged ELF relocatable object file (`peal-postlink ...`). -s Generates a PRC-format resource file, with each ELF section in its own resource. Each code and data section must be less than 64 KB. See LARGE CODE above. "resID" is the first resource ID to use. A typical program will occupy resources resID..resID+9. The default resource type is 'armc'. This format matches the .ro or .prc formats used by build-prc. Use PealLoadFromResources(resType, resID) to load this code. (without -s) Generates a single ELF relocatable object file. Unlike -s, this format has no section size limit. However, if the entire file is larger than 64 KB, then it will not fit in a Palm OS resource. In this case, the binary must be split and reassembled in feature memory or on the heap. See LARGE CODE above. Use PealLoad(address) to load this code. Other options: -K Keeps only symbols listed in the given file, and strips the rest. By default, all non-static functions and data get a symbol name and a symbol table entry. This can occupy large amounts of memory. The `strip` command can remove unwanted entries, but it is also likely to remove information needed by Peal. The -K option is safer. Functions and data intended to be used by m68k code must have symbol table entries. These functions and data should be listed in the keep file. The symbol "PealArmStub" is required for Peal's operation and will not be stripped, even if it is not listed in the keep file. -o Sets the output file name. Typical file name extensions are ".ro" or ".prc" (when using -s), or ".bin" (when not using -s). If no -o option is given, peal-postlink overwrites the input file. -t With -s, sets the resource type for generated resources. This option is silently ignored when not using -s. -v Verbose output. Lists the sections and resources written. -V Prints peal-postlink version, copyright, and lack of warranty. peal-postlink silently removes any sections named ".disposn", ".comment", ".got.plt", ".debug*", and ".debu.*". Other sections are copied to the output file. Peal's relocatable image format is similar to an ELF-ARM relocatable object file, but it does not fully conform to any ELF ABI. API PealModule *PealLoad(void *mem) Loads ARM code and data from mem. If mem points into a relocatable block, that block must be locked before calling PealLoad() and must remain locked until after PealUnload(). Note that PealLoad() may modify *mem. If mem points into a resource or feature memory block, then mem must point to the beginning of the block and the memory must be writable with DmWrite(). Returns NULL if the load fails for any reason. Warning: if you do not call PealUnload() before your program exits, your program may crash with a handle lock overflow error when run more than 16 times. PealModule *PealLoadFromResources(DmTypeID type, DmResID baseID) Loads ARM code and data from resources of the given type, starting with the given resource ID. The resources should be numbered sequentially starting with baseID, one for the ELF header and section list plus one for each ELF section. This is the output format generated by `peal-postlink -s ...`. The resources are loaded with DmGetResource(). Some of the resources are kept open and locked until PealUnload() is called. Others are released immediately. The resources must be writable with DmWrite(). Returns NULL if the load fails for any reason. Warning: if you do not call PealUnload() before your program exits, your program may crash with a handle lock overflow error when run more than 16 times. void PealUnload(PealModule *m) Unloads ARM code and data previously loaded by PealLoad(). The module must not be used after this call. Warning: if you do not call PealUnload() before your program exits, your program may crash with a handle lock overflow error when run more than 16 times. void *PealLookupSymbol(PealModule *m, char *query) Returns the address of a named ARM function or variable in module m. Returns NULL if the module contains no such function or variable. A function can be called by passing this address to PealCall(). A variable can be read or written by dereferencing this address. If the named function is a Thumb function, the returned address is the function's address plus one. This address may be used as a function pointer in interworking-enabled ARM code. However the address may not be passed to PceNativeCall. Use PealCall instead. uint32_t PealCall(PealModule *m, void *addr, void *arg) Calls the function at addr in ARM module m, passing it the given arg. Returns the value returned by that function. The ARM function PealArmStub() is used to prepare ARM global state. The called function should take zero or one arguments. void PealCxxConstruct(PealModule *m) Calls C++ constructors if they have not been called already. By default, Peal calls C++ constructors (if any) during the first PealCall(). You can force C++ constructors to run earlier by calling PealCxxConstruct() directly. Calling PealCxxConstruct() multiple times is harmless. void PealCxxDestruct(PealModule *m) Calls C++ destructors if they have not been called already. By default, Peal calls C++ destructors (if any) during the PealUnload(). You can force C++ destructors to run earlier by calling PealCxxDestruct() directly. Calling PealCxxDestruct() multiple times is harmless. ALTERNATIVES If you don't like Peal, there are several other tools to help provide a more full-featured ARM environment on Palm OS: ARM Relocatable ELF Loader by Hilary Cheng. GPL license. http://hilary.e-fever.org/arm-elf/ elink by Brett Beebe. No license specified. http://home.comcast.net/~beebeb/palmos/elink.zip Roll your own, using Aaron Ardiri's instructions. http://news.palmos.com/read/messages?id=143901#143901 http://news.palmos.com/read/messages?id=130117#130117 PNOLoader in Metrowerks CodeWarrior 9 for Palm OS. http://www.metrowerks.com/ LICENSE Peal was written by Greg Parker , using ELF headers by David O'Brien and John Polstra. *** Copyright (c) 2004-2005 Greg Parker. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *** Copyright (c) 2001 David E. O'Brien Copyright (c) 1996-1998 John D. Polstra. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***