PalmBinaryFile.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000, The University of Queensland
00003  * Copyright (C) 2001, Sun Microsystems, Inc
00004  *
00005  * See the file "LICENSE.TERMS" for information on usage and
00006  * redistribution of this file, and for a DISCLAIMER OF ALL
00007  * WARRANTIES.
00008  *
00009  */
00010 
00011 /* File: PalmBinaryFile.cc
00012  * Desc: This class loads a Palm Pilot .prc file. Derived from class BinaryFile
00013  */
00014 
00015 /*
00016  * 02 Feb 00 - Mike: Initial version
00017  * 24 Feb 00 - Mike: Support for system trapcalls
00018  * 17 Apr 00 - Mike: GetAppID(); find PilotMain with patterns
00019  * 16 Feb 01 - Nathan: removed util references
00020  * 01 Aug 01 - Mike: Changed GetGlobalPointerInfo to the new definition
00021  */
00022 
00023 #include <assert.h>
00024 #if defined(_MSC_VER) && _MSC_VER <= 1200
00025 #pragma warning(disable:4786)
00026 #endif
00027 
00028 #include "PalmBinaryFile.h"
00029 #include "palmsystraps.h"
00030 
00031 // Macro to convert a pointer to a Big Endian integer into a host integer
00032 #define UC(p) ((unsigned char*)p)
00033 #define UINT4(p) ((UC(p)[0] << 24) + (UC(p)[1] << 16) + (UC(p)[2] << 8) + \
00034     UC(p)[3])
00035 
00036 PalmBinaryFile::PalmBinaryFile()
00037     : m_pImage(0), m_pData(0)
00038 {
00039 }
00040 
00041 PalmBinaryFile::~PalmBinaryFile()
00042 {
00043     for (int i=0; i < m_iNumSections; i++)
00044         if (m_pSections[i].pSectionName != 0)
00045             delete [] m_pSections[i].pSectionName;
00046     if (m_pImage) {
00047         delete [] m_pImage;
00048     }
00049     if (m_pData) {
00050         delete [] m_pData;
00051     }
00052 }
00053 
00054 bool PalmBinaryFile::RealLoad(const char* sName)
00055 {
00056     FILE    *fp;
00057     char    buf[32];
00058 
00059     m_pFileName = sName;
00060 
00061     if ((fp = fopen(sName, "rb")) == NULL) {
00062         fprintf(stderr, "Could not open binary file %s\n", sName);
00063         return false;
00064     }
00065 
00066     fseek(fp, 0, SEEK_END);
00067     long size = ftell(fp);
00068 
00069     // Allocate a buffer for the image
00070     m_pImage = new unsigned char[size];
00071     if (m_pImage == 0) {
00072         fprintf(stderr, "Could not allocate %ld bytes for image\n", size);
00073         return false;
00074     }
00075     memset(m_pImage, size, 0);
00076 
00077     fseek(fp, 0, SEEK_SET);
00078     if (fread(m_pImage, 1, size, fp) != (unsigned)size) {
00079         fprintf(stderr, "Error reading binary file %s\n", sName);
00080         return false;
00081     }
00082 
00083     // Check type at offset 0x3C; should be "appl" (or "palm"; ugh!)
00084     if ((strncmp((char*)(m_pImage+0x3C), "appl", 4) != 0) &&
00085         (strncmp((char*)(m_pImage+0x3C), "panl", 4) != 0) &&
00086         (strncmp((char*)(m_pImage+0x3C), "libr", 4) != 0)) {
00087         fprintf(stderr, "%s is not a standard .prc file\n", sName);
00088         return false;
00089     }
00090 
00091     // Get the number of resource headers (one section per resource)
00092     m_iNumSections = (m_pImage[0x4C] << 8) + m_pImage[0x4D];
00093 
00094     // Allocate the section information
00095     m_pSections = new SectionInfo[m_iNumSections];
00096     if (m_pSections == 0) {
00097         fprintf(stderr, "Could not allocate section info array of %d items\n",
00098             m_iNumSections);
00099         if (m_pImage) {
00100             delete m_pImage;
00101             m_pImage = 0;
00102         }
00103     }
00104 
00105     // Iterate through the resource headers (generating section info structs)
00106     unsigned char* p = m_pImage + 0x4E;          // First resource header
00107     unsigned off = 0;
00108     for (int i=0; i < m_iNumSections; i++) {
00109         // First get the name (4 alpha)
00110         strncpy(buf, (char*)p, 4);
00111         buf[4] = '\0';
00112         std::string name(buf);
00113         // Now get the identifier (2 byte binary)
00114         unsigned id = (p[4] << 8) + p[5];
00115         sprintf(buf, "%d", id);
00116         // Join the id to the name, e.g. code0, data12
00117         name += buf;
00118         m_pSections[i].pSectionName = new char[name.size()+1];
00119         strcpy(m_pSections[i].pSectionName, name.c_str());
00120         p += 4+2;
00121         off = UINT4(p);
00122         p += 4;
00123         m_pSections[i].uNativeAddr = off;
00124         m_pSections[i].uHostAddr = off + (ADDRESS)m_pImage;
00125 
00126         // Guess the length
00127         if (i > 0) {
00128             m_pSections[i-1].uSectionSize = off - m_pSections[i-1].uNativeAddr;
00129             m_pSections[i].uSectionEntrySize = 1;        // No info available
00130         }
00131 
00132         // Decide if code or data; note that code0 is a special case (not code)
00133         m_pSections[i].bCode =
00134           (name != "code0") && (name.substr(0, 4) == "code");
00135         m_pSections[i].bData = name.substr(0, 4) == "data";
00136 
00137     }
00138         
00139     // Set the length for the last section
00140     m_pSections[m_iNumSections-1].uSectionSize = size - off;
00141 
00142     // Create a separate, uncompressed, initialised data section
00143     SectionInfo* pData = GetSectionInfoByName("data0");
00144     if (pData == 0) {
00145         fprintf(stderr, "No data section!\n");
00146         return false;
00147     }
00148 
00149     SectionInfo* pCode0 = GetSectionInfoByName("code0");
00150     if (pCode0 == 0) {
00151         fprintf(stderr, "No code 0 section!\n");
00152         return false;
00153     }
00154 
00155     // When the info is all boiled down, the two things we need from the
00156     // code 0 section are at offset 0, the size of data above a5, and at
00157     // offset 4, the size below. Save the size below as a member variable
00158     m_SizeBelowA5 = UINT4(pCode0->uHostAddr+4);
00159     // Total size is this plus the amount above (>=) a5
00160     unsigned sizeData = m_SizeBelowA5 + UINT4(pCode0->uHostAddr);
00161 
00162     // Allocate a new data section
00163     m_pData = new unsigned char[sizeData];
00164     if (m_pData == 0) {
00165         fprintf(stderr, "Could not allocate %u bytes for data section\n",
00166             sizeData);
00167     }
00168 
00169     // Uncompress the data. Skip first long (offset of CODE1 "xrefs")
00170     p = (unsigned char*)(pData->uHostAddr+4);
00171     int start = (int) UINT4(p);
00172     p += 4;
00173     unsigned char* q = (m_pData + m_SizeBelowA5 + start);
00174     bool done = false;
00175     while (!done && (p < (unsigned char*)(pData->uHostAddr +
00176       pData->uSectionSize))) {
00177         unsigned char rle = *p++;
00178         if (rle == 0) {
00179             done = true;
00180             break;
00181         }
00182         else if (rle == 1) {
00183             // 0x01 b_0 b_1
00184             // => 0x00 0x00 0x00 0x00 0xFF 0xFF b_0 b_1
00185             *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0;
00186             *q++ = 0xFF; *q++ = 0xFF; *q++ = *p++; *q++ = *p++;
00187         }
00188         else if (rle == 2) {
00189             // 0x02 b_0 b_1 b_2
00190             // => 0x00 0x00 0x00 0x00 0xFF b_0 b_1 b_2
00191             *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0;
00192             *q++ = 0xFF; *q++ = *p++; *q++ = *p++; *q++ = *p++;
00193         }
00194         else if (rle == 3) {
00195             // 0x03 b_0 b_1 b_2
00196             // => 0xA9 0xF0 0x00 0x00 b_0 b_1 0x00 b_2
00197             *q++ = 0xA9; *q++ = 0xF0; *q++ = 0; *q++ = 0;
00198             *q++ = *p++; *q++ = *p++; *q++ = 0; *q++ = *p++; 
00199         }
00200         else if (rle == 4) {
00201             // 0x04 b_0 b_1 b_2 b_3
00202             // => 0xA9 axF0 0x00 b_0 b_1 b_3 0x00 b_3
00203             *q++ = 0xA9; *q++ = 0xF0; *q++ = 0; *q++ = *p++;
00204             *q++ = *p++; *q++ = *p++; *q++ = 0; *q++ = *p++;
00205         }
00206         else if (rle < 0x10) {
00207             // 5-0xF are invalid.
00208             assert(0);
00209         }
00210         else if (rle >= 0x80) {
00211             // n+1 bytes of literal data
00212             for (int k=0; k <= (rle-0x80); k++)
00213                 *q++ = *p++;
00214         }
00215         else if (rle >= 40) {
00216             // n+1 repetitions of 0
00217             for (int k=0; k <= (rle-0x40); k++)
00218                 *q++ = 0;
00219         }
00220         else if (rle >= 20) {
00221             // n+2 repetitions of b
00222             unsigned char b = *p++;
00223             for (int k=0; k < (rle-0x20+2); k++)
00224                 *q++ = b;
00225         }
00226         else {
00227             // 0x10: n+1 repetitions of 0xFF
00228             for (int k=0; k <= (rle-0x10); k++)
00229                 *q++ = 0xFF;
00230         }
00231     }
00232 
00233     if (!done)
00234         fprintf(stderr, "Warning! Compressed data section premature end\n");
00235 //printf("Used %u bytes of %u in decompressing data section\n",
00236 //p-(unsigned char*)pData->uHostAddr, pData->uSectionSize);
00237 
00238     // Replace the data pointer and size with the uncompressed versions
00239     pData->uHostAddr = (ADDRESS)m_pData;
00240     pData->uSectionSize = sizeData;
00241     // May as well make the native address zero; certainly the offset in the
00242     // file is no longer appropriate (and is confusing)
00243     pData->uNativeAddr = 0;
00244 
00245     return true;
00246 }
00247 
00248 void PalmBinaryFile::UnLoad()
00249 {
00250     if (m_pImage) {
00251         delete [] m_pImage;
00252         m_pImage = 0;
00253     }
00254 }
00255 
00256 // This is provided for completeness only...
00257 std::list<SectionInfo*>& PalmBinaryFile::GetEntryPoints(const char* pEntry
00258  /* = "main" */)
00259 {
00260     std::list<SectionInfo*>* ret = new std::list<SectionInfo*>;
00261     SectionInfo* pSect = GetSectionInfoByName("code1");
00262     if (pSect == 0)
00263         return *ret;               // Failed
00264     ret->push_back(pSect);
00265     return *ret;
00266 }
00267 
00268 ADDRESS PalmBinaryFile::GetEntryPoint()
00269 {
00270     assert(0); /* FIXME: Need to be implemented */
00271     return 0;
00272 }
00273 
00274 bool PalmBinaryFile::Open(const char* sName)
00275 {
00276     // Not implemented yet
00277     return false;
00278 }
00279 void PalmBinaryFile::Close()
00280 {
00281     // Not implemented yet
00282     return; 
00283 }
00284 bool PalmBinaryFile::PostLoad(void* handle)
00285 {
00286     // Not needed: for archives only
00287     return false;
00288 }
00289 
00290 LOAD_FMT PalmBinaryFile::GetFormat() const
00291 {
00292     return LOADFMT_PALM;
00293 }
00294 
00295 MACHINE PalmBinaryFile::GetMachine() const
00296 {
00297     return MACHINE_PALM;
00298 }
00299 
00300 bool PalmBinaryFile::isLibrary() const
00301 {
00302     return (strncmp((char*)(m_pImage+0x3C), "libr", 4) == 0);
00303 }
00304 std::list<const char *> PalmBinaryFile::getDependencyList()
00305 {
00306     return std::list<const char *>(); /* doesn't really exist on palm */
00307 }
00308 
00309 ADDRESS PalmBinaryFile::getImageBase()
00310 {
00311     return 0; /* FIXME */
00312 }
00313 
00314 size_t PalmBinaryFile::getImageSize()
00315 {
00316     return 0; /* FIXME */
00317 }
00318 
00319 // We at least need to be able to name the main function and system calls
00320 const char* PalmBinaryFile::SymbolByAddress(ADDRESS dwAddr)
00321 {
00322     if ((dwAddr & 0xFFFFF000) == 0xAAAAA000) {
00323         // This is the convention used to indicate an A-line system call
00324         unsigned offset = dwAddr & 0xFFF;
00325         if (offset < numTrapStrings)
00326             return trapNames[offset];
00327         else
00328             return 0;
00329     }
00330     if (dwAddr == GetMainEntryPoint())
00331         return "PilotMain";
00332     else return 0;
00333 }
00334 
00335 // Not really dynamically linked, but the closest thing
00336 bool PalmBinaryFile::IsDynamicLinkedProc(ADDRESS uNative)
00337 {
00338     return ((uNative & 0xFFFFF000) == 0xAAAAA000);
00339 }
00340 
00341 // Specific to BinaryFile objects that implement a "global pointer"
00342 // Gets a pair of unsigned integers representing the address of %agp,
00343 // and the value for GLOBALOFFSET. For Palm, the latter is the amount of
00344 // space allocated below %a5, i.e. the difference between %a5 and %agp
00345 // (%agp points to the bottom of the global data area).
00346 std::pair<unsigned,unsigned> PalmBinaryFile::GetGlobalPointerInfo()
00347 {
00348     unsigned agp = 0;
00349     const SectionInfo* ps = GetSectionInfoByName("data0");
00350     if (ps) agp = ps->uNativeAddr;
00351     std::pair<unsigned, unsigned> ret(agp, m_SizeBelowA5);
00352     return ret;
00353 }
00354 
00355 //  //  //  //  //  //  //
00356 //  Specific for Palm   //
00357 //  //  //  //  //  //  //
00358 
00359 int PalmBinaryFile::GetAppID() const
00360 {
00361     // The answer is in the header. Return 0 if file not loaded
00362     if (m_pImage == 0)
00363         return 0;
00364     // Beware the endianness (large)
00365 #define OFFSET_ID 0x40
00366     return (m_pImage[OFFSET_ID  ] << 24) + (m_pImage[OFFSET_ID+1] << 16) + 
00367            (m_pImage[OFFSET_ID+2] <<  8) + (m_pImage[OFFSET_ID+3]);
00368 }
00369 
00370 // Patterns for Code Warrior
00371 #define WILD 0x4AFC
00372 static SWord CWFirstJump[] = {
00373     0x0, 0x1,           // ? All Pilot programs seem to start with this
00374     0x487a, 0x4,        // pea 4(pc)
00375     0x0697, WILD, WILD, // addil #number, (a7)
00376     0x4e75};            // rts
00377 static SWord CWCallMain[] = {
00378     0x487a, 14,         // pea 14(pc)
00379     0x487a, 4,          // pea 4(pc)
00380     0x0697, WILD, WILD, // addil #number, (a7)
00381     0x4e75};            // rts
00382 static SWord GccCallMain[] = {
00383     0x3F04,             // movew d4, -(a7)
00384     0x6100, WILD,       // bsr xxxx
00385     0x3F04,             // movew d4, -(a7)
00386     0x2F05,             // movel d5, -(a7)
00387     0x3F06,             // movew d6, -(a7)
00388     0x6100, WILD};      // bsr PilotMain
00389     
00390 /*==============================================================================
00391  * FUNCTION:      findPattern
00392  * OVERVIEW:      Try to find a pattern
00393  * PARAMETERS:    start - pointer to code to start searching
00394  *                patt - pattern to look for
00395  *                pattSize - size of the pattern (in SWords)
00396  *                max - max number of SWords to search
00397  * RETURNS:       0 if no match; pointer to start of match if found
00398  *============================================================================*/
00399 SWord* findPattern(SWord* start, const SWord* patt, int pattSize, int max)
00400 {
00401     const SWord* last = start + max;
00402     for (; start < last; start++) {
00403         bool found = true;
00404         for (int i=0; i < pattSize; i++) {
00405             SWord curr = patt[i];
00406             if ((curr != WILD) && (curr != start[i])) {
00407                 found = false;
00408                 break;              // Mismatch
00409             }
00410         }
00411         if (found)
00412             // All parts of the pattern matched
00413             return start;
00414     }
00415     // Each start position failed
00416     return 0;
00417 }
00418 
00419 // Find the native address for the start of the main entry function.
00420 // For Palm binaries, this is PilotMain.
00421 ADDRESS PalmBinaryFile::GetMainEntryPoint()
00422 {
00423     SectionInfo* pSect = GetSectionInfoByName("code1");
00424     if (pSect == 0)
00425         return 0;               // Failed
00426     // Return the start of the code1 section
00427     SWord* startCode = (SWord*) pSect->uHostAddr;
00428     int delta = pSect->uHostAddr - pSect->uNativeAddr;
00429 
00430     // First try the CW first jump pattern
00431     SWord* res = findPattern(startCode, CWFirstJump,
00432         sizeof(CWFirstJump) / sizeof(SWord), 1);
00433     if (res) {
00434         // We have the code warrior first jump. Get the addil operand
00435         int addilOp = (startCode[5] << 16) + startCode[6];
00436         SWord* startupCode = (SWord*)((int)startCode + 10 + addilOp);
00437         // Now check the next 60 SWords for the call to PilotMain
00438         res = findPattern(startupCode, CWCallMain,
00439             sizeof(CWCallMain) / sizeof(SWord), 60);
00440         if (res) {
00441             // Get the addil operand
00442             addilOp = (res[5] << 16) + res[6];
00443             // That operand plus the address of that operand is PilotMain
00444             return (ADDRESS)res + 10 + addilOp - delta;
00445         }
00446         else {
00447             fprintf( stderr, "Could not find call to PilotMain in CW app\n" );
00448             return 0;
00449         }
00450     }
00451     // Check for gcc call to main
00452     res = findPattern(startCode, GccCallMain,
00453         sizeof(GccCallMain) / sizeof(SWord), 75);
00454     if (res) {
00455         // Get the operand to the bsr
00456         SWord bsrOp = res[7];
00457         return (ADDRESS)res + 14 + bsrOp - delta;
00458     }
00459 
00460     fprintf(stderr,"Cannot find call to PilotMain\n");
00461     return 0;
00462 }
00463 
00464 void PalmBinaryFile::GenerateBinFiles(const std::string& path) const
00465 {
00466     for (int i=0; i < m_iNumSections; i++) {
00467         SectionInfo* pSect = m_pSections + i;
00468         if ((strncmp(pSect->pSectionName, "code", 4) != 0) &&
00469             (strncmp(pSect->pSectionName, "data", 4) != 0)) {
00470             // Save this section in a file
00471             // First construct the file name
00472             char name[20];
00473             strncpy(name, pSect->pSectionName, 4);
00474             sprintf(name+4, "%04x.bin", atoi(pSect->pSectionName+4));
00475             std::string fullName(path);
00476             fullName += name;
00477             // Create the file
00478             FILE* f = fopen(fullName.c_str(), "w");
00479             if (f == NULL) {
00480                 fprintf( stderr, "Could not open %s for writing binary file\n",
00481                          fullName.c_str() );
00482                 return;
00483             }
00484             fwrite((void*)pSect->uHostAddr, pSect->uSectionSize, 1, f);
00485             fclose(f);
00486         }
00487    }
00488 } 
00489 
00490 // This function is called via dlopen/dlsym; it returns a new BinaryFile
00491 // derived concrete object. After this object is returned, the virtual function
00492 // call mechanism will call the rest of the code in this library
00493 // It needs to be C linkage so that it its name is not mangled
00494 extern "C" {
00495 #ifdef _WIN32
00496     __declspec(dllexport)
00497 #endif
00498     BinaryFile* construct()
00499     {
00500         return new PalmBinaryFile;
00501     }    
00502 }

Generated on Tue Sep 19 21:18:25 2006 for Boomerang by  doxygen 1.4.6