* Correctly open the first dependency that matches the ABI of its parent.
[dyninst.git] / dyninstAPI / src / BPatch_binaryEdit.C
1 /*
2  * Copyright (c) 1996-2004 Barton P. Miller
3  * 
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.
10  * 
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.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
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.
29  * 
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.
40  */
41
42 #define BPATCH_FILE
43
44 #include "process.h"
45 #include "binaryEdit.h"
46 #include "addressSpace.h"
47 #include "EventHandler.h"
48 #include "mailbox.h"
49 #include "signalgenerator.h"
50 #include "inst.h"
51 #include "instP.h"
52 #include "instPoint.h"
53 #include "function.h" // int_function
54 #include "codeRange.h"
55 #include "dyn_thread.h"
56 #include "miniTramp.h"
57
58 #include "mapped_module.h"
59
60 #include "BPatch_libInfo.h"
61 #include "BPatch.h"
62 #include "BPatch_addressSpace.h"
63
64 #include "BPatch_binaryEdit.h"
65 #include "BPatch_image.h"
66 #include "BPatch_thread.h"
67 #include "BPatch_function.h"
68 #include "callbacks.h"
69
70 #include "BPatch_private.h"
71 #include <queue>
72
73
74 #include "ast.h"
75
76 #include "sys/stat.h"
77
78 /*
79  * BPatch_binaryEdit::BPatch_binaryEdit
80  *
81  * Starts a new process and associates it with the BPatch_binaryEdit being
82  * constructed.  The new process is placed into a stopped state before
83  * executing any code.
84  *
85  * path         Pathname of the executable to start.
86  * argv         A list of pointers to character strings which are the
87  *              arguments for the new process, terminated by a NULL pointer.
88  * envp         A list of pointers to character strings which are the
89  *              environment variables for the new process, terminated by a
90  *              NULL pointer.  If NULL, the default environment will be used.
91  */
92 BPatch_binaryEdit::BPatch_binaryEdit(const char *path, bool openDependencies) :
93    BPatch_addressSpace(),
94    creation_error(false)
95 {
96   pendingInsertions = new BPatch_Vector<batchInsertionRecord *>;
97  
98   pdvector<std::string> argv_vec;
99   pdvector<std::string> envp_vec;
100   
101   std::string directoryName = "";
102
103   startup_printf("[%s:%u] - Opening original file %s\n", 
104                  FILE__, __LINE__, path);
105   origBinEdit = BinaryEdit::openFile(std::string(path));
106   if (!origBinEdit){
107      startup_printf("[%s:%u] - Creation error opening %s\n", 
108                     FILE__, __LINE__, path);
109      creation_error = true;
110      return;
111   }
112   llBinEdits[path] = origBinEdit;
113   
114   if(openDependencies) {
115     origBinEdit->getAllDependencies(llBinEdits); 
116   }
117
118   origBinEdit->getDyninstRTLibName();
119   std::string rt_name = origBinEdit->dyninstRT_name;
120   rtLib = BinaryEdit::openFile(rt_name);
121   if (!rtLib) {
122      startup_printf("[%s:%u] - ERROR.  Could not open RT library\n",
123                     __FILE__, __LINE__);
124      creation_error = true;
125      return;
126   }
127
128   std::map<std::string, BinaryEdit*>::iterator i, j;
129   for(i = llBinEdits.begin(); i != llBinEdits.end(); i++) {
130      (*i).second->setupRTLibrary(rtLib);
131   }
132
133   int_variable* masterTrampGuard = origBinEdit->createTrampGuard();
134   assert(masterTrampGuard);
135   
136   for(i = llBinEdits.begin(); i != llBinEdits.end(); i++) {
137      BinaryEdit *llBinEdit = (*i).second;
138      llBinEdit->registerFunctionCallback(createBPFuncCB);
139      llBinEdit->registerInstPointCallback(createBPPointCB);
140      llBinEdit->set_up_ptr(this);
141      llBinEdit->setupRTLibrary(rtLib);
142      llBinEdit->setTrampGuard(masterTrampGuard);
143      llBinEdit->setMultiThreadCapable(isMultiThreadCapable());
144      for (j = llBinEdits.begin(); j != llBinEdits.end(); j++) {
145         llBinEdit->addSibling((*j).second);
146      }
147   }
148
149   image = new BPatch_image(this);
150 }
151
152 bool BPatch_binaryEdit::isMultiThreadCapable() const
153 {
154   return origBinEdit->isMultiThreadCapable();
155 }
156
157 BPatch_image * BPatch_binaryEdit::getImageInt() {
158         return image;
159 }
160
161 void BPatch_binaryEdit::BPatch_binaryEdit_dtor()
162 {
163    if (image) 
164       delete image;
165    
166    image = NULL;
167
168    if (pendingInsertions) {
169      for (unsigned f = 0; f < pendingInsertions->size(); f++) {
170        delete (*pendingInsertions)[f];
171      }
172      delete pendingInsertions;
173      pendingInsertions = NULL;
174    }
175
176   std::map<std::string, BinaryEdit*>::iterator i = llBinEdits.begin();
177   for(; i != llBinEdits.end(); i++) {
178      delete (*i).second;
179   }
180   llBinEdits.clear();
181   origBinEdit = NULL;
182   
183   assert(BPatch::bpatch != NULL);
184 }
185
186 bool BPatch_binaryEdit::writeFileInt(const char * outFile)
187 {
188     assert(pendingInsertions);
189
190     // This should be a parameter...
191     bool atomic = false;
192    
193     // Define up here so we don't have gotos causing issues
194     std::set<int_function *> instrumentedFunctions;
195     
196     // Two loops: first addInst, then generate/install/link
197     pdvector<miniTramp *> workDone;
198     bool err = false;
199
200     for (unsigned i = 0; i < pendingInsertions->size(); i++) {
201         batchInsertionRecord *&bir = (*pendingInsertions)[i];
202         assert(bir);
203
204         // Don't handle thread inst yet...
205         assert(!bir->thread_);
206
207         if (!bir->points_.size()) {
208           fprintf(stderr, "%s[%d]:  WARN:  zero points for insertion record\n", FILE__, __LINE__);
209           fprintf(stderr, "%s[%d]:  failing to addInst\n", FILE__, __LINE__);
210         }
211
212         for (unsigned j = 0; j < bir->points_.size(); j++) {
213             BPatch_point *bppoint = bir->points_[j];
214             instPoint *point = bppoint->point;
215             callWhen when = bir->when_[j];
216             
217             miniTramp *mini = point->addInst(bir->snip.ast_wrapper,
218                                              when,
219                                              bir->order_,
220                                              bir->trampRecursive_,
221                                              false);
222             if (mini) {
223                 workDone.push_back(mini);
224                 // Add to snippet handle
225                 bir->handle_->addMiniTramp(mini);
226             }
227             else {
228                 err = true;
229                 if (atomic) break;
230             }
231         }
232         if (atomic && err)
233             break;
234     }
235     
236    if (atomic && err) goto cleanup;
237
238    // Having inserted the requests, we hand things off to functions to 
239    // actually do work. First, develop a list of unique functions. 
240
241    for (unsigned i = 0; i < pendingInsertions->size(); i++) {
242        batchInsertionRecord *&bir = (*pendingInsertions)[i];
243        for (unsigned j = 0; j < bir->points_.size(); j++) {
244            BPatch_point *bppoint = bir->points_[j];
245            instPoint *point = bppoint->point;
246            point->optimizeBaseTramps(bir->when_[j]);
247
248            instrumentedFunctions.insert(point->func());
249        }
250    }
251
252    for (std::set<int_function *>::iterator funcIter = instrumentedFunctions.begin();
253         funcIter != instrumentedFunctions.end();
254         funcIter++) {
255
256        pdvector<instPoint *> failedInstPoints;
257        (*funcIter)->performInstrumentation(atomic,
258                                            failedInstPoints); 
259        if (failedInstPoints.size() && atomic) {
260            err = true;
261            goto cleanup;
262        }
263    }
264
265    
266    // Now that we've instrumented we can see if we need to replace the
267    // trap handler.
268    replaceTrapHandler();
269
270
271    if (atomic && err) 
272       goto cleanup;
273
274    for(std::map<std::string, BinaryEdit*>::iterator i = llBinEdits.begin();
275        i != llBinEdits.end(); i++) 
276    {
277       (*i).second->trapMapping.flush();
278    }
279
280   cleanup:
281     bool ret = true;
282
283     if (atomic && err) {
284         // Something failed...   Cleanup...
285         for (unsigned k = 0; k < workDone.size(); k++) {
286             workDone[k]->uninstrument();
287         }
288         ret = false;
289     }
290
291     for (unsigned int i = 0; i < pendingInsertions->size(); i++) {
292         batchInsertionRecord *&bir = (*pendingInsertions)[i];
293         assert(bir);
294         delete(bir);
295     }
296
297     pendingInsertions->clear();
298
299     origBinEdit->writeFile(outFile);
300
301     std::map<std::string, BinaryEdit *>::iterator i;
302     for (i = llBinEdits.begin(); i != llBinEdits.end(); i++) {
303        BinaryEdit *bin = (*i).second;
304        if (bin == origBinEdit)
305           continue;
306        if (!bin->isDirty())
307           continue;
308        
309        std::string newname = bin->getMappedObject()->fileName();
310        bin->writeFile(newname);
311     }
312
313
314     return ret;
315 }
316
317 bool BPatch_binaryEdit::getType()
318 {
319   return STATIC_EDITOR;
320 }
321
322 void BPatch_binaryEdit::getAS(std::vector<AddressSpace *> &as)
323 {
324    std::map<std::string, BinaryEdit*>::iterator i = llBinEdits.begin();
325    as.push_back(origBinEdit);
326    for(; i != llBinEdits.end(); i++) {
327       if ((*i).second == origBinEdit)
328          continue;
329       as.push_back((*i).second);
330    }
331 }
332
333 /*
334  * BPatch_addressSpace::beginInsertionSet
335  * 
336  * Starts a batch insertion set; that is, all calls to insertSnippet until
337  * finalizeInsertionSet are delayed.
338  *
339  */
340
341 void BPatch_binaryEdit::beginInsertionSetInt() 
342 {
343     return;
344 }
345
346
347 bool BPatch_binaryEdit::finalizeInsertionSetInt(bool /*atomic*/, bool * /*modified*/) 
348 {
349     return true;
350 }
351
352 bool BPatch_binaryEdit::loadLibraryInt(const char *libname, bool deps)
353 {
354   std::pair<std::string, BinaryEdit*> lib = origBinEdit->openResolvedLibraryName(libname);
355   if(!lib.second)
356     return false;
357
358   llBinEdits.insert(lib);
359   
360   if (deps)
361     return lib.second->getAllDependencies(llBinEdits);
362   return true;
363   
364 }
365
366 // Here's the story. We may need to install a trap handler for instrumentation
367 // to work in the rewritten binary. This doesn't play nicely with trap handlers
368 // that the binary itself registers. So we're going to replace every call to
369 // sigaction in the binary with a call to our wrapper. This wrapper:
370 //   1) Ignores attempts to register a SIGTRAP
371 //   2) Passes everything else through to sigaction
372 // It's called "dyn_sigaction".
373 // This is just a multiplexing function over each child binaryEdit
374 // object because they're all individual.
375
376 bool BPatch_binaryEdit::replaceTrapHandler() {
377     // Did we use a trap?
378
379     bool usedATrap = false;
380
381     std::map<std::string, BinaryEdit *>::iterator iter = llBinEdits.begin();
382     for (; iter != llBinEdits.end(); iter++) {
383         if (iter->second == rtLib) continue;
384         if (iter->second->usedATrap()) {
385             usedATrap = true;
386         }
387     }
388
389     if (!usedATrap) return true;
390
391     // We used a trap, so go through and set up the replacement instrumentation.
392     // However, don't let this be the first piece of instrumentation put into
393     // a library.
394
395     bool success = true;
396     iter = llBinEdits.begin();
397     for (; iter != llBinEdits.end(); iter++) {
398         BinaryEdit *binEd = iter->second;
399         if (binEd == rtLib) {
400             continue;
401         }
402
403         // Did we instrument this already?
404         if (!binEd->isDirty()) {
405             continue;
406         }
407
408         // Okay, replace trap handler
409         if (!binEd->replaceTrapHandler()) {
410             success = false;
411         }
412     }
413     return success;
414 }
415     
416