Subversion Repositories Kolibri OS

Rev

Rev 4349 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /* pseudo-reloc.c
  2.  
  3.    Contributed by Egor Duda  <deo@logos-m.ru>
  4.    Modified by addition of runtime_pseudo_reloc version 2
  5.    by Kai Tietz  <kai.tietz@onevision.com>
  6.  
  7.    THIS SOFTWARE IS NOT COPYRIGHTED
  8.  
  9.    This source code is offered for use in the public domain. You may
  10.    use, modify or distribute it freely.
  11.  
  12.    This code is distributed in the hope that it will be useful but
  13.    WITHOUT ANY WARRANTY. ALL WARRENTIES, EXPRESS OR IMPLIED ARE HEREBY
  14.    DISCLAMED. This includes but is not limited to warrenties of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. */
  17.  
  18. #include <stdint.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <stdarg.h>
  22.  
  23. extern char __RUNTIME_PSEUDO_RELOC_LIST__;
  24. extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
  25. extern char _image_base__;
  26.  
  27. void _pei386_runtime_relocator (void);
  28.  
  29. /* v1 relocation is basically:
  30.  *   *(base + .target) += .addend
  31.  * where (base + .target) is always assumed to point
  32.  * to a DWORD (4 bytes).
  33.  */
  34. typedef struct {
  35.   uint32_t addend;
  36.   uint32_t target;
  37. } runtime_pseudo_reloc_item_v1;
  38.  
  39. /* v2 relocation is more complex. In effect, it is
  40.  *    *(base + .target) += *(base + .sym) - (base + .sym)
  41.  * with care taken in both reading, sign extension, and writing
  42.  * because .flags may indicate that (base + .target) may point
  43.  * to a BYTE, WORD, DWORD, or QWORD (w64).
  44.  */
  45. typedef struct {
  46.   uint32_t sym;
  47.   uint32_t target;
  48.   uint32_t flags;
  49. } runtime_pseudo_reloc_item_v2;
  50.  
  51. typedef struct {
  52.   uint32_t magic1;
  53.   uint32_t magic2;
  54.   uint32_t version;
  55. } runtime_pseudo_reloc_v2;
  56.  
  57. #define RP_VERSION_V1 0
  58. #define RP_VERSION_V2 1
  59.  
  60. static void
  61. do_pseudo_reloc (void * start, void * end, void * base)
  62. {
  63.   ptrdiff_t addr_imp, reldata;
  64.   ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start);
  65.   runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start;
  66.   runtime_pseudo_reloc_item_v2 *r;
  67.  
  68.   /* A valid relocation list will contain at least one entry, and
  69.    * one v1 data structure (the smallest one) requires two DWORDs.
  70.    * So, if the relocation list is smaller than 8 bytes, bail.
  71.    */
  72.   if (reloc_target < 8)
  73.     return;
  74.  
  75.   /* Check if this is the old pseudo relocation version.  */
  76.   /* There are two kinds of v1 relocation lists:
  77.    *   1) With a (v2-style) version header. In this case, the
  78.    *      first entry in the list is a 3-DWORD structure, with
  79.    *      value:
  80.    *         { 0, 0, RP_VERSION_V1 }
  81.    *      In this case, we skip to the next entry in the list,
  82.    *      knowing that all elements after the head item can
  83.    *      be cast to runtime_pseudo_reloc_item_v1.
  84.    *   2) Without a (v2-style) version header. In this case, the
  85.    *      first element in the list IS an actual v1 relocation
  86.    *      record, which is two DWORDs.  Because there will never
  87.    *      be a case where a v1 relocation record has both
  88.    *      addend == 0 and target == 0, this case will not be
  89.    *      confused with the prior one.
  90.    * All current binutils, when generating a v1 relocation list,
  91.    * use the second (e.g. original) form -- that is, without the
  92.    * v2-style version header.
  93.    */
  94.   if (reloc_target >= 12
  95.       && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0
  96.       && v2_hdr->version == RP_VERSION_V1)
  97.     {
  98.       /* We have a list header item indicating that the rest
  99.        * of the list contains v1 entries.  Move the pointer to
  100.        * the first true v1 relocation record.  By definition,
  101.        * that v1 element will not have both addend == 0 and
  102.        * target == 0 (and thus, when interpreted as a
  103.        * runtime_pseudo_reloc_v2, it will not have both
  104.        * magic1 == 0 and magic2 == 0).
  105.        */
  106.       v2_hdr++;
  107.     }
  108.  
  109.   if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0)
  110.     {
  111.       /*************************
  112.        * Handle v1 relocations *
  113.        *************************/
  114.       runtime_pseudo_reloc_item_v1 * o;
  115.       for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr;
  116.            o < (runtime_pseudo_reloc_item_v1 *)end;
  117.            o++)
  118.         {
  119.       uint32_t newval;
  120.           reloc_target = (ptrdiff_t) base + o->target;
  121.       newval = (*((uint32_t*) reloc_target)) + o->addend;
  122.       *(uint32_t*)reloc_target = newval;
  123.         }
  124.       return;
  125.     }
  126.  
  127.   /* If we got this far, then we have relocations of version 2 or newer */
  128.  
  129.   /* Check if this is a known version.  */
  130.   if (v2_hdr->version != RP_VERSION_V2)
  131.     {
  132. //      printf("  Unknown pseudo relocation protocol version %d.\n",
  133. //             (int) v2_hdr->version);
  134.       return;
  135.     }
  136.  
  137.   /*************************
  138.    * Handle v2 relocations *
  139.    *************************/
  140.  
  141.   /* Walk over header. */
  142.   r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
  143.  
  144.   for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
  145.     {
  146.       /* location where new address will be written */
  147.       reloc_target = (ptrdiff_t) base + r->target;
  148.  
  149.       /* get sym pointer. It points either to the iat entry
  150.        * of the referenced element, or to the stub function.
  151.        */
  152.       addr_imp = (ptrdiff_t) base + r->sym;
  153.       addr_imp = *((ptrdiff_t *) addr_imp);
  154.  
  155.       /* read existing relocation value from image, casting to the
  156.        * bitsize indicated by the 8 LSBs of flags. If the value is
  157.        * negative, manually sign-extend to ptrdiff_t width. Raise an
  158.        * error if the bitsize indicated by the 8 LSBs of flags is not
  159.        * supported.
  160.        */
  161.       switch ((r->flags & 0xff))
  162.         {
  163.           case 8:
  164.             reldata = (ptrdiff_t) (*((unsigned char *)reloc_target));
  165.             if ((reldata & 0x80) != 0)
  166.               reldata |= ~((ptrdiff_t) 0xff);
  167.             break;
  168.           case 16:
  169.             reldata = (ptrdiff_t) (*((unsigned short *)reloc_target));
  170.             if ((reldata & 0x8000) != 0)
  171.               reldata |= ~((ptrdiff_t) 0xffff);
  172.             break;
  173.           case 32:
  174.             reldata = (ptrdiff_t) (*((unsigned int *)reloc_target));
  175.             break;
  176.           default:
  177.             reldata=0;
  178. //        printf("  Unknown pseudo relocation bit size %d.\n",
  179. //           (int) (r->flags & 0xff));
  180.             break;
  181.         }
  182.  
  183.       /* Adjust the relocation value */
  184.       reldata -= ((ptrdiff_t) base + r->sym);
  185.       reldata += addr_imp;
  186.  
  187.       /* Write the new relocation value back to *reloc_target */
  188.       switch ((r->flags & 0xff))
  189.         {
  190.          case 8:
  191.            *(uint8_t*)reloc_target = (uint8_t)reldata;
  192.            break;
  193.          case 16:
  194.            *(uint16_t*)reloc_target = (uint16_t)reldata;
  195.            break;
  196.          case 32:
  197.            *(uint32_t*)reloc_target = (uint32_t)reldata;
  198.            break;
  199.         }
  200.      }
  201. }
  202.  
  203. void
  204. _pei386_runtime_relocator (void)
  205. {
  206.   static int was_init = 0;
  207.   if (was_init)
  208.     return;
  209.   ++was_init;
  210.   do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
  211.                    &__RUNTIME_PSEUDO_RELOC_LIST_END__,
  212.            &_image_base__);
  213. }
  214.