1 /*
2 Title: Address scanner
3
4 Copyright (c) 2006-8, 2012, 2019 David C.J. Matthews
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
20 */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #elif defined(_WIN32)
24 #include "winconfig.h"
25 #else
26 #error "No configuration file"
27 #endif
28
29 #ifdef HAVE_ASSERT_H
30 #include <assert.h>
31 #define ASSERT(x) assert(x)
32 #else
33 #define ASSERT(x) 0
34 #endif
35
36 #include <new>
37
38 #include "globals.h"
39 #include "scanaddrs.h"
40 #include "machine_dep.h"
41 #include "diagnostics.h"
42 #include "memmgr.h"
43
44 // Process the value at a given location and update it as necessary.
ScanAddressAt(PolyWord * pt)45 POLYUNSIGNED ScanAddress::ScanAddressAt(PolyWord *pt)
46 {
47 PolyWord val = *pt;
48 PolyWord newVal = val;
49 if (IS_INT(val) || val == PolyWord::FromUnsigned(0))
50 {
51 // We can get zeros in the constant area if we garbage collect
52 // while compiling some code. */
53 }
54 else
55 {
56 ASSERT(OBJ_IS_DATAPTR(val));
57 // Any sort of address
58 newVal = ScanObjectAddress(val.AsObjPtr());
59 }
60 if (newVal != val) // Only update if we need to.
61 *pt = newVal;
62 return 0;
63 }
64
65 // General purpose object processor, Processes all the addresses in an object.
66 // Handles the various kinds of object that may contain addresses.
ScanAddressesInObject(PolyObject * obj,POLYUNSIGNED lengthWord)67 void ScanAddress::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED lengthWord)
68 {
69 do
70 {
71 ASSERT (OBJ_IS_LENGTH(lengthWord));
72
73 if (OBJ_IS_BYTE_OBJECT(lengthWord))
74 return; /* Nothing more to do */
75
76 POLYUNSIGNED length = OBJ_OBJECT_LENGTH(lengthWord);
77 PolyWord *baseAddr = (PolyWord*)obj;
78
79 if (OBJ_IS_CODE_OBJECT(lengthWord))
80 {
81 // Scan constants within the code.
82 machineDependent->ScanConstantsWithinCode(obj, obj, length, this);
83
84 // Skip to the constants and get ready to scan them.
85 obj->GetConstSegmentForCode(length, baseAddr, length);
86 // Adjust to the read-write area if necessary.
87 baseAddr = gMem.SpaceForAddress(baseAddr)->writeAble(baseAddr);
88 }
89
90 else if (OBJ_IS_CLOSURE_OBJECT(lengthWord))
91 {
92 // The first word is a code pointer so we need to treat it specially
93 // but it is possible it hasn't yet been set.
94 if ((*(uintptr_t*)baseAddr & 1) == 0)
95 {
96 POLYUNSIGNED lengthWord = ScanCodeAddressAt((PolyObject**)baseAddr); // N.B. This could side-effect *baseAddr
97 if (lengthWord != 0)
98 ScanAddressesInObject(*(PolyObject**)baseAddr, lengthWord);
99 }
100 baseAddr += sizeof(PolyObject*) / sizeof(PolyWord);
101 length -= sizeof(PolyObject*) / sizeof(PolyWord);
102 }
103
104 PolyWord *endWord = baseAddr + length;
105
106 // We want to minimise the actual recursion we perform so we try to
107 // use tail recursion if we can. We first scan from the end and
108 // remove any words that don't need recursion.
109 POLYUNSIGNED lastLengthWord = 0;
110 while (endWord != baseAddr)
111 {
112 PolyWord wordAt = endWord[-1];
113 if (IS_INT(wordAt) || wordAt == PolyWord::FromUnsigned(0))
114 endWord--; // Don't need to look at this.
115 else if ((lastLengthWord = ScanAddressAt(endWord-1)) != 0)
116 // We need to process this one
117 break;
118 else endWord--; // We're not interested in this.
119 }
120
121 if (endWord == baseAddr)
122 return; // We've done everything.
123
124 // There is at least one word that needs to be processed, the
125 // one at endWord-1.
126 // Now process from the beginning forward to see if there are
127 // any words before this that need to be handled. This way we are more
128 // likely to handle the head of a list by recursion and the
129 // tail by looping (tail recursion).
130 while (baseAddr < endWord-1)
131 {
132 PolyWord wordAt = *baseAddr;
133 if (IS_INT(wordAt) || wordAt == PolyWord::FromUnsigned(0))
134 baseAddr++; // Don't need to look at this.
135 else
136 {
137 POLYUNSIGNED lengthWord = ScanAddressAt(baseAddr);
138 if (lengthWord != 0)
139 {
140 wordAt = *baseAddr; // Reload because it may have been side-effected
141 // We really have to process this recursively.
142 ASSERT(wordAt.IsDataPtr());
143 ScanAddressesInObject(wordAt.AsObjPtr(), lengthWord);
144 baseAddr++;
145 }
146 else baseAddr++;
147 }
148 }
149
150 // Finally process the last word we found that has to be processed.
151 // Do this by looping rather than recursion.
152 PolyWord wordAt = *baseAddr; // Last word to do.
153 // This must be an address
154 ASSERT(wordAt.IsDataPtr());
155 obj = wordAt.AsObjPtr();
156
157 lengthWord = lastLengthWord;
158
159 } while(1);
160 }
161
ScanAddressesInRegion(PolyWord * region,PolyWord * end)162 void ScanAddress::ScanAddressesInRegion(PolyWord *region, PolyWord *end)
163 {
164 PolyWord *pt = region;
165 while (pt < end)
166 {
167 #ifdef POLYML32IN64
168 if ((((uintptr_t)pt) & 4) == 0)
169 {
170 // Skip any padding. The length word should be on an odd-word boundary.
171 pt++;
172 continue;
173 }
174 #endif
175 pt++; // Skip length word.
176 // pt actually points AT the object here.
177 PolyObject *obj = (PolyObject*)pt;
178 if (obj->ContainsForwardingPtr()) /* skip over moved object */
179 {
180 // We can now get multiple forwarding pointers as a result
181 // of applying ShareData repeatedly. Perhaps we should
182 // turn the forwarding pointers back into normal words in
183 // an extra pass.
184 obj = obj->FollowForwardingChain();
185 ASSERT(obj->ContainsNormalLengthWord());
186 pt += obj->Length();
187 }
188 else
189 {
190 ASSERT(obj->ContainsNormalLengthWord());
191 POLYUNSIGNED length = obj->Length();
192 if (pt+length > end)
193 Crash("Malformed object at %p - length %lu\n", pt, length);
194 if (length != 0)
195 ScanAddressesInObject(obj);
196 pt += length;
197 }
198 }
199 }
200
201 // Extract a constant from the code.
GetConstantValue(byte * addressOfConstant,ScanRelocationKind code,PolyWord * base)202 PolyObject *ScanAddress::GetConstantValue(byte *addressOfConstant, ScanRelocationKind code, PolyWord *base)
203 {
204 switch (code)
205 {
206 case PROCESS_RELOC_DIRECT: // Absolute address
207 {
208 uintptr_t valu;
209 byte *pt = addressOfConstant;
210 if (pt[sizeof(uintptr_t)-1] & 0x80) valu = 0-1; else valu = 0;
211 for (unsigned i = sizeof(uintptr_t); i > 0; i--)
212 valu = (valu << 8) | pt[i-1];
213 if (valu == 0 || PolyWord::FromUnsigned((POLYUNSIGNED)valu).IsTagged())
214 return 0;
215 else return (PolyObject*)valu;
216 }
217 case PROCESS_RELOC_I386RELATIVE: // 32 bit relative address
218 {
219 POLYSIGNED disp;
220 byte *pt = addressOfConstant;
221 // Get the displacement. This is signed.
222 if (pt[3] & 0x80) disp = -1; else disp = 0; // Set the sign just in case.
223 for(unsigned i = 4; i > 0; i--) disp = (disp << 8) | pt[i-1];
224 byte *absAddr = pt + disp + 4; // The address is relative to AFTER the constant
225 return (PolyObject*)absAddr;
226 }
227 default:
228 ASSERT(false);
229 return 0;
230 }
231 }
232
233 // Store a constant value. Also used with a patch table when importing a saved heap which has
234 // been exported using the C exporter.
SetConstantValue(byte * addressOfConstant,PolyObject * p,ScanRelocationKind code)235 void ScanAddress::SetConstantValue(byte *addressOfConstant, PolyObject *p, ScanRelocationKind code)
236 {
237 MemSpace* space = gMem.SpaceForAddress(addressOfConstant);
238 byte* addressToWrite = space->writeAble(addressOfConstant);
239 switch (code)
240 {
241 case PROCESS_RELOC_DIRECT: // Absolute address
242 {
243 uintptr_t valu = (uintptr_t)p;
244 for (unsigned i = 0; i < sizeof(uintptr_t); i++)
245 {
246 addressToWrite[i] = (byte)(valu & 255);
247 valu >>= 8;
248 }
249 }
250 break;
251 case PROCESS_RELOC_I386RELATIVE: // 32 bit relative address
252 {
253 // This offset may be positive or negative
254 intptr_t newDisp = (byte*)p - addressOfConstant - 4;
255 #if (SIZEOF_VOIDP != 4)
256 ASSERT(newDisp < (intptr_t)0x80000000 && newDisp >= -(intptr_t)0x80000000);
257 #endif
258 for (unsigned i = 0; i < 4; i++) {
259 addressToWrite[i] = (byte)(newDisp & 0xff);
260 newDisp >>= 8;
261 }
262 }
263 break;
264 }
265 }
266
ScanConstant(PolyObject * base,byte * addressOfConstant,ScanRelocationKind code)267 void ScanAddress::ScanConstant(PolyObject *base, byte *addressOfConstant, ScanRelocationKind code)
268 {
269 PolyObject *p = GetConstantValue(addressOfConstant, code);
270
271 if (p != 0)
272 {
273 PolyObject *oldValue = p;
274 // If this was a relative address we must have a code address.
275 if (code == PROCESS_RELOC_I386RELATIVE)
276 ScanCodeAddressAt(&p);
277 else p = ScanObjectAddress(p);
278 if (p != oldValue) // Update it if it has changed.
279 SetConstantValue(addressOfConstant, p, code);
280 }
281 }
282
ScanRuntimeWord(PolyWord * w)283 void ScanAddress::ScanRuntimeWord(PolyWord *w)
284 {
285 if (w->IsTagged()) {} // Don't need to do anything
286 else {
287 ASSERT(w->IsDataPtr());
288 *w = ScanObjectAddress(w->AsObjPtr());
289 }
290 }
291