home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 May / pcp151c.iso / misc / src / install / modutils / obj / obj_ppc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-01-06  |  5.7 KB  |  226 lines

  1. /* PowerPC specific support for Elf loading and relocation.
  2.    Copyright 1996, 1997 Linux International.
  3.  
  4.    Adapted by Paul Mackerras <paulus@cs.anu.edu.au> from the
  5.    obj-sparc.c and obj-alpha.c files.
  6.  
  7.    This file is part of the Linux modutils.
  8.  
  9.    This program is free software; you can redistribute it and/or modify it
  10.    under the terms of the GNU General Public License as published by the
  11.    Free Software Foundation; either version 2 of the License, or (at your
  12.    option) any later version.
  13.  
  14.    This program is distributed in the hope that it will be useful, but
  15.    WITHOUT ANY WARRANTY; without even the implied warranty of
  16.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.    General Public License for more details.
  18.  
  19.    You should have received a copy of the GNU General Public License
  20.    along with this program; if not, write to the Free Software Foundation,
  21.    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  22.  
  23. #ident "$Id: obj_ppc.c,v 1.1.1.1 1998/01/06 20:51:08 ewt Exp $"
  24.  
  25. #include <stddef.h>
  26. #include <module.h>
  27. #include <obj.h>
  28. #include <util.h>
  29. #include <assert.h>
  30.  
  31.  
  32. /*======================================================================*/
  33.  
  34. /*
  35.  * Unfortunately, the bl (branch-and-link) instruction used for
  36.  * procedure calls on the PowerPC can only reach +/- 32MB from the
  37.  * current instruction.  If the module is loaded far enough away from
  38.  * the main kernel text (or other modules) that this limit is
  39.  * exceeded, we have to redirect procedure calls via a procedure
  40.  * linkage table (PLT).  Each entry in the PLT contains instructions
  41.  * to put the address of the procedure in a register and jump to it.
  42.  */
  43.  
  44. typedef unsigned int instruction;    /* a powerpc instruction (4 bytes) */
  45.  
  46. struct ppc_plt_entry
  47. {
  48.   struct ppc_plt_entry *next;
  49.   ElfW(Addr) addend;
  50.   int offset;
  51.   int inited;
  52. };
  53.  
  54. struct ppc_file
  55. {
  56.   struct obj_file file;
  57.   struct obj_section *plt;
  58. };
  59.  
  60. struct ppc_symbol
  61. {
  62.   struct obj_symbol sym;
  63.   struct ppc_plt_entry *plt_entries;
  64. };
  65.  
  66. struct obj_file *
  67. arch_new_file (void)
  68. {
  69.   struct ppc_file *f;
  70.  
  71.   f = xmalloc(sizeof(struct ppc_file));
  72.   f->plt = NULL;
  73.   return &f->file;
  74. }
  75.  
  76. struct obj_section *
  77. arch_new_section (void)
  78. {
  79.   return xmalloc(sizeof(struct obj_section));
  80. }
  81.  
  82. struct obj_symbol *
  83. arch_new_symbol (void)
  84. {
  85.   struct ppc_symbol *p;
  86.  
  87.   p = xmalloc(sizeof(struct ppc_symbol));
  88.   p->plt_entries = NULL;
  89.   return &p->sym;
  90. }
  91.  
  92.  
  93. enum obj_reloc
  94. arch_apply_relocation (struct obj_file *ef,
  95.                struct obj_section *targsec,
  96.                struct obj_section *symsec,
  97.                struct obj_symbol *sym,
  98.                Elf32_Rela *rel,
  99.                Elf32_Addr v)
  100. {
  101.   Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset);
  102.   Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset;
  103.   struct ppc_file *pf = (struct ppc_file *) ef;
  104.   struct ppc_symbol *psym = (struct ppc_symbol *) sym;
  105.   struct ppc_plt_entry *pe;
  106.   instruction *ip;
  107.  
  108.   enum obj_reloc ret = obj_reloc_ok;
  109.  
  110.   switch (ELF32_R_TYPE(rel->r_info))
  111.     {
  112.     case R_PPC_ADDR16_HA:
  113.       *(unsigned short *)loc = (v + 0x8000) >> 16;
  114.       break;
  115.  
  116.     case R_PPC_ADDR16_HI:
  117.       *(unsigned short *)loc = v >> 16;
  118.       break;
  119.  
  120.     case R_PPC_ADDR16_LO:
  121.       *(unsigned short *)loc = v;
  122.       break;
  123.  
  124.     case R_PPC_REL24:
  125.       /* find the plt entry and initialize it if necessary */
  126.       assert(psym != NULL);
  127.       for (pe = psym->plt_entries; pe != NULL && pe->addend != rel->r_addend; )
  128.     pe = pe->next;
  129.       assert(pe != NULL);
  130.       if (!pe->inited)
  131.     {
  132.       ip = (instruction *) (pf->plt->contents + pe->offset);
  133.       ip[0] = 0x3d600000 + ((v + 0x8000) >> 16);  /* lis r11,sym@ha */
  134.       ip[1] = 0x396b0000 + (v & 0xffff);          /* addi r11,r11,sym@l */
  135.       ip[2] = 0x7d6903a6;                  /* mtctr r11 */
  136.       ip[3] = 0x4e800420;                  /* bctr */
  137.       pe->inited = 1;
  138.     }
  139.  
  140.       v -= dot;
  141.       if ((int)v < -0x02000000 || (int)v >= 0x02000000)
  142.     {
  143.       /* go via the plt */
  144.       v = pf->plt->header.sh_addr + pe->offset - dot;
  145.     }
  146.       if (v & 3)
  147.     ret = obj_reloc_dangerous;
  148.       *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
  149.       break;
  150.  
  151.     case R_PPC_ADDR32:
  152.       *loc = v;
  153.       break;
  154.  
  155.     default:
  156.       ret = obj_reloc_unhandled;
  157.       break;
  158.     }
  159.  
  160.   return ret;
  161. }
  162.  
  163. int
  164. arch_create_got (struct obj_file *f)
  165. {
  166.   struct ppc_file *pf = (struct ppc_file *) f;
  167.   int i, offset;
  168.   struct obj_section *sec, *syms, *strs;
  169.   ElfW(Rela) *rel, *relend;
  170.   ElfW(Sym) *symtab, *extsym;
  171.   const char *strtab, *name;
  172.   struct ppc_symbol *intsym;
  173.   struct ppc_plt_entry *pe;
  174.  
  175.   offset = 0;
  176.   for (i = 0; i < f->header.e_shnum; ++i)
  177.     {
  178.       sec = f->sections[i];
  179.       if (sec->header.sh_type != SHT_RELM)
  180.     continue;
  181.       syms = f->sections[sec->header.sh_link];
  182.       strs = f->sections[syms->header.sh_link];
  183.  
  184.       rel = (ElfW(RelM) *) sec->contents;
  185.       relend = rel + (sec->header.sh_size / sizeof(ElfW(RelM)));
  186.       symtab = (ElfW(Sym) *) syms->contents;
  187.       strtab = (const char *) strs->contents;
  188.  
  189.       for (; rel < relend; ++rel)
  190.     {
  191.       if (ELF32_R_TYPE(rel->r_info) != R_PPC_REL24)
  192.         continue;
  193.       extsym = &symtab[ELF32_R_SYM(rel->r_info)];
  194.       if (extsym->st_name)
  195.         name = strtab + extsym->st_name;
  196.       else
  197.         name = f->sections[extsym->st_shndx]->name;
  198.       intsym = (struct ppc_symbol *) obj_find_symbol(f, name);
  199.  
  200.       for (pe = intsym->plt_entries; pe != NULL; pe = pe->next)
  201.         if (pe->addend == rel->r_addend)
  202.           break;
  203.       if (pe == NULL)
  204.         {
  205.           pe = xmalloc(sizeof(struct ppc_plt_entry));
  206.           pe->next = intsym->plt_entries;
  207.           pe->addend = rel->r_addend;
  208.           pe->offset = offset;
  209.           pe->inited = 0;
  210.           intsym->plt_entries = pe;
  211.           offset += 16;
  212.         }
  213.     }
  214.     }
  215.  
  216.   pf->plt = obj_create_alloced_section(f, ".plt", 16, offset);
  217.  
  218.   return 1;
  219. }
  220.  
  221. int
  222. arch_init_module (struct obj_file *f, struct new_module *mod)
  223. {
  224.   return 1;
  225. }
  226.