Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
1908 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
#include 
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
    {
3900 Serge 133
//      printf("  Unknown pseudo relocation protocol version %d.\n",
134
//             (int) v2_hdr->version);
1908 serge 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;
3900 Serge 179
//        printf("  Unknown pseudo relocation bit size %d.\n",
180
//           (int) (r->flags & 0xff));
1908 serge 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
}