BPatch_binaryEdit::staticExecutableLoaded was poorly named. Changing
[dyninst.git] / dyninstAPI / src / BPatch_binaryEdit.C
1 /*
2  * Copyright (c) 1996-2009 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  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30  */
31
32 #define BPATCH_FILE
33
34 #include "process.h"
35 #include "binaryEdit.h"
36 #include "addressSpace.h"
37 #include "EventHandler.h"
38 #include "mailbox.h"
39 #include "signalgenerator.h"
40 #include "inst.h"
41 #include "instP.h"
42 #include "instPoint.h"
43 #include "function.h" // int_function
44 #include "codeRange.h"
45 #include "dyn_thread.h"
46 #include "miniTramp.h"
47
48 #include "mapped_module.h"
49
50 #include "BPatch_libInfo.h"
51 #include "BPatch.h"
52 #include "BPatch_addressSpace.h"
53
54 #include "BPatch_binaryEdit.h"
55 #include "BPatch_image.h"
56 #include "BPatch_thread.h"
57 #include "BPatch_function.h"
58 #include "callbacks.h"
59
60 #include "BPatch_private.h"
61 #include <queue>
62
63
64 #include "ast.h"
65
66 #include "sys/stat.h"
67
68 /*
69  * BPatch_binaryEdit::BPatch_binaryEdit
70  *
71  * Starts a new process and associates it with the BPatch_binaryEdit being
72  * constructed.  The new process is placed into a stopped state before
73  * executing any code.
74  *
75  * path         Pathname of the executable to start.
76  * argv         A list of pointers to character strings which are the
77  *              arguments for the new process, terminated by a NULL pointer.
78  * envp         A list of pointers to character strings which are the
79  *              environment variables for the new process, terminated by a
80  *              NULL pointer.  If NULL, the default environment will be used.
81  */
82 BPatch_binaryEdit::BPatch_binaryEdit(const char *path, bool openDependencies) :
83    BPatch_addressSpace(),
84    creation_error(false)
85 {
86   pendingInsertions = new BPatch_Vector<batchInsertionRecord *>;
87  
88   pdvector<std::string> argv_vec;
89   pdvector<std::string> envp_vec;
90   
91   std::string directoryName = "";
92
93   startup_printf("[%s:%u] - Opening original file %s\n", 
94                  FILE__, __LINE__, path);
95   origBinEdit = BinaryEdit::openFile(std::string(path));
96   if (!origBinEdit){
97      startup_printf("[%s:%u] - Creation error opening %s\n", 
98                     FILE__, __LINE__, path);
99      creation_error = true;
100      return;
101   }
102   llBinEdits[path] = origBinEdit;
103   
104   if(openDependencies) {
105     origBinEdit->getAllDependencies(llBinEdits); 
106   }
107
108   origBinEdit->getDyninstRTLibName();
109   std::string rt_name = origBinEdit->dyninstRT_name;
110
111   // Load the RT library and create the collection of BinaryEdits that represent it
112   std::map<std::string, BinaryEdit *> rtlibs = origBinEdit->openResolvedLibraryName(rt_name);
113   std::map<std::string, BinaryEdit *>::iterator rtlibs_it;
114   for(rtlibs_it = rtlibs.begin(); rtlibs_it != rtlibs.end(); ++rtlibs_it) {
115       if( !rtlibs_it->second ) {
116           std::string msg("Failed to load Dyninst runtime library, check the environment variable DYNINSTAPI_RT_LIB");
117           showErrorCallback(70, msg.c_str());
118           creation_error = true;
119           return;
120       }
121
122       rtLib.push_back(rtlibs_it->second);
123       // Ensure that the correct type of library is loaded
124       if(    rtlibs_it->second->getMappedObject()->isSharedLib() 
125           && origBinEdit->getMappedObject()->isStaticExec() ) 
126       {
127           std::string msg = std::string("RT Library is a shared library ") +
128               std::string("when it should be a static library");
129           showErrorCallback(70, msg.c_str());
130           creation_error = true;
131           return;
132       }else if(     !rtlibs_it->second->getMappedObject()->isSharedLib()
133                 &&  !origBinEdit->getMappedObject()->isStaticExec() )
134       {
135           std::string msg = std::string("RT Library is a static library ") +
136               std::string("when it should be a shared library");
137           showErrorCallback(70, msg.c_str());
138           creation_error = true;
139           return;
140       }
141   }
142
143   std::map<std::string, BinaryEdit*>::iterator i, j;
144   for(i = llBinEdits.begin(); i != llBinEdits.end(); i++) {
145      (*i).second->setupRTLibrary(rtLib);
146   }
147
148   int_variable* masterTrampGuard = origBinEdit->createTrampGuard();
149   assert(masterTrampGuard);
150   
151   for(i = llBinEdits.begin(); i != llBinEdits.end(); i++) {
152      BinaryEdit *llBinEdit = (*i).second;
153      llBinEdit->registerFunctionCallback(createBPFuncCB);
154      llBinEdit->registerInstPointCallback(createBPPointCB);
155      llBinEdit->set_up_ptr(this);
156      llBinEdit->setupRTLibrary(rtLib);
157      llBinEdit->setTrampGuard(masterTrampGuard);
158      llBinEdit->setMultiThreadCapable(isMultiThreadCapable());
159      for (j = llBinEdits.begin(); j != llBinEdits.end(); j++) {
160         llBinEdit->addSibling((*j).second);
161      }
162   }
163
164   image = new BPatch_image(this);
165 }
166
167 bool BPatch_binaryEdit::isMultiThreadCapable() const
168 {
169   return origBinEdit->isMultiThreadCapable();
170 }
171
172 BPatch_image * BPatch_binaryEdit::getImageInt() {
173         return image;
174 }
175
176 void BPatch_binaryEdit::BPatch_binaryEdit_dtor()
177 {
178    if (image) 
179       delete image;
180    
181    image = NULL;
182
183    if (pendingInsertions) {
184      for (unsigned f = 0; f < pendingInsertions->size(); f++) {
185        delete (*pendingInsertions)[f];
186      }
187      delete pendingInsertions;
188      pendingInsertions = NULL;
189    }
190
191   std::map<std::string, BinaryEdit*>::iterator i = llBinEdits.begin();
192   for(; i != llBinEdits.end(); i++) {
193      delete (*i).second;
194   }
195   llBinEdits.clear();
196   origBinEdit = NULL;
197   
198   assert(BPatch::bpatch != NULL);
199 }
200
201 bool BPatch_binaryEdit::writeFileInt(const char * outFile)
202 {
203     assert(pendingInsertions);
204
205     // This should be a parameter...
206     bool atomic = false;
207    
208     // Define up here so we don't have gotos causing issues
209     std::set<int_function *> instrumentedFunctions;
210     
211     // Two loops: first addInst, then generate/install/link
212     pdvector<miniTramp *> workDone;
213     bool err = false;
214
215     for (unsigned i = 0; i < pendingInsertions->size(); i++) {
216         batchInsertionRecord *&bir = (*pendingInsertions)[i];
217         assert(bir);
218
219         // Don't handle thread inst yet...
220         assert(!bir->thread_);
221
222         if (!bir->points_.size()) {
223           fprintf(stderr, "%s[%d]:  WARN:  zero points for insertion record\n", FILE__, __LINE__);
224           fprintf(stderr, "%s[%d]:  failing to addInst\n", FILE__, __LINE__);
225         }
226
227         for (unsigned j = 0; j < bir->points_.size(); j++) {
228             BPatch_point *bppoint = bir->points_[j];
229             instPoint *point = bppoint->point;
230             callWhen when = bir->when_[j];
231             
232             miniTramp *mini = point->addInst(bir->snip.ast_wrapper,
233                                              when,
234                                              bir->order_,
235                                              bir->trampRecursive_,
236                                              false);
237             if (mini) {
238                 workDone.push_back(mini);
239                 // Add to snippet handle
240                 bir->handle_->addMiniTramp(mini);
241             }
242             else {
243                 err = true;
244                 if (atomic) break;
245             }
246         }
247         if (atomic && err)
248             break;
249     }
250     
251    if (atomic && err) goto cleanup;
252
253    // Having inserted the requests, we hand things off to functions to 
254    // actually do work. First, develop a list of unique functions. 
255
256    for (unsigned i = 0; i < pendingInsertions->size(); i++) {
257        batchInsertionRecord *&bir = (*pendingInsertions)[i];
258        for (unsigned j = 0; j < bir->points_.size(); j++) {
259            BPatch_point *bppoint = bir->points_[j];
260            instPoint *point = bppoint->point;
261            point->optimizeBaseTramps(bir->when_[j]);
262
263            instrumentedFunctions.insert(point->func());
264        }
265    }
266
267    for (std::set<int_function *>::iterator funcIter = instrumentedFunctions.begin();
268         funcIter != instrumentedFunctions.end();
269         funcIter++) {
270
271        pdvector<instPoint *> failedInstPoints;
272        (*funcIter)->performInstrumentation(atomic,
273                                            failedInstPoints); 
274        if (failedInstPoints.size() && atomic) {
275            err = true;
276            goto cleanup;
277        }
278    }
279
280    
281    // Now that we've instrumented we can see if we need to replace the
282    // trap handler.
283    replaceTrapHandler();
284
285
286    if (atomic && err) 
287       goto cleanup;
288
289    for(std::map<std::string, BinaryEdit*>::iterator i = llBinEdits.begin();
290        i != llBinEdits.end(); i++) 
291    {
292       (*i).second->trapMapping.flush();
293    }
294
295   cleanup:
296     bool ret = true;
297
298     if (atomic && err) {
299         // Something failed...   Cleanup...
300         for (unsigned k = 0; k < workDone.size(); k++) {
301             workDone[k]->uninstrument();
302         }
303         ret = false;
304     }
305
306     for (unsigned int i = 0; i < pendingInsertions->size(); i++) {
307         batchInsertionRecord *&bir = (*pendingInsertions)[i];
308         assert(bir);
309         delete(bir);
310     }
311
312     pendingInsertions->clear();
313
314     if( !origBinEdit->writeFile(outFile) ) return false;
315
316     std::map<std::string, BinaryEdit *>::iterator i;
317     for (i = llBinEdits.begin(); i != llBinEdits.end(); i++) {
318        BinaryEdit *bin = (*i).second;
319        if (bin == origBinEdit)
320           continue;
321        if (!bin->isDirty())
322           continue;
323        
324        std::string newname = bin->getMappedObject()->fileName();
325        if( !bin->writeFile(newname) ) return false;
326     }
327
328
329     return ret;
330 }
331
332 bool BPatch_binaryEdit::getType()
333 {
334   return STATIC_EDITOR;
335 }
336
337 void BPatch_binaryEdit::getAS(std::vector<AddressSpace *> &as)
338 {
339    std::map<std::string, BinaryEdit*>::iterator i = llBinEdits.begin();
340    as.push_back(origBinEdit);
341    for(; i != llBinEdits.end(); i++) {
342       if ((*i).second == origBinEdit)
343          continue;
344       as.push_back((*i).second);
345    }
346 }
347
348
349 /*
350  * BPatch_addressSpace::beginInsertionSet
351  * 
352  * Starts a batch insertion set; that is, all calls to insertSnippet until
353  * finalizeInsertionSet are delayed.
354  *
355  */
356
357 void BPatch_binaryEdit::beginInsertionSetInt() 
358 {
359     return;
360 }
361
362
363 bool BPatch_binaryEdit::finalizeInsertionSetInt(bool /*atomic*/, bool * /*modified*/) 
364 {
365     return true;
366 }
367
368 bool BPatch_binaryEdit::loadLibraryInt(const char *libname, bool deps)
369 {
370    std::map<std::string, BinaryEdit*> libs = origBinEdit->openResolvedLibraryName(libname);
371    std::map<std::string, BinaryEdit*>::iterator lib_it;
372    for(lib_it = libs.begin(); lib_it != libs.end(); ++lib_it) {
373       std::pair<std::string, BinaryEdit*> lib = *lib_it;
374
375       if(!lib.second)
376         return false;
377
378       llBinEdits.insert(lib);
379       
380       int_variable* masterTrampGuard = origBinEdit->createTrampGuard();
381       assert(masterTrampGuard);
382       
383       lib.second->registerFunctionCallback(createBPFuncCB);
384       lib.second->registerInstPointCallback(createBPPointCB);
385       lib.second->set_up_ptr(this);
386       lib.second->setupRTLibrary(rtLib);
387       lib.second->setTrampGuard(masterTrampGuard);
388       lib.second->setMultiThreadCapable(isMultiThreadCapable());
389       /* Do we need to do this? 
390       std::map<std::string, BinaryEdit*>::iterator j;
391       for (j = llBinEdits.begin(); j != llBinEdits.end(); j++) {
392             lib.second->addSibling((*j).second);
393       }
394       */
395     if (deps)
396        if( !lib.second->getAllDependencies(llBinEdits) ) return false;
397    }
398        
399   return true;
400 }
401
402 bool BPatch_binaryEdit::isStaticExecutableInt() {
403     return origBinEdit->getMappedObject()->isStaticExec();
404 }
405
406 // Here's the story. We may need to install a trap handler for instrumentation
407 // to work in the rewritten binary. This doesn't play nicely with trap handlers
408 // that the binary itself registers. So we're going to replace every call to
409 // sigaction in the binary with a call to our wrapper. This wrapper:
410 //   1) Ignores attempts to register a SIGTRAP
411 //   2) Passes everything else through to sigaction
412 // It's called "dyn_sigaction".
413 // This is just a multiplexing function over each child binaryEdit
414 // object because they're all individual.
415
416 bool BPatch_binaryEdit::replaceTrapHandler() {
417     // Did we use a trap?
418
419     bool usedATrap = false;
420
421     std::map<std::string, BinaryEdit *>::iterator iter = llBinEdits.begin();
422     for (; iter != llBinEdits.end(); iter++) {
423         if (iter->second->usedATrap()) {
424             usedATrap = true;
425         }
426     }
427
428     if (!usedATrap) return true;
429
430     // We used a trap, so go through and set up the replacement instrumentation.
431     // However, don't let this be the first piece of instrumentation put into
432     // a library.
433
434     bool success = true;
435     iter = llBinEdits.begin();
436     for (; iter != llBinEdits.end(); iter++) {
437         BinaryEdit *binEd = iter->second;
438
439         // Did we instrument this already?
440         if (!binEd->isDirty()) {
441             continue;
442         }
443
444         // Okay, replace trap handler
445         if (!binEd->replaceTrapHandler()) {
446             success = false;
447         }
448     }
449     return success;
450 }
451     
452