|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "LulDwarfSummariser.h" |
|
8 |
|
9 #include "mozilla/Assertions.h" |
|
10 |
|
11 // Set this to 1 for verbose logging |
|
12 #define DEBUG_SUMMARISER 0 |
|
13 |
|
14 namespace lul { |
|
15 |
|
16 Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias, |
|
17 void(*aLog)(const char*)) |
|
18 : mSecMap(aSecMap) |
|
19 , mTextBias(aTextBias) |
|
20 , mLog(aLog) |
|
21 { |
|
22 mCurrAddr = 0; |
|
23 mMax1Addr = 0; // Gives an empty range. |
|
24 |
|
25 // Initialise the running RuleSet to "haven't got a clue" status. |
|
26 new (&mCurrRules) RuleSet(); |
|
27 } |
|
28 |
|
29 void |
|
30 Summariser::Entry(uintptr_t aAddress, uintptr_t aLength) |
|
31 { |
|
32 aAddress += mTextBias; |
|
33 if (DEBUG_SUMMARISER) { |
|
34 char buf[100]; |
|
35 snprintf(buf, sizeof(buf), "LUL Entry(%llx, %llu)\n", |
|
36 (unsigned long long int)aAddress, |
|
37 (unsigned long long int)aLength); |
|
38 buf[sizeof(buf)-1] = 0; |
|
39 mLog(buf); |
|
40 } |
|
41 // This throws away any previous summary, that is, assumes |
|
42 // that the previous summary, if any, has been properly finished |
|
43 // by a call to End(). |
|
44 mCurrAddr = aAddress; |
|
45 mMax1Addr = aAddress + aLength; |
|
46 new (&mCurrRules) RuleSet(); |
|
47 } |
|
48 |
|
49 void |
|
50 Summariser::Rule(uintptr_t aAddress, |
|
51 int aNewReg, int aOldReg, intptr_t aOffset, bool aDeref) |
|
52 { |
|
53 aAddress += mTextBias; |
|
54 if (DEBUG_SUMMARISER) { |
|
55 char buf[100]; |
|
56 snprintf(buf, sizeof(buf), |
|
57 "LUL 0x%llx old-r%d = %sr%d + %ld%s\n", |
|
58 (unsigned long long int)aAddress, aNewReg, |
|
59 aDeref ? "*(" : "", aOldReg, (long)aOffset, aDeref ? ")" : ""); |
|
60 buf[sizeof(buf)-1] = 0; |
|
61 mLog(buf); |
|
62 } |
|
63 if (mCurrAddr < aAddress) { |
|
64 // Flush the existing summary first. |
|
65 mCurrRules.mAddr = mCurrAddr; |
|
66 mCurrRules.mLen = aAddress - mCurrAddr; |
|
67 mSecMap->AddRuleSet(&mCurrRules); |
|
68 if (DEBUG_SUMMARISER) { |
|
69 mLog("LUL "); mCurrRules.Print(mLog); |
|
70 mLog("\n"); |
|
71 } |
|
72 mCurrAddr = aAddress; |
|
73 } |
|
74 |
|
75 // FIXME: factor out common parts of the arch-dependent summarisers. |
|
76 |
|
77 #if defined(LUL_ARCH_arm) |
|
78 |
|
79 // ----------------- arm ----------------- // |
|
80 |
|
81 // Now, can we add the rule to our summary? This depends on whether |
|
82 // the registers and the overall expression are representable. This |
|
83 // is the heart of the summarisation process. |
|
84 switch (aNewReg) { |
|
85 |
|
86 case DW_REG_CFA: |
|
87 // This is a rule that defines the CFA. The only forms we |
|
88 // choose to represent are: r7/11/12/13 + offset. The offset |
|
89 // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM, |
|
90 // hence there is no need to check it for overflow. |
|
91 if (aDeref) { |
|
92 goto cant_summarise; |
|
93 } |
|
94 switch (aOldReg) { |
|
95 case DW_REG_ARM_R7: case DW_REG_ARM_R11: |
|
96 case DW_REG_ARM_R12: case DW_REG_ARM_R13: |
|
97 break; |
|
98 default: |
|
99 goto cant_summarise; |
|
100 } |
|
101 mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset); |
|
102 break; |
|
103 |
|
104 case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12: |
|
105 case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: { |
|
106 // Check the aOldReg is valid. |
|
107 switch (aOldReg) { |
|
108 case DW_REG_CFA: |
|
109 case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12: |
|
110 case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: |
|
111 break; |
|
112 default: |
|
113 goto cant_summarise; |
|
114 } |
|
115 // This is a new rule for one of r{7,11,12,13,14,15} and has a |
|
116 // representable offset. In particular the new value of r15 is |
|
117 // going to be the return address. |
|
118 LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF, |
|
119 aOldReg, aOffset); |
|
120 switch (aNewReg) { |
|
121 case DW_REG_ARM_R7: mCurrRules.mR7expr = expr; break; |
|
122 case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break; |
|
123 case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break; |
|
124 case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break; |
|
125 case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break; |
|
126 case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break; |
|
127 default: MOZ_ASSERT(0); |
|
128 } |
|
129 break; |
|
130 } |
|
131 |
|
132 default: |
|
133 goto cant_summarise; |
|
134 } |
|
135 |
|
136 // Mark callee-saved registers (r4 .. r11) as unchanged, if there is |
|
137 // no other information about them. FIXME: do this just once, at |
|
138 // the point where the ruleset is committed. |
|
139 if (mCurrRules.mR7expr.mHow == LExpr::UNKNOWN) { |
|
140 mCurrRules.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0); |
|
141 } |
|
142 if (mCurrRules.mR11expr.mHow == LExpr::UNKNOWN) { |
|
143 mCurrRules.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0); |
|
144 } |
|
145 if (mCurrRules.mR12expr.mHow == LExpr::UNKNOWN) { |
|
146 mCurrRules.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0); |
|
147 } |
|
148 |
|
149 // The old r13 (SP) value before the call is always the same as the |
|
150 // CFA. |
|
151 mCurrRules.mR13expr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0); |
|
152 |
|
153 // If there's no information about R15 (the return address), say |
|
154 // it's a copy of R14 (the link register). |
|
155 if (mCurrRules.mR15expr.mHow == LExpr::UNKNOWN) { |
|
156 mCurrRules.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0); |
|
157 } |
|
158 |
|
159 #elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) |
|
160 |
|
161 // ---------------- x64/x86 ---------------- // |
|
162 |
|
163 // Now, can we add the rule to our summary? This depends on whether |
|
164 // the registers and the overall expression are representable. This |
|
165 // is the heart of the summarisation process. In the 64 bit case |
|
166 // we need to check that aOffset will fit into an int32_t. In the |
|
167 // 32 bit case it is expected that the compiler will fold out the |
|
168 // test since it always succeeds. |
|
169 if (aNewReg == DW_REG_CFA) { |
|
170 // This is a rule that defines the CFA. The only forms we can |
|
171 // represent are: = SP+offset or = FP+offset. |
|
172 if (!aDeref && aOffset == (intptr_t)(int32_t)aOffset && |
|
173 (aOldReg == DW_REG_INTEL_XSP || aOldReg == DW_REG_INTEL_XBP)) { |
|
174 mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset); |
|
175 } else { |
|
176 goto cant_summarise; |
|
177 } |
|
178 } |
|
179 else |
|
180 if ((aNewReg == DW_REG_INTEL_XSP || |
|
181 aNewReg == DW_REG_INTEL_XBP || aNewReg == DW_REG_INTEL_XIP) && |
|
182 (aOldReg == DW_REG_CFA || |
|
183 aOldReg == DW_REG_INTEL_XSP || |
|
184 aOldReg == DW_REG_INTEL_XBP || aOldReg == DW_REG_INTEL_XIP) && |
|
185 aOffset == (intptr_t)(int32_t)aOffset) { |
|
186 // This is a new rule for SP, BP or the return address |
|
187 // respectively, and has a representable offset. |
|
188 LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF, |
|
189 aOldReg, aOffset); |
|
190 switch (aNewReg) { |
|
191 case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break; |
|
192 case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break; |
|
193 case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break; |
|
194 default: MOZ_CRASH("impossible value for aNewReg"); |
|
195 } |
|
196 } |
|
197 else { |
|
198 goto cant_summarise; |
|
199 } |
|
200 |
|
201 // On Intel, it seems the old SP value before the call is always the |
|
202 // same as the CFA. Therefore, in the absence of any other way to |
|
203 // recover the SP, specify that the CFA should be copied. |
|
204 if (mCurrRules.mXspExpr.mHow == LExpr::UNKNOWN) { |
|
205 mCurrRules.mXspExpr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0); |
|
206 } |
|
207 |
|
208 // Also, gcc says "Undef" for BP when it is unchanged. |
|
209 if (mCurrRules.mXbpExpr.mHow == LExpr::UNKNOWN) { |
|
210 mCurrRules.mXbpExpr = LExpr(LExpr::NODEREF, DW_REG_INTEL_XBP, 0); |
|
211 } |
|
212 |
|
213 #else |
|
214 |
|
215 # error "Unsupported arch" |
|
216 #endif |
|
217 |
|
218 return; |
|
219 cant_summarise: |
|
220 if (0) { |
|
221 mLog("LUL can't summarise\n"); |
|
222 } |
|
223 } |
|
224 |
|
225 void |
|
226 Summariser::End() |
|
227 { |
|
228 if (DEBUG_SUMMARISER) { |
|
229 mLog("LUL End\n"); |
|
230 } |
|
231 if (mCurrAddr < mMax1Addr) { |
|
232 mCurrRules.mAddr = mCurrAddr; |
|
233 mCurrRules.mLen = mMax1Addr - mCurrAddr; |
|
234 mSecMap->AddRuleSet(&mCurrRules); |
|
235 if (DEBUG_SUMMARISER) { |
|
236 mLog("LUL "); mCurrRules.Print(mLog); |
|
237 mLog("\n"); |
|
238 } |
|
239 } |
|
240 } |
|
241 |
|
242 } // namespace lul |