2 * Copyright (c) 1996-2004 Barton P. Miller
4 * We provide the Paradyn Parallel Performance Tools (below
5 * described as "Paradyn") on an AS IS basis, and do not warrant its
6 * validity or performance. We reserve the right to update, modify,
7 * or discontinue this software at any time. We shall have no
8 * obligation to supply such updates or modifications or any other
9 * form of support to you.
11 * This license is for research uses. For such uses, there is no
12 * charge. We define "research use" to mean you may freely use it
13 * inside your organization for whatever purposes you see fit. But you
14 * may not re-distribute Paradyn or parts of Paradyn, in any form
15 * source or binary (including derivatives), electronic or otherwise,
16 * to any other organization or entity without our permission.
18 * (for other uses, please contact us at paradyn@cs.wisc.edu)
20 * All warranties, including without limitation, any warranty of
21 * merchantability or fitness for a particular purpose, are hereby
24 * By your use of Paradyn, you understand and agree that we (or any
25 * other person or entity with proprietary rights in Paradyn) are
26 * under no obligation to provide either maintenance services,
27 * update services, notices of latent defects, or correction of
28 * defects for Paradyn.
30 * Even if advised of the possibility of such damages, under no
31 * circumstances shall we (or any other person or entity with
32 * proprietary rights in the software licensed hereunder) be liable
33 * to you or any third party for direct, indirect, or consequential
34 * damages of any character regardless of type of action, including,
35 * without limitation, loss of profits, loss of use, loss of good
36 * will, or computer failure or malfunction. You agree to indemnify
37 * us (and any other person or entity with proprietary rights in the
38 * software licensed hereunder) for any and all liability it may
39 * incur to third parties resulting from your use of Paradyn.
42 #include "dyninstAPI/src/function.h"
43 #include "dyninstAPI/src/symtab.h"
44 #include "instPoint.h"
46 #include "instructionAPI/h/InstructionDecoder.h"
47 #include "instructionAPI/h/Result.h"
48 #include "instructionAPI/h/Instruction.h"
53 #include "stackanalysis.h"
55 #include "Annotatable.h"
59 using namespace Dyninst;
60 using namespace InstructionAPI;
62 const StackAnalysis::StackHeight StackAnalysis::StackHeight::bottom(StackAnalysis::StackHeight::notUnique);
63 const StackAnalysis::StackHeight StackAnalysis::StackHeight::top(StackAnalysis::StackHeight::uninitialized);
65 AnnotationClass <StackAnalysis::HeightTree> StackHeightAnno(std::string("StackHeightAnno"));
66 AnnotationClass <StackAnalysis::PresenceTree> StackPresenceAnno(std::string("StackPresenceAnno"));
73 // There are three terms we throw around:
75 // Stack height: the size of the stack; (cur_stack_ptr - func_start_stack_ptr)
76 // Stack delta: the difference in the size of the stack over the execution of
77 // a region of code (basic block here). (block_end_stack_ptr - block_start_stack_ptr)
78 // Stack clean: the amount the callee function shifts the stack. This is an x86 idiom.
79 // On x86 the caller pushes arguments onto the stack (and thus shifts the stack).
80 // Normally the caller also cleans the stack (that is, removes arguments). However,
81 // some callee functions perform this cleaning themselves. We need to account for this
82 // in our analysis, which otherwise assumes that callee functions don't alter the stack.
85 bool StackAnalysis::analyze()
87 stanalysis_printf("Beginning stack analysis for function %s\n",
88 func->symTabName().c_str());
89 blocks = func->blocks();
90 if (blocks.empty()) return false;
92 blocks = func->blocks();
94 stanalysis_printf("\tSummarizing block effects\n");
95 summarizeBlockDeltas();
97 stanalysis_printf("\tPerforming fixpoint analysis\n");
98 calculateInterBlockDepth();
100 stanalysis_printf("\tCreating interval trees\n");
103 func->addAnnotation(heightIntervals_, StackHeightAnno);
104 func->addAnnotation(presenceIntervals_, StackPresenceAnno);
106 if (dyn_debug_stackanalysis) {
108 debugStackPresences();
111 stanalysis_printf("Finished stack analysis for function %s\n",
112 func->symTabName().c_str());
117 void StackAnalysis::summarizeBlockDeltas() {
121 // Foreach (insn i, offset o) in B:
122 // Let d = change in stack height at i;
124 // blockToInsnDeltas[B][o] = d;
125 // blockHeightDeltas[B] = acc;
129 // Let d = uninitialized;
130 // Foreach (insn i, offset o) in B:
131 // d = change in stack presence at d;
132 // blockToInsnDeltas[B][o] = d;
133 // blockHeightDeltas[B] = d;
135 for (Block::blockSet::iterator iter = blocks.begin(); iter != blocks.end(); iter++) {
136 Block *block = *iter;
137 stanalysis_printf("\t Block starting at 0x%lx\n", block->firstInsnOffset());
139 StackHeight heightChange(0);
140 StackPresence stackPresence;
142 std::vector<std::pair<InstructionAPI::Instruction, Offset> > instances;
143 block->getInsnInstances(instances);
145 for (unsigned j = 0; j < instances.size(); j++) {
146 InstructionAPI::Instruction insn = instances[j].first;
147 Offset off = instances[j].second;
151 computeInsnEffects(block, insn, off, delta, stackPresence);
152 blockToInsnDeltas[block][off] = delta;
153 blockToInsnPresence[block][off] = stackPresence;
155 heightChange += delta;
157 blockHeightDeltas[block] = heightChange;
158 blockPresenceDeltas[block] = stackPresence;
162 void StackAnalysis::calculateInterBlockDepth() {
164 std::queue<Block *> worklist;
166 worklist.push(func->entryBlock());
168 //BlockHeight_t blockHeights; // This by default initializes all entries to "bottom".
170 while (!worklist.empty()) {
171 Block *block = worklist.front();
172 stanalysis_printf("\t Fixpoint analysis: visiting block at 0x%lx\n", block->firstInsnOffset());
176 // Step 1: calculate the meet over the heights of all incoming
177 // intraprocedural blocks.
179 std::set<StackHeight> inHeights;
180 std::set<StackPresence> inPresences;
182 if (block->isEntryBlock(func)) {
183 // The in height is 0
184 inHeights.insert(StackHeight(0));
185 inPresences.insert(StackPresence(StackPresence::noFrame));
188 std::vector<Edge *> inEdges; block->getSources(inEdges);
189 for (unsigned i = 0; i < inEdges.size(); i++) {
190 Edge *edge = inEdges[i];
191 if (edge->getType() == ET_CALL) continue;
192 inHeights.insert(outBlockHeights[edge->getSource()]);
193 inPresences.insert(outBlockPresences[edge->getSource()]);
197 StackHeight newInHeight = StackHeight::meet(inHeights);
198 StackPresence newInPresence = StackPresence::meet(inPresences);
200 // Step 2: see if the input has changed
202 if ((newInHeight == inBlockHeights[block]) &&
203 (newInPresence == inBlockPresences[block])) {
208 // Step 3: calculate our new out height
210 inBlockHeights[block] = newInHeight;
211 outBlockHeights[block] = newInHeight + blockHeightDeltas[block];
213 inBlockPresences[block] = newInPresence;
214 // If there was no change in the block then use the in presence; otherwise
215 // use the change in the block. Effectively a 2-part meet.
216 outBlockPresences[block] = (blockPresenceDeltas[block].isTop()) ? (newInPresence) : (blockPresenceDeltas[block]);
218 // Step 4: push all children on the worklist.
220 std::vector<Edge *> outEdges; block->getTargets(outEdges);
222 for (unsigned i = 0; i < outEdges.size(); i++) {
223 if (outEdges[i]->getType() == ET_CALL) continue;
224 worklist.push(outEdges[i]->getTarget());
229 void StackAnalysis::createIntervals() {
230 // Use the results summaries to calculate the
231 // stack heights. We expect that heights will
232 // change infrequently and so summarize them at
233 // the function level.
235 heightIntervals_ = new HeightTree();
236 presenceIntervals_ = new PresenceTree();
238 StackHeight curHeight;
242 for (Block::blockSet::iterator iter = blocks.begin(); iter != blocks.end(); iter++) {
243 Block *block = *iter;
245 stanalysis_printf("\t Interval creation: visiting block at 0x%lx\n", block->firstInsnOffset());
247 curLB = block->firstInsnOffset();
249 curHeight = inBlockHeights[block];
251 // Now create extents for instructions within this block.
253 for (InsnDeltas::iterator iter = blockToInsnDeltas[block].begin();
254 iter != blockToInsnDeltas[block].end();
256 // Now create extents for instructions within this block.
257 if ((*iter).second == StackHeight(0)) {
261 // We've changed the height of the stack. End this interval
262 // and start a new one.
264 // We need to determine UB. It's defined as the end of the
265 // current instruction. Therefore there are two cases:
266 // if we're the last insn (take the block end) or not
267 // the last insn (take the start of the next insn)
268 InsnDeltas::iterator iter2 = iter;
270 if (iter2 == blockToInsnDeltas[block].end()) {
271 curUB = block->endOffset();
274 curUB = (*iter2).first;
276 heightIntervals_->insert(curLB, curUB, curHeight);
281 curHeight += (*iter).second;
285 if (curLB != block->endOffset()) {
286 // Cap off the extent for the current block
287 curUB = block->endOffset();
288 if (curHeight != outBlockHeights[block]) {
289 fprintf(stderr, "ERROR: inconsistency in stack analysis, %s != %s\n",
290 curHeight.getString().c_str(), outBlockHeights[block].getString().c_str());
292 assert(curHeight == outBlockHeights[block]);
293 heightIntervals_->insert(curLB, curUB, curHeight);
297 StackPresence curPres;
301 for (Block::blockSet::iterator iter = blocks.begin(); iter != blocks.end(); iter++) {
302 Block *block = *iter;
304 curLB = block->firstInsnOffset();
306 curPres = inBlockPresences[block];
308 for (InsnPresence::iterator iter = blockToInsnPresence[block].begin();
309 iter != blockToInsnPresence[block].end();
312 if ((*iter).second.isTop())
315 // We've changed the state of the stack pointer. End this interval
316 // and start a new one.
318 // We need to determine UB. It's defined as the end of the
319 // current instruction. Therefore there are two cases:
320 // if we're the last insn (take the block end) or not
321 // the last insn (take the start of the next insn)
322 InsnPresence::iterator iter2 = iter;
324 if (iter2 == blockToInsnPresence[block].end()) {
325 curUB = block->endOffset();
328 curUB = (*iter2).first;
330 presenceIntervals_->insert(curLB, curUB, curPres);
335 curPres = (*iter).second;
338 if (curLB != block->endOffset()) {
339 // Cap off the extent for the current block
340 curUB = block->endOffset();
341 assert(curPres == outBlockPresences[block]);
342 presenceIntervals_->insert(curLB, curUB, curPres);
348 void StackAnalysis::computeInsnEffects(const Block *block,
349 const Instruction &insn,
354 stanalysis_printf("\t\tInsn at 0x%lx\n", off);
355 Expression::Ptr theStackPtr(new RegisterAST(r_eSP));
356 Expression::Ptr stackPtr32(new RegisterAST(r_ESP));
357 Expression::Ptr stackPtr64(new RegisterAST(r_RSP));
359 Expression::Ptr theFramePtr(new RegisterAST(r_eBP));
360 Expression::Ptr framePtr32(new RegisterAST(r_EBP));
361 Expression::Ptr framePtr64(new RegisterAST(r_RBP));
363 //TODO: Integrate entire test into analysis lattice
364 entryID what = insn.getOperation().getID();
366 if (insn.isWritten(theFramePtr) || insn.isWritten(framePtr32) || insn.isWritten(framePtr64)) {
367 stanalysis_printf("\t\t\t FP written\n");
369 (insn.isRead(theStackPtr) || insn.isRead(stackPtr32) || insn.isRead(stackPtr64))) {
370 pres = StackPresence::frame;
371 stanalysis_printf("\t\t\t Frame created\n");
374 pres = StackPresence::noFrame;
375 stanalysis_printf("\t\t\t Frame destroyed\n");
382 pdvector<image_edge *> outs;
383 block->getTargets(outs);
384 for (unsigned i=0; i<outs.size(); i++) {
385 image_edge *cur_edge = outs[i];
386 if (cur_edge->getType() != ET_CALL)
389 image_basicBlock *target_bbl = cur_edge->getTarget();
390 image_func *target_func = target_bbl->getEntryFunc();
393 height = getStackCleanAmount(target_func);
394 stanalysis_printf("\t\t\t Stack height changed by self-cleaning function: %s\n", height.getString().c_str());
397 height = StackHeight(0);
398 stanalysis_printf("\t\t\t Stack height assumed unchanged by call\n");
402 int word_size = func->img()->getAddressWidth();
404 if(!insn.isWritten(theStackPtr) && !insn.isWritten(stackPtr32)) {
405 height = StackHeight(0);
414 Operand arg = insn.getOperand(0);
415 if (arg.getValue()->eval().defined) {
416 height = StackHeight(sign * word_size);
417 stanalysis_printf("\t\t\t Stack height changed by evaluated push/pop: %s\n", height.getString().c_str());
420 height = StackHeight(sign * arg.getValue()->size());
421 stanalysis_printf("\t\t\t Stack height changed by unevalled push/pop: %s\n", height.getString().c_str());
427 // Add/subtract are op0 += (or -=) op1
428 Operand arg = insn.getOperand(1);
429 Result delta = arg.getValue()->eval();
433 height = StackHeight(sign * delta.val.u8val);
436 height = StackHeight(sign * delta.val.u16val);
439 height = StackHeight(sign * delta.val.u32val);
442 height = StackHeight(sign * delta.val.u64val);
445 height = StackHeight(sign * delta.val.s8val);
448 height = StackHeight(sign * delta.val.s16val);
451 height = StackHeight(sign * delta.val.s32val);
454 height = StackHeight(sign * delta.val.s64val);
457 // Add of anything that's not a "normal" integral type
458 // means we don't know what happened
459 height = StackHeight(StackHeight::bottom);
462 stanalysis_printf("\t\t\t Stack height changed by evalled add/sub: %s\n", height.getString().c_str());
466 height = StackHeight(StackHeight::bottom);
467 stanalysis_printf("\t\t\t Stack height changed by unevalled add/sub: %s\n", height.getString().c_str());
469 // We treat return as zero-modification right now
472 height = StackHeight(0);
473 stanalysis_printf("\t\t\t Stack height changed by ret_near/ret_far %s\n", height.getString().c_str());
476 stanalysis_printf("\t\t\t Stack height changed by unhandled insn %s: %s\n",
477 insn.format().c_str(), height.getString().c_str());
478 height = StackHeight(StackHeight::bottom);
486 StackAnalysis::StackHeight StackAnalysis::getStackCleanAmount(image_func *func) {
487 // Cache previous work...
488 if (funcCleanAmounts.find(func) != funcCleanAmounts.end()) {
489 return funcCleanAmounts[func];
492 if (!func->cleansOwnStack()) {
493 funcCleanAmounts[func] = StackHeight(0);
494 return StackHeight(0);
497 InstructionDecoder decoder;
500 std::set<StackHeight> returnCleanVals;
502 for (unsigned i=0; i < func->funcExits().size(); i++) {
503 cur = (unsigned char *) func->getPtrToInstruction(func->funcExits()[i]->offset());
505 Instruction insn = decoder.decode(cur, size);
507 entryID what = insn.getOperation().getID();
508 if (what != e_ret_near)
512 std::vector<Operand> ops;
513 insn.getOperands(ops);
514 if (ops.size() == 0) {
518 Result imm = ops[0].getValue()->eval();
520 val = (int) imm.val.s16val;
522 returnCleanVals.insert(StackHeight(val));
525 funcCleanAmounts[func] = StackHeight::meet(returnCleanVals);
526 return funcCleanAmounts[func];
529 StackAnalysis::StackAnalysis(Function *f) : func(f) {
530 blocks = func->blocks();
531 heightIntervals_ = NULL;
532 presenceIntervals_ = NULL;
535 const StackAnalysis::HeightTree *StackAnalysis::heightIntervals() {
536 if (func == NULL) return NULL;
538 // Check the annotation
540 func->getAnnotation(ret, StackHeightAnno);
542 if (heightIntervals_ == NULL) {
543 if (!analyze()) return NULL;
544 ret = heightIntervals_;
551 const StackAnalysis::PresenceTree *StackAnalysis::presenceIntervals() {
552 if (func == NULL) return NULL;
554 // Check the annotation
556 func->getAnnotation(ret, StackPresenceAnno);
559 if (presenceIntervals_ == NULL) {
560 if (!analyze()) return NULL;
561 ret = presenceIntervals_;
567 void StackAnalysis::debugStackHeights() {
568 if (!heightIntervals_) return;
569 std::vector<std::pair<std::pair<Offset, Offset>, StackHeight> > elements;
570 heightIntervals_->elements(elements);
572 for (unsigned i = 0; i < elements.size(); i++) {
573 fprintf(stderr, "0x%lx - 0x%lx: %s\n",
574 elements[i].first.first,
575 elements[i].first.second,
576 elements[i].second.getString().c_str());
580 void StackAnalysis::debugStackPresences() {
581 if (!presenceIntervals_) return;
583 std::vector<std::pair<std::pair<Offset, Offset>, StackPresence> > elements;
584 presenceIntervals_->elements(elements);
586 for (unsigned i = 0; i < elements.size(); i++) {
587 fprintf(stderr, "0x%lx - 0x%lx: %s\n",
588 elements[i].first.first,
589 elements[i].first.second,
590 elements[i].second.getString().c_str());