Subversion Repositories Kolibri OS

Rev

Rev 4349 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/* pseudo-reloc.c
2
 
3
   Contributed by Egor Duda  
4
   Modified by addition of runtime_pseudo_reloc version 2
5
   by Kai Tietz  
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 
19
#include 
20
#include 
21
#include 
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
}