📄 peal.c
字号:
/********** * Copyright (c) 2004 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. **********/#include <PalmOS.h>#include <PalmOSGlue.h>#include <PceNativeCall.h>#include <stdint.h>#include <stddef.h>#include "peal.h"#include "elf.h"#define FTRID_STEP 64
typedef struct { uint32_t vm_addr; uint32_t block; MemHandle resource; Boolean is_ftr;
Boolean is_lib;} SectionInfo;struct PealModule {
size_t size;
Boolean is_ftr;
Boolean is_memsemaphore;
const Elf32_Ehdr *ehdr;
UInt32 prgId;
UInt16 ftrId;
int symcount; const Elf32_Sym *syms; // real address of .symtab section const char *strings; // real address of .strtab section uintptr_t got; // real address of .got section uintptr_t rw_block; // heap block containing rw_start (unaligned) uintptr_t rw_start; // real address of all r/w memory (aligned) uintptr_t stub; // PealArmStub()
SectionInfo sinfo[1];// real addresses of each section
};// Must match struct PealArgs in arm/pealstub.ctypedef struct { void *fn; void *arg; void *got;} PealArgs;static inline uint32_t swap32(uint32_t v){ return ( ((v & 0x000000ff) << 24) | ((v & 0x0000ff00) << 8) | ((v & 0x00ff0000) >> 8) | ((v & 0xff000000) >> 24) );}static inline uint16_t swap16(uint16_t v){ return ( ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8) );}static inline void swap_ehdr(Elf32_Ehdr *ehdr){ ehdr->e_type = swap16(ehdr->e_type); ehdr->e_machine = swap16(ehdr->e_machine); ehdr->e_version = swap32(ehdr->e_version); ehdr->e_entry = swap32(ehdr->e_entry); ehdr->e_phoff = swap32(ehdr->e_phoff); ehdr->e_shoff = swap32(ehdr->e_shoff); ehdr->e_flags = swap32(ehdr->e_flags); ehdr->e_ehsize = swap16(ehdr->e_ehsize); ehdr->e_phentsize = swap16(ehdr->e_phentsize); ehdr->e_phnum = swap16(ehdr->e_phnum); ehdr->e_shentsize = swap16(ehdr->e_shentsize); ehdr->e_shnum = swap16(ehdr->e_shnum); ehdr->e_shstrndx = swap16(ehdr->e_shstrndx);}static uint16_t peek16(uintptr_t block, uintptr_t dst){ block = block; return swap16(*(uint16_t *)dst);}static uint32_t peek32(uintptr_t block, uintptr_t dst){ block = block; return swap32(*(uint32_t *)dst);}static void poke16(PealModule *m,uintptr_t block, uintptr_t dst, uint16_t value){ ptrdiff_t offset = dst - block; value = swap16(value);
if (*(uint16_t *)dst != value)
{
if (!m->is_memsemaphore && errNone == DmWriteCheck((void *)block, offset, sizeof(value))) { DmWrite((void *)block, offset, &value, sizeof(value)); } else { *(uint16_t *)dst = value; }
}}static void poke32(PealModule *m,uintptr_t block, uintptr_t dst, uint32_t value){ ptrdiff_t offset = dst - block; value = swap32(value);
if (*(uint32_t *)dst != value)
{
if (!m->is_memsemaphore && errNone == DmWriteCheck((void *)block, offset, sizeof(value))) { DmWrite((void *)block, offset, &value, sizeof(value)); } else { *(uint32_t *)dst = value; }
}}static const Elf32_Shdr *section_for_index(const Elf32_Ehdr *ehdr, uint16_t index){ Elf32_Shdr *shdr; if (index >= swap16(ehdr->e_shnum)) return NULL; shdr = (Elf32_Shdr *)(((uint8_t *)ehdr)+swap32(ehdr->e_shoff)); return shdr+index;}static const char *section_name(const PealModule *m, const Elf32_Shdr *shdr){ // NOT m->strings! const char *strings = (const char *)m->sinfo[swap16(m->ehdr->e_shstrndx)].vm_addr; return strings + swap32(shdr->sh_name);}
static const char *symbol_name(const PealModule *m, const Elf32_Sym *sym)
{
return m->strings + swap32(sym->st_name);
}
static const Elf32_Sym *symbol_lookup(const PealModule *m, const char *query, int hint)
{
const Elf32_Sym *end;
const Elf32_Sym *sym;
int i;
if (!m->syms || !m->strings || !query) return NULL; // sorry
if (hint >= m->symcount)
hint = m->symcount-1;
sym = m->syms+hint;
i = StrCompareAscii(symbol_name(m, sym),query);
if (i==0)
return sym;
// symtab has to be ordered by peal-postlink!
if (i<0)
{
end = m->syms+m->symcount;
for (++sym;sym<end;++sym)
if (StrCompareAscii(symbol_name(m, sym),query)==0)
return sym;
}
else
{
end = m->syms;
for (--sym;sym>=end;--sym)
if (StrCompareAscii(symbol_name(m, sym),query)==0)
return sym;
}
return NULL;
}
// ELF file behaves like a .o, so st_value is an in-section offset#define thumb 1static uintptr_t symbol_address(const PealModule *m, const Elf32_Sym *sym, int interwork, const PealModule *common){
uintptr_t address;
int index = swap16(sym->st_shndx);
if (m->sinfo[index].is_lib)
{
if (!common) return 0;
sym = symbol_lookup(common, symbol_name(m,sym), swap32(sym->st_value) >> 2);
if (!sym) return 0;
m = common;
index = swap16(sym->st_shndx);
}
address = (uintptr_t)(m->sinfo[index].vm_addr + swap32(sym->st_value)); if (interwork && ELF32_ST_TYPE(sym->st_info) == STT_LOPROC) { // symbol is a pointer to a Thumb function - use interworkable address address++; } return address;
}/* thunks from Thumb code: BL: thunk is Thumb, jumps to Thumb BLX: thunk is ARM, jumps to ARM thunks from ARM code: Bcc: thunk is ARM, jumps to ARM BLcc: thunk is ARM, jumps to ARM BLX: thunk is Thumb, jumps to Thumb*/// 1: thumb thunk// 0: arm thunk// -1: bad srcstatic int choose_thunk(uintptr_t src, Boolean srcIsThumb){ if (srcIsThumb) { uint16_t insn1 = swap16(*(uint16_t *)src); uint16_t insn2 = swap16(*(uint16_t *)(src+2)); uint16_t opcode1 = insn1 >> 11; uint16_t opcode2 = insn2 >> 11; if (opcode1 == 0x001e && opcode2 == 0x001f) { // BL: Thumb->Thumb branch, use Thumb thunk return thumb; } else if (opcode1 == 0x001e && opcode2 == 0x001d) { // BLX: Thumb->ARM branch, use ARM thunk return 0; } else { // unknown instruction return -1; } } else { uint32_t insn = swap32(*(uint32_t *)src); uint32_t cond = (insn & 0xf0000000) >> 28; uint32_t opcode = (insn & 0x0e000000) >> 25; if (opcode == 0x05) { if (cond == 0x0f) { // BLX: ARM->Thumb branch, use Thumb thunk return thumb; } else { // Bcc, BLcc: ARM->ARM branch, use ARM thunk return 0; } } else { // unknown instruction return -1; } }}/* Thumb thunk * 16 bytes total * must be 4-byte aligned with leading NOP to handle ARM BLX * data value is dst+1 so BX stays in Thumb mode * for LDR, the PC is already 4 bytes ahead, and the immediate must be 4-byte aligned. code (little-endian): 00 46C0 NOP 02 B401 PUSH r0 04 4801 LDR r0, [PC, #4] 06 4684 MOV r12, r0 08 BC01 POP r0 0a 4760 BX r12 data: 0c 4-byte dst+1 (needs +1 so BX stays in Thumb mode)*//* ARM thunk * 8 bytes total * for LDR, the PC is already 8 bytes ahead * `LDR PC, ...` on ARMv5T will switch to Thumb mode if dest bit 0 is set, but that should never happen here. code (big-endian): 00 E51FF004 LDR PC, [PC, #-4] data: 04 4-byte dst */typedef struct { uintptr_t thunk; uintptr_t dst;} thunk_desc_t;// thunkList[0] is ARM, thunkList[1] is Thumbstatic thunk_desc_t *thunkList[2] = {NULL, NULL};static unsigned int thunksUsed[2] = {0, 0};static unsigned int thunksAllocated[2] = {0, 0};// arch 1 == Thumb, arch 0 == ARMstatic thunk_desc_t *find_thunk_desc(uintptr_t dst, int arch){ thunk_desc_t *desc; unsigned int i; // Search for existing thunk. for (i = 0; i < thunksUsed[arch]; i++) { desc = &thunkList[arch][i]; if (desc->dst == dst) return desc; } // No match. Make new thunk descriptor. if (thunksUsed[arch] == thunksAllocated[arch]) { thunk_desc_t *newList; thunksAllocated[arch] = thunksAllocated[arch] * 2 + 8; newList = MemPtrNew(thunksAllocated[arch] * sizeof(thunk_desc_t)); if (!newList) return NULL; MemSet(newList, thunksAllocated[arch] * sizeof(thunk_desc_t), 0); if (thunkList[arch]) { MemMove(newList, thunkList[arch], thunksUsed[arch] * sizeof(thunk_desc_t)); MemPtrFree(thunkList[arch]); } thunkList[arch] = newList; } desc = &thunkList[arch][thunksUsed[arch]++]; desc->thunk = 0; desc->dst = dst; return desc;}static void free_thunk_descs(void){ if (thunkList[0]) MemPtrFree(thunkList[0]); if (thunkList[1]) MemPtrFree(thunkList[1]); thunkList[0] = thunkList[1] = NULL; thunksUsed[0] = thunksUsed[1] = 0; thunksAllocated[0] = thunksAllocated[1] = 0;}static Boolean insn_is_bx(uint16_t insn){ uint16_t opcode = insn >> 7; // keep prefix+opcode+H1; discard H2+regs return (opcode == 0x8e); // 010001 11 0 x xxx xxx}static uintptr_t generate_thunk(PealModule *m,uintptr_t *thunks, uintptr_t src_block, uintptr_t src, uintptr_t dst, int srcIsThumb){ int thunkIsThumb; thunk_desc_t *desc; // Examine the instruction at *src to choose which thunk type we need. thunkIsThumb = choose_thunk(src, srcIsThumb); if (thunkIsThumb < 0) return 0;
// Look for an existing thunk with the same dst and instruction set, // and make a new one if necessary desc = find_thunk_desc(dst, thunkIsThumb); if (!desc) return 0; // out of memory if (desc->thunk == 0) { // New thunk. if (thunkIsThumb) { uint16_t target_insn = swap16(*(uint16_t *)dst); desc->thunk = (*thunks -= 16); if (insn_is_bx(target_insn)) { // Branch target insn at dst is another bx (e.g. call_via_rX // glue). Simply copy that instruction into the stub. // This is necessary for correctness - the Thumb thunk // interferes with call_via_ip - and also runs faster. // fixme need to handle BLX too? poke16(m,src_block, desc->thunk + 0, target_insn); } else { // Normal Thumb thunk. poke16(m,src_block, desc->thunk + 0, 0x46C0); // NOP poke16(m,src_block, desc->thunk + 2, 0xB401); // PUSH r0 poke16(m,src_block, desc->thunk + 4, 0x4801); // LDR r0,[PC,#4] poke16(m,src_block, desc->thunk + 6, 0x4684); // MOV r12, r0 poke16(m,src_block, desc->thunk + 8, 0xBC01); // POP r0 poke16(m,src_block, desc->thunk + 10, 0x4760); // BX r12 poke32(m,src_block, desc->thunk + 12, dst | 1); } } else { desc->thunk = (*thunks -= 8); poke32(m,src_block, desc->thunk + 0, 0xE51FF004); // LDR PC, [PC,#-4] poke32(m,src_block, desc->thunk + 4, dst); } }
return desc->thunk;}static PealModule *allocate(const Elf32_Ehdr *ehdr){ size_t size = sizeof(SectionInfo) * (swap16(ehdr->e_shnum)-1) + sizeof(PealModule); PealModule *m = MemPtrNew(size); if (!m) return NULL; // fixme sanity-check ehdr
MemSet(m, size, 0);
m->size = size; m->ehdr = ehdr; return m;}static void cleanup(PealModule *m){ free_thunk_descs();
if (m) {
unsigned int i, count = swap16(m->ehdr->e_shnum);
for (i = 0; i < count; i++) { if (m->sinfo[i].resource) { MemHandleUnlock(m->sinfo[i].resource); DmReleaseResource(m->sinfo[i].resource); } else if (m->sinfo[i].is_ftr) {
FtrPtrFree(m->prgId,m->ftrId+i);
} } if (m->rw_block) MemPtrFree((void *)m->rw_block);
if (m->is_ftr)
FtrPtrFree(m->prgId,m->ftrId-1);
else
MemPtrFree(m); }}static Boolean load(PealModule *m,const PealModule *common){ const Elf32_Shdr *shdr; const Elf32_Sym *stub_sym; uintptr_t rw_sh_addr; uintptr_t rw_sh_end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -