Logo Search packages:      
Sourcecode: eresi version File versions  Download package

alpha64.c

Go to the documentation of this file.
/**
 * @file alpha64.c
 * @ingroup libelfsh
** Alpha64.c for libelfsh
**
** Started on  Sat Feb 26 23:25:37 2005 jfv
**
** Skeleton cut & pasted from the sparc64 backend
**
**
** $Id: alpha64.c,v 1.7 2008-02-16 12:32:27 thor Exp $
**
*/
#include "libelfsh.h"



/**
 * Static hooking for Alpha64
 * @param sect elfsh section object
 * @param name the name of the section
 * @param sym the name of symbol
 * @param addr the address
 * @return
 */
00026 int   elfsh_cflow_alpha64(elfshobj_t  *sect,
                      char    *name,
                      elfsh_Sym     *sym,
                      eresi_Addr    addr)
{
  // uint32_t     cur;
  

  // opcodes to hijack
  // 0xc3 (br)
  // 0xe0 / 0xe1 (blbc)
  // 0xe4 / 0xe5 / 0xe6 / 0xe7 (beq)      (21bits of offset)
  // 0xf4 / 0xf5 / 0xf7 (bne)
  // 0x6a / 0x6b (jsr/jmp)
  // 0xe8 (blt)
  // 0xec / 0xed (ble)

  PROFILER_IN(__FILE__, __FUNCTION__, __LINE__);             

  PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, 
                "Unsupported Arch, ELF type, or OS", -1);
}






/** ALTPLT hijacking on ALPHA64 
 * On ALPHA we dont have any relative operands while those
 * bytes are copied.

 * However since we only have 32 bytes free in the first
 * PLT entry, we cannot insert this 12 instrs length code 
 * updating the first entry of .alt.plt by the first entry
 * of .plt at runtime (since this last is only filled at 
 * runtime).

 * Because alpha does not provide an instruction that only
 * load the 2 low bytes part of a register, we use different
 * register values depending on the addresses we need to
 * read/write. The only predictable register value is %gp
 * whoose value is always .got+0x8000

 * If we dont do this ALTPLT hijack, we cannot call the original 
 * function from the hook function that was setup in PLT.
 *
 * @param file elfshobj
 * @param symbol elfsh symbol sturcture
 * @param addr address
 * @return
 */
