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