Subversion Repositories Kolibri OS

Rev

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