00078 int         elfsh_hijack_altplt_alpha64(elfshobj_t *file, 
                                  elfsh_Sym *symbol,
                                  eresi_Addr addr)
{
  eresi_Addr      result, result2;
  uint32_t      opcode[12];
  uint32_t      off;
  elfshsect_t   *altplt;
  elfshsect_t     *altpltprolog;
  elfshsect_t     *plt;
  elfshsect_t     *got;
  uint16_t  hi, low, hi2, low2;

  PROFILER_IN(__FILE__, __FUNCTION__, __LINE__);

  /* Regular checks */
  if (!FILE_IS_ALPHA64(file))
    PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "requested "
               "ELFSH_HIJACK_CPU_ALPHA while the elf file is not "
               "ALPHA\n", -1);
  
  got        = file->secthash[ELFSH_SECTION_GOT];
  plt          = file->secthash[ELFSH_SECTION_PLT];
  altplt       = file->secthash[ELFSH_SECTION_ALTPLT];
  altpltprolog = file->secthash[ELFSH_SECTION_ALTPLTPROLOG];
  if (!plt || !altplt || !got || !altpltprolog)
    PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, 
                  "Cannot find one of the PLT sections", 
                  -1);

  /* Compute offsets */
  result = plt->shdr->sh_addr - (got->shdr->sh_addr + 0x8000);
  off    = (uint32_t) result;
  hi     = (off & 0xFFFF0000) >> 16;
  low    = (off & 0xFFFF);
  result2 = altplt->shdr->sh_addr - (got->shdr->sh_addr + 0x8000);
  off    = (uint32_t) result2;
  hi2    = (off & 0xFFFF0000) >> 16;
  low2   = (off & 0xFFFF);
  
  /* 00 00 3d 24     ldah     t0,0(gp) */
  opcode[0] = 0x243d0000 | (uint16_t) hi;

  /* 00 00 3d 20     lda    t0,0(t0|gp) */
  opcode[1] = (((short) low > 0)  ? 0x20210000 : 0x203D0000) | (uint16_t) low;

  /* 00 00 7d 24     ldah     t2,0(gp) */
  opcode[2] = 0x247d0000 | (uint16_t) hi2;

  /* 00 00 7d 20     lda    t2,0(t2|gp) */
  opcode[3] = (((short) low2 > 0) ? 0x20630000 : 0x207d0000) | (uint16_t) low2;

  /* 02 14 64 40     addq    t2,0x20,t1 */
  opcode[4] = 0x40641402;

  /* 00 00 81 a4     ldq     t3,0(t0) */
  opcode[5] = 0xa4810000;

  /* 00 00 83 b4     stq     t3,0(t2) */
  opcode[6] = 0xb4830000;

  /* 03 14 61 40     addq    t2,0x8,t2 */
  opcode[7] = 0x40611403;

  /* 01 14 21 40     addq    t0,0x8,t0 */
  opcode[8] = 0x40211401;

  /* a0 0d 43 40     cmple   t1,t2,v0 */
  opcode[9] = 0x40430da0;

  /* fa ff 1f e4     beq     v0,120000ac4 <loop> */
  opcode[10] = 0xe41ffffa;

  /* 00 00 60 d0     bsr     t2,120000ae0 <loop+0x1c> */
  opcode[11] = 0xd0600000;

  elfsh_raw_write(file, altpltprolog->shdr->sh_offset, 
              opcode, sizeof(uint32_t) * 12);


  /* Last but not least : redirect .alt.plt + 0 on .alt.plt.prolog */
  off = ((-52) >> 2);
  off = (off & (~(0xffffffff << 20)));
  opcode[0] = 0xc0900000 | (uint32_t) off;
  elfsh_raw_write(file, altplt->shdr->sh_offset, 
              opcode, sizeof(uint32_t));

  PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0);
}


/**
 * PLT hijacking on ALPHA64 
 * @param file elfshobj
 * @param symbol elfsh symbol sturcture
 * @param addr address
 * @return
 */
00176 int         elfsh_hijack_plt_alpha64(elfshobj_t *file, 
                               elfsh_Sym *symbol,
                               eresi_Addr addr)
{
  int           foffset;
  uint32_t      opcode[3];
  uint32_t      off;
  uint16_t  hi, low;

  PROFILER_IN(__FILE__, __FUNCTION__, __LINE__);

  if (file->hdr->e_machine != EM_ALPHA && file->hdr->e_machine != EM_ALPHA_EXP)
      PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "requested "
                     "ELFSH_HIJACK_CPU_ALPHA while the elf file is not "
                     "ALPHA\n", -1);

  if (addr > symbol->st_value)
    {
      opcode[2] = 0xc3800000; /* forward br */
      off = ((addr - 4) - symbol->st_value - 8) >> 2;
    }
  else
    {
      opcode[2] = 0xc3900000; /* backward br */
      off = ((addr + 4) - symbol->st_value - 8) >> 2;
    }

  hi  = ((off << 2) & 0xFFFF0000) >> 16;
  low = ((off << 2) + 12) & 0xFFFF;
  off = (off & (~(0xffffffff << 20)));

  /* Set t12 to the address of the called function */
  opcode[0] = 0x277b0000 | (uint16_t) hi;
  opcode[1] = 0x237b0000 | (uint16_t) low;
  opcode[2] |= (uint32_t) off;

#if __DEBUG_REDIR__
  printf("[DEBUG_REDIR] hookaddr = " XFMT " mem2patch = " XFMT 
       "computed offset = %08X (real = %08X), final opcode = %08X \n",
         addr, symbol->st_value, off, (off << 2), opcode);
#endif
  
  foffset = elfsh_get_foffset_from_vaddr(file, symbol->st_value);

#if __BYTE_ORDER == __BIG_ENDIAN
  opcode[0] = swap32(opcode[0]);
  opcode[1] = swap32(opcode[1]);
  opcode[2] = swap32(opcode[2]);  
  elfsh_raw_write(file, foffset, opcode, sizeof(uint32_t) * 3);
#else
  elfsh_raw_write(file, foffset, opcode, sizeof(uint32_t) * 3);
#endif

  PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0);
}




/**
 * Find the address of modgot entry for this value 
 *
 * @param modgot
 * @param addr
 * @return
 */
00242 eresi_Addr  elfsh_modgot_find(elfshsect_t *modgot, eresi_Addr addr)
{
  unsigned int    idx;
  eresi_Addr      *cur;

  PROFILER_IN(__FILE__, __FUNCTION__, __LINE__);
  
  cur = elfsh_get_raw(modgot);
  for (idx = 0; idx < modgot->shdr->sh_size / sizeof(eresi_Addr); idx++)
    {

      //printf("[modgot_find] Compare entries %016lX - %016lX \n", 
      //     cur[idx], addr);

      if (cur[idx] == addr)
      {
        printf("returned success in modgot_find \n");
        PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (modgot->shdr->sh_addr + idx * sizeof(eresi_Addr)));
      }
    }
  PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0);
}

/**
 * Handle the mod.o.got section 
 *
 * @param infile
 * @param modrel
 * @return
 */
00272 elfshsect_t *elfsh_modgot_alpha64(elfshsect_t *infile, elfshsect_t *modrel)
{
  elfshsect_t     *modgot;
  elfshsect_t     *sctcur;
  elfshsect_t     *sect;
  elfsh_Rel *relcur;
  char            *buff;
  unsigned int    idx;
  unsigned int    gotidx;
  unsigned int    size;
  unsigned int    needed;
  char            *data;
  elfsh_Shdr      hdr;
  eresi_Addr      *got;
  elfsh_Sym *sym;
  char            *name;
  char            tmpname[BUFSIZ];

  PROFILER_IN(__FILE__, __FUNCTION__, __LINE__);

  /* Create or return existing mod.o.got section */
  XALLOC(__FILE__, __FUNCTION__, __LINE__,buff, strlen(modrel->parent->name) + 4 + 1, NULL);
  sprintf(buff, "%s.got", modrel->parent->name);
  modgot = elfsh_get_section_by_name(infile->parent, buff, 0, 0, 0);
  if (modgot)
    {
      XFREE(__FILE__, __FUNCTION__, __LINE__,buff);
      PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (modgot));
    }

  needed = 0;
  modgot = elfsh_create_section(buff);
  size = (IS_REL(modrel) ? sizeof(elfsh_Rel) : sizeof(elfsh_Rela));

  /* Allocate mod.o.got from the number of LITERAL relocations */
  for (sctcur = modrel->parent->sectlist; sctcur; sctcur = sctcur->next)
    if (sctcur->shdr->sh_type == SHT_REL || sctcur->shdr->sh_type == SHT_RELA)
      for (idx = 0; idx < sctcur->shdr->sh_size / size; idx++)
      {
        relcur = (IS_REL(modrel) ?
                (void *) ((elfsh_Rel  *) elfsh_get_raw(sctcur) + idx) : 
                (void *) ((elfsh_Rela *) elfsh_get_raw(sctcur) + idx));
        if (elfsh_get_reltype(relcur) == R_ALPHA_LITERAL)
          needed++;
      }


  printf("Size for modgot = %u entries \n", needed);

  /* 
  ** Inject section. Here we are sure that it was loaded in last 
  ** for this module and all ET_REL symbols have been injected.  
  */
  XALLOC(__FILE__, __FUNCTION__, __LINE__,data, needed * sizeof(eresi_Addr), NULL);
  hdr = elfsh_create_shdr(0, SHT_PROGBITS, SHF_WRITE | SHF_ALLOC,
                    0, 0, needed * sizeof(eresi_Addr), 0, 0, 0, 0);

  if (elfsh_insert_mapped_section(infile->parent, modgot, hdr,
                          data, ELFSH_DATA_INJECTION, 0) < 0)
    goto bad;

  modgot = elfsh_get_section_by_name(infile->parent, buff, NULL, NULL, NULL);
  if (modgot == NULL)
    goto bad;

  got = elfsh_get_raw(modgot);

  /* 
  **   Fill the mod.o.got with pointers to the real data
  **   A Real pain .. need to be modularized with the elfsh_relocate_etrel_section() code
  **   -may
  */
  gotidx = 0;
  for (sctcur = modrel->parent->sectlist; sctcur; sctcur = sctcur->next)
    if (sctcur->shdr->sh_type == SHT_REL || sctcur->shdr->sh_type == SHT_RELA)
      for (idx = 0; idx < sctcur->shdr->sh_size / size; idx++)
      {
        relcur = (IS_REL(modrel) ?
                (void *) ((elfsh_Rel  *) elfsh_get_raw(sctcur) + idx) :
                (void *) ((elfsh_Rela *) elfsh_get_raw(sctcur) + idx));
        
        /* Now there are many possible computations for the address */
        if (elfsh_get_reltype(relcur) == R_ALPHA_LITERAL)
          {

            sym = elfsh_get_symbol_from_reloc(sctcur->parent, relcur);
            name = elfsh_get_symname_from_reloc(sctcur->parent, relcur);
            if (!sym || !name)
            PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Symbol or Name not found for LITERAL", 
                       NULL);
            
            if (elfsh_get_symbol_bind(sym) != STB_LOCAL   && /* patch BeoS */
              (elfsh_get_symbol_type(sym) == STT_NOTYPE ||
               elfsh_get_symbol_link(sym) == SHN_COMMON))
            {
              
              sym = elfsh_get_metasym_by_name(infile->parent, name);
              if (sym == NULL)
                {
                  printf("missing sym = %s \n", name);
                  PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__,
                             " Cant find requested symbol in ET_EXEC\n", NULL);
                  }
              got[gotidx] = sym->st_value;            
            }
            
            else
            {
              
              /* Find target section in ET_REL */
              sect = elfsh_get_section_by_index(sctcur->parent, sym->st_shndx,
                                        NULL, NULL);
              if (sect == NULL)
                PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__,
                           "Cant find extracted section in ET_REL\n", NULL);
              
              /* Find corresponding inserted section in ET_EXEC */
              snprintf(tmpname, sizeof(tmpname), "%s%s", sctcur->parent->name, sect->name);
              sect = elfsh_get_section_by_name(infile->parent, tmpname, 0, 0, 0);
              if (sect == NULL)
                PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__,
                           "Cant find inserted section in ET_EXEC\n", NULL);
              
              /* Compute pointer value */
              got[gotidx] = sect->shdr->sh_addr + sym->st_value;
              
            }
            
            printf("MODGOT : found a literal and filling modgot with " XFMT "\n",
                 got[gotidx]);
            gotidx++;
          }
      }

  PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (modgot));
 bad:
  PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Bad return for modgot", NULL);
}




/**
 * Perform relocation on entry for ALPHA64 architecture.
 * Only the types that are needed for relocating ET_REL objects 
 * are filled, others might come on the need.
 * @param new
 * @param cur
 * @param dword
 * @param addr
 * @param mod
 * @return
 */
00425 int       elfsh_relocate_alpha64(elfshsect_t       *new,
                         elfsh_Rela        *cur,
                         eresi_Addr        *dword,
                         eresi_Addr        addr,
                         elfshsect_t         *mod)
{
  elfshsect_t     *modgot;
  eresi_Addr     result;
  int          retval;
  uint32_t  *dw;
  uint32_t  off;
  uint16_t  hi, low;
  eresi_Addr      val;


  PROFILER_IN(__FILE__, __FUNCTION__, __LINE__);

  printf("relocata_alpha64 Input addr = " XFMT "\n", addr);


#define ADD       (cur->r_addend)
#define BAS       (elfsh_get_object_baseaddr(new->parent))
#define PLA       (new->shdr->sh_addr + cur->r_offset) 
#define SYM       (addr)
#define     SETSYM(a) (addr = a, printf("SETSYM addr set to " XFMT "\n", a))
#define GP        (elfsh_get_sht_entry_by_name(new->parent, ".got")->sh_addr+0x8000)

#define VERIFY8(x)      (((x) & (0xFFFFFFFF <<  8)) ? 1 : 0)
#define VERIFY13(x)     (((x) & (0xFFFFFFFF << 13)) ? 1 : 0)
#define VERIFY16(x)     (((x) & (0xFFFFFFFF << 16)) ? 1 : 0)
#define VERIFY22(x)     (((x) & (0xFFFFFFFF << 22)) ? 1 : 0)
#define VERIFY30(x)     (((x) & (0xFFFFFFFF << 30)) ? 1 : 0)
#define VERIFY32(x)     (((x) & (0xFFFFFFFF <<  0)) ? 1 : 0)

#define TRUNCATE8(x)    ((x) & ~(0xFFFFFFFF <<  8)) 
#define TRUNCATE13(x)   ((x) & ~(0xFFFFFFFF << 13)) 
#define TRUNCATE16(x)   ((x) & ~(0xFFFFFFFF << 16))
#define TRUNCATE22(x)   ((x) & ~(0xFFFFFFFF << 22))
#define TRUNCATE30(x)   ((x) & ~(0xFFFFFFFF << 30))
#define TRUNCATE32(x)   (x)  /* XXX just assume its 32bit */ 

#define TRUNCATE16HI(x)   ((x) & ~(0xFFFFFFFF >> 16))

  /* We compute a 32bit dword offset for using with <= 32bit relocations */
  dw = (uint32_t *) dword; 
  retval = result = 0;
  
  switch (elfsh_get_reltype((elfsh_Rel *) cur))
    {
      /* cases ... */
    case R_ALPHA_NONE:
      printf ("R_ALPHA_NONE\n"); 
      break;
    case R_ALPHA_REFLONG:
      printf ("R_ALPHA_REFLONG\n"); 
      result = TRUNCATE32(addr);
      break;
    case R_ALPHA_REFQUAD:
      printf ("R_ALPHA_REFQUAD\n"); 
      result = addr;
      break;
    case R_ALPHA_GPREL32:
      printf ("R_ALPHA_GPREL32\n"); 
      // XXX
      break;
      
      
      /* 
       On alpha, each static object in the relocatable file that have
       an address filled by R_ALPHA_LITERAL has an entry in .got
       which is filled at compil time and loaded in a register
       at runtime using this relocation. The .got entry is supposed
       to contains the absolute address of the object. This is alpha 
       specific and we have to reconstruct this behavior for the injected
       module with a new section : mod.o.got
                                                  -mm
      */
    case R_ALPHA_LITERAL:

      printf ("R_ALPHA_LITERAL : GP displacement for symbol \n");
      
      modgot = elfsh_modgot_alpha64(new, mod);
      if (modgot == NULL)
      PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__,"modgot failed", -1);
      val = elfsh_modgot_find(modgot, addr);
      SETSYM(val);
      
      //elfsh_print_sectlist(new->parent, "MODGOT");
      
      if (SYM > GP)
      {
        result = SYM - GP;
        low = (uint16_t) result;
      }
      else
      {
        result = GP - SYM;
        low = (uint16_t) result;
        low = -low;
      }
      
      printf ("SYM    : " XFMT " \n", SYM);
      printf ("GP     : " XFMT " \n" , GP);
      printf ("low    : %hd      \n", low);
      
      // seems to be badly encoded
      *dw += low;
      PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0);
      
    case R_ALPHA_LITUSE:      
      printf ("R_ALPHA_LITUSE\n"); 
      // XXX
      break;
      
    case R_ALPHA_GPDISP:
      printf ("R_ALPHA_GPDISP : GP displacement for current addr \n"); 
      
      if (PLA < (GP))
      result = (GP) - PLA;
      else
      result = PLA  - (GP); 
      
      // The particular case of elfsh makes the high offset always 0
      // since the .got stay the same on function hijack and the injected
      // code is always mapped after the .got in a 16bits shift interval
      off = (uint32_t) result;
      hi  = 0;
      low = TRUNCATE16(off);
      
      if (PLA > (GP))
      low = -low;
      
      if (off > 0xFFFF)
      printf("[debug_alpha_reloc] warning : difference does not make in 16bits %08X \n", off);
      else
      {
        printf("[debug_alpha_reloc] offset = %08X low = %04X high = %04X \n", off, low, hi);
      }
      
      *(dw + (ADD / 4)) += low;
      PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (retval));
      
    case R_ALPHA_BRADDR:
      printf ("R_ALPHA_BRADDR\n"); 
      // XXX
      break;
    case R_ALPHA_HINT:
      printf ("R_ALPHA_HINT\n"); 
      // XXX
      
      break;
    case R_ALPHA_SREL16:
      printf ("R_ALPHA_SREL16\n"); 
      // XXX
      break;
    case R_ALPHA_SREL32:
      printf ("R_ALPHA_SREL32\n"); 
      // XXX
      break;
    case R_ALPHA_SREL64:
      printf ("R_ALPHA_SREL64\n"); 
      // XXX
      break;
    case R_ALPHA_OP_PUSH:
      printf ("R_ALPHA_OP_PUSH\n"); 
      // XXX
      break;
    case R_ALPHA_OP_STORE:
      printf ("R_ALPHA_OP_STORE\n"); 
      // XXX
      break;
    case R_ALPHA_OP_PSUB: 
      printf ("R_ALPHA_OP_PSUB\n"); 
      // XXX
      break;
    case R_ALPHA_OP_PRSHIFT:
      printf ("R_ALPHA_PRSHIFT\n"); 
      // XXX
      break;
    case R_ALPHA_GPVALUE:    
      printf ("R_ALPHA_GPVALUE\n"); 
      // XXX
      break;
    case R_ALPHA_GPRELHIGH:  
      printf ("R_ALPHA_GPRELHIGH\n"); 
      // XXX
      break;
    case R_ALPHA_GPRELLOW:    
      printf ("R_ALPHA_GPRELLOW\n"); 
      // XXX
      break;
    case R_ALPHA_IMMED_GP_16: 
      printf ("R_ALPHA_IMMED_GP_16\n"); 
      // XXX
      break;
    case R_ALPHA_IMMED_GP_HI32:
      printf ("R_ALPHA_IMMED_GP_HI32\n"); 
      // XXX
      break;
    case R_ALPHA_IMMED_SCN_HI32: 
      printf ("R_ALPHA_IMMED_SCN_HI32\n"); 
      // XXX
      break;
    case R_ALPHA_IMMED_BR_HI32:  
      printf ("R_ALPHA_IMMED_BR_HI32\n"); 
      // XXX
      break;
    case R_ALPHA_IMMED_LO32:  
      printf ("R_ALPHA_IMMED_LO32\n"); 
      // XXX
      break;
    case R_ALPHA_COPY:        
      printf ("R_ALPHA_COPY\n"); 
      // XXX
      break;
    case R_ALPHA_GLOB_DAT:    
      printf ("R_ALPHA_GLOB_DAT\n"); 
      // XXX
      break;
    case R_ALPHA_JMP_SLOT:    
      printf ("R_ALPHA_JMP_SLOT\n"); 
      // XXX
      break;
    case R_ALPHA_RELATIVE:    
      printf ("R_ALPHA_RELATIVE\n"); 
      // XXX
      break;

    default:
PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unsupported relocation type",
                 -1);
      break;
    }
  
  *dword += result;

#undef ADD
#undef BAS
#undef PLA
#undef SYM
#undef SETSYM
#undef GP
  
  PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (retval));
}


Generated by  Doxygen 1.6.0   Back to index