Redid static ctor/dtor handling to be compatible with init_array/fini_array as well...
[dyninst.git] / dyninstAPI / src / parse-x86.C
1 /*
2  * See the dyninst/COPYRIGHT file for copyright information.
3  * 
4  * We provide the Paradyn Tools (below described as "Paradyn")
5  * on an AS IS basis, and do not warrant its validity or performance.
6  * We reserve the right to update, modify, or discontinue this
7  * software at any time.  We shall have no obligation to supply such
8  * updates or modifications or any other form of support to you.
9  * 
10  * By your use of Paradyn, you understand and agree that we (or any
11  * other person or entity with proprietary rights in Paradyn) are
12  * under no obligation to provide either maintenance services,
13  * update services, notices of latent defects, or correction of
14  * defects for Paradyn.
15  * 
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  * 
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  * 
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29  */
30
31 /*
32  * inst-x86.C - x86 dependent functions and code generator
33  */
34
35 #include "common/src/Vector.h"
36 #include <unordered_map>
37 #include "common/src/Vector.h"
38 #include "parse-cfg.h"
39 #include "instPoint.h"
40 #include "mapped_object.h"
41 #include "image.h"
42 #include "debug.h"
43 #include <deque>
44 #include <set>
45 #include <algorithm>
46 //#include "arch.h"
47
48 #include "instructionAPI/h/Instruction.h"
49 #include "instructionAPI/h/InstructionDecoder.h"
50
51 using namespace Dyninst::ParseAPI;
52
53 bool parse_func::writesFPRs(unsigned level) {
54     
55     using namespace Dyninst::InstructionAPI;
56     // Oh, we should be parsed by now...
57     if (!parsed()) image_->analyzeIfNeeded();
58
59     if (containsFPRWrites_ == unknown) {
60         // Iterate down and find out...
61         // We know if we have callees because we can
62         // check the instPoints; no reason to iterate over.
63         // We also cache callee values here for speed.
64
65         if (level >= 3) {
66             return true; // Arbitrarily decided level 3 iteration.
67         }        
68         const Function::edgelist & calls = callEdges();
69         Function::edgelist::const_iterator cit = calls.begin();
70         for( ; cit != calls.end(); ++cit) {
71             image_edge * ce = static_cast<image_edge*>(*cit);
72             parse_func * ct = static_cast<parse_func*>(
73                 obj()->findFuncByEntry(region(),ce->trg()->start()));
74             if(ct && ct != this) {
75                 if (ct->writesFPRs(level+1)) {
76                     // One of our kids does... if we're top-level, cache it; in 
77                     // any case, return
78                     if (level == 0)
79                         containsFPRWrites_ = used;
80                     return true;
81                 }
82             }
83             else if(!ct){
84                 // Indirect call... oh, yeah. 
85                 if (level == 0)
86                     containsFPRWrites_ = used;
87                 return true;
88             }
89         }
90
91         // No kids contain writes. See if our code does.
92         static RegisterAST::Ptr st0(new RegisterAST(x86::st0));
93         static RegisterAST::Ptr st1(new RegisterAST(x86::st1));
94         static RegisterAST::Ptr st2(new RegisterAST(x86::st2));
95         static RegisterAST::Ptr st3(new RegisterAST(x86::st3));
96         static RegisterAST::Ptr st4(new RegisterAST(x86::st4));
97         static RegisterAST::Ptr st5(new RegisterAST(x86::st5));
98         static RegisterAST::Ptr st6(new RegisterAST(x86::st6));
99         static RegisterAST::Ptr st7(new RegisterAST(x86::st7));
100         static RegisterAST::Ptr xmm0(new RegisterAST(x86::xmm0));
101         static RegisterAST::Ptr xmm1(new RegisterAST(x86::xmm1));
102         static RegisterAST::Ptr xmm2(new RegisterAST(x86::xmm2));
103         static RegisterAST::Ptr xmm3(new RegisterAST(x86::xmm3));
104         static RegisterAST::Ptr xmm4(new RegisterAST(x86::xmm4));
105         static RegisterAST::Ptr xmm5(new RegisterAST(x86::xmm5));
106         static RegisterAST::Ptr xmm6(new RegisterAST(x86::xmm6));
107         static RegisterAST::Ptr xmm7(new RegisterAST(x86::xmm7));
108
109         vector<FuncExtent *>::const_iterator eit = extents().begin();
110         for( ; eit != extents().end(); ++eit) {
111             FuncExtent * fe = *eit;
112         
113             const unsigned char* buf = (const unsigned char*)
114                 isrc()->getPtrToInstruction(fe->start());
115             if(!buf) {
116                 parsing_printf("%s[%d]: failed to get insn ptr at %lx\n",
117                     FILE__, __LINE__,fe->start());
118                 // if the function cannot be parsed, it is only safe to 
119                 // assume that the FPRs are written -- mcnulty
120                 return true; 
121             }
122             InstructionDecoder d(buf,fe->end()-fe->start(),isrc()->getArch());
123             Instruction::Ptr i;
124
125             while(i = d.decode()) {
126                 if(i->isWritten(st0) ||
127                     i->isWritten(st1) ||
128                     i->isWritten(st2) ||
129                     i->isWritten(st3) ||
130                     i->isWritten(st4) ||
131                     i->isWritten(st5) ||
132                     i->isWritten(st6) ||
133                     i->isWritten(st7) ||
134                    i->isWritten(xmm0) ||
135                    i->isWritten(xmm1) ||
136                    i->isWritten(xmm2) ||
137                    i->isWritten(xmm3) ||
138                    i->isWritten(xmm4) ||
139                    i->isWritten(xmm5) ||
140                    i->isWritten(xmm6) ||
141                    i->isWritten(xmm7)
142                   )
143                 {
144                     containsFPRWrites_ = used;
145                     return true;
146                 }
147             }
148         }
149         // No kids do, and we don't. Impressive.
150         containsFPRWrites_ = unused;
151         return false;
152     }
153     else if (containsFPRWrites_ == used) {
154         return true;
155     }
156     else if (containsFPRWrites_ == unused) {
157         return false;
158     }
159
160     fprintf(stderr, "ERROR: function %s, containsFPRWrites_ is %d (illegal value!)\n", 
161             symTabName().c_str(), containsFPRWrites_);
162     
163     assert(0);
164     return false;
165 }
166
167 #if defined(os_linux) || defined(os_freebsd)
168
169 #include "binaryEdit.h"
170 #include "addressSpace.h"
171 #include "function.h"
172 #include "baseTramp.h"
173 #include "image.h"
174
175 using namespace Dyninst::SymtabAPI;
176
177 /*
178  * Static binary rewriting support
179  *
180  * Some of the following functions replace the standard ctor and dtor handlers
181  * in a binary. Currently, these operations only work with binaries linked with
182  * the GNU toolchain. However, it should be straightforward to extend these
183  * operations to other toolchains.
184  */
185 static const std::string LIBC_CTOR_HANDLER("__libc_csu_init");
186 static const std::string LIBC_DTOR_HANDLER("__libc_csu_fini");
187 static const std::string DYNINST_CTOR_HANDLER("DYNINSTglobal_ctors_handler");
188 static const std::string DYNINST_CTOR_BEGIN("DYNINSTctors_begin");
189 static const std::string DYNINST_CTOR_END("DYNINSTctors_end");
190 static const std::string DYNINST_DTOR_HANDLER("DYNINSTglobal_dtors_handler");
191 static const std::string DYNINST_DTOR_BEGIN("DYNINSTdtors_begin");
192 static const std::string DYNINST_DTOR_END("DYNINSTdtors_end");
193 static const std::string SYMTAB_CTOR_LIST_REL("__SYMTABAPI_CTOR_LIST__");
194 static const std::string SYMTAB_DTOR_LIST_REL("__SYMTABAPI_DTOR_LIST__");
195 static const std::string LIBC_IREL_HANDLER("__libc_csu_irel");
196 static const std::string DYNINST_IREL_HANDLER("DYNINSTglobal_irel_handler");
197 static const std::string DYNINST_IREL_START("DYNINSTirel_start");
198 static const std::string DYNINST_IREL_END("DYNINSTirel_end");
199 static const std::string SYMTAB_IREL_START("__SYMTABAPI_IREL_START__");
200 static const std::string SYMTAB_IREL_END("__SYMTABAPI_IREL_END__");
201
202
203 static bool replaceHandler(func_instance *origHandler, func_instance *newHandler, 
204                            std::vector<std::pair<int_symbol *, std::string> > &reloc_replacements) {
205     // Add instrumentation to replace the function
206    // TODO: this should be a function replacement!
207    // And why the hell is it in parse-x86.C?
208    origHandler->proc()->replaceFunction(origHandler, newHandler);
209    AddressSpace::patch(origHandler->proc());
210
211    for (auto iter = reloc_replacements.begin(); iter != reloc_replacements.end(); ++iter) {
212      int_symbol *newList = iter->first;
213      std::string listRelName = iter->second;
214
215      /* create the special relocation for the new list -- search the RT library for
216       * the symbol
217       */
218      Symbol *newListSym = const_cast<Symbol *>(newList->sym());
219      
220      std::vector<Region *> allRegions;
221      if( !newListSym->getSymtab()->getAllRegions(allRegions) ) {
222        return false;
223      }
224      
225      std::vector<Region *>::iterator reg_it;
226      bool found = false;
227      for(reg_it = allRegions.begin(); reg_it != allRegions.end(); ++reg_it) {
228        std::vector<relocationEntry> &region_rels = (*reg_it)->getRelocations();
229        vector<relocationEntry>::iterator rel_it;
230        for( rel_it = region_rels.begin(); rel_it != region_rels.end(); ++rel_it) {
231          if( rel_it->getDynSym() == newListSym ) {
232            relocationEntry *rel = &(*rel_it);
233            rel->setName(listRelName);
234            found = true;
235          }
236        }
237      }
238      if (!found) {
239        return false;
240      }
241    }
242
243    return true;
244 }
245
246 void add_handler(instPoint* pt, func_instance* add_me)
247 {
248   vector<AstNodePtr> args;
249   // no args, just add
250   AstNodePtr snip = AstNode::funcCallNode(add_me, args);
251   auto instrumentation = pt->pushFront(snip);
252   instrumentation->disableRecursiveGuard();
253 }
254
255
256 static bool replaceHandler(func_instance *origHandler, func_instance *newHandler,
257                            int_symbol *sym, std::string name) {
258   std::vector<std::pair<int_symbol *, std::string> > tmp;
259   tmp.push_back(make_pair(sym, name));
260   return replaceHandler(origHandler, newHandler, tmp);
261 }
262
263 bool BinaryEdit::doStaticBinarySpecialCases() {
264     Symtab *origBinary = mobj->parse_img()->getObject();
265
266     /* Special Case 1: Handling global constructor and destructor Regions
267      *
268      * Replace global ctors function with special ctors function,
269      * and create a special relocation for the ctors list used by the special
270      * ctors function
271      *
272      * Replace global dtors function with special dtors function,
273      * and create a special relocation for the dtors list used by the special
274      * dtors function
275      */
276
277     // First, find all the necessary symbol info.
278
279     func_instance *globalCtorHandler = mobj->findGlobalConstructorFunc(LIBC_CTOR_HANDLER);
280     if( !globalCtorHandler ) {
281         logLine("failed to find libc destructor handler\n");
282         return false;
283     }
284     func_instance *dyninstCtorHandler = findOnlyOneFunction(DYNINST_CTOR_HANDLER);
285     if( !dyninstCtorHandler ) {
286         logLine("failed to find Dyninst constructor handler\n");
287         return false;
288     }
289
290     func_instance *globalDtorHandler = mobj->findGlobalDestructorFunc(LIBC_DTOR_HANDLER);
291     if( !globalDtorHandler ) {
292         logLine("failed to find libc destructor handler\n");
293         return false;
294     }
295
296     func_instance *dyninstDtorHandler = findOnlyOneFunction(DYNINST_DTOR_HANDLER);
297     if( !dyninstDtorHandler ) {
298         logLine("failed to find Dyninst destructor handler\n");
299         return false;
300     }
301     // Wire in our handlers at libc ctor exit/dtor entry
302     vector<instPoint*> init_pts;
303     instPoint* fini_point;
304     globalCtorHandler->funcExitPoints(&init_pts);
305     fini_point = globalDtorHandler->funcEntryPoint(true);
306     // convert points to instpoints
307     for(auto exit_pt = init_pts.begin();
308         exit_pt != init_pts.end();
309         ++exit_pt)
310     {
311       add_handler(*exit_pt, dyninstCtorHandler);
312     }
313     add_handler(fini_point, dyninstDtorHandler);
314     AddressSpace::patch(this);
315     
316
317     /*
318      * Replace the irel handler with our extended version, since they
319      * hard-code ALL THE OFFSETS in the function
320      */
321     func_instance *globalIrelHandler = findOnlyOneFunction(LIBC_IREL_HANDLER);
322     func_instance *dyninstIrelHandler = findOnlyOneFunction(DYNINST_IREL_HANDLER);
323     int_symbol irelStart;
324     int_symbol irelEnd;
325     bool irs_found = false;
326     bool ire_found = false;
327     for (auto rtlib_it = rtlib.begin(); rtlib_it != rtlib.end(); ++rtlib_it) {
328       if( (*rtlib_it)->getSymbolInfo(DYNINST_IREL_START, irelStart) ) {
329         irs_found = true;
330       }
331       
332       if( (*rtlib_it)->getSymbolInfo(DYNINST_IREL_END, irelEnd) ) {
333         ire_found = true;
334       }
335       if (irs_found && ire_found) break;
336     }
337     if (globalIrelHandler) {
338       assert(dyninstIrelHandler);
339       assert(irs_found);
340       assert(ire_found);
341       std::vector<std::pair<int_symbol *, string> > tmp;
342       tmp.push_back(make_pair(&irelStart, SYMTAB_IREL_START));
343       tmp.push_back(make_pair(&irelEnd, SYMTAB_IREL_END));
344       if (!replaceHandler(globalIrelHandler, dyninstIrelHandler, tmp)) {
345         return false;
346       }
347     }
348
349
350     /*
351      * Special Case 2: Issue a warning if attempting to link pthreads into a binary
352      * that originally did not support it or into a binary that is stripped. This
353      * scenario is not supported with the initial release of the binary rewriter for
354      * static binaries.
355      *
356      * The other side of the coin, if working with a binary that does have pthreads
357      * support, pthreads needs to be loaded.
358      */
359     bool isMTCapable = isMultiThreadCapable();
360     bool foundPthreads = false;
361
362     vector<Archive *> libs;
363     vector<Archive *>::iterator libIter;
364     if( origBinary->getLinkingResources(libs) ) {
365         for(libIter = libs.begin(); libIter != libs.end(); ++libIter) {
366             if( (*libIter)->name().find("libpthread") != std::string::npos ||
367                 (*libIter)->name().find("libthr") != std::string::npos ) 
368             {
369                 foundPthreads = true;
370                 break;
371             }
372         }
373     }
374
375     if( foundPthreads && (!isMTCapable || origBinary->isStripped()) ) {
376         fprintf(stderr,
377             "\nWARNING: the pthreads library has been loaded and\n"
378             "the original binary is not multithread-capable or\n"
379             "it is stripped. Currently, the combination of these two\n"
380             "scenarios is unsupported and unexpected behavior may occur.\n");
381     }else if( !foundPthreads && isMTCapable ) {
382         fprintf(stderr,
383             "\nWARNING: the pthreads library has not been loaded and\n"
384             "the original binary is multithread-capable. Unexpected\n"
385             "behavior may occur because some pthreads routines are\n"
386             "unavailable in the original binary\n");
387     }
388
389     /* 
390      * Special Case 3:
391      * The RT library has some dependencies -- Symtab always needs to know
392      * about these dependencies. So if the dependencies haven't already been
393      * loaded, load them.
394      */
395     bool loadLibc = true;
396
397     for(libIter = libs.begin(); libIter != libs.end(); ++libIter) {
398         if( (*libIter)->name().find("libc.a") != std::string::npos ) {
399             loadLibc = false;
400         }
401     }
402
403     if( loadLibc ) {
404        std::map<std::string, BinaryEdit *> res;
405        openResolvedLibraryName("libc.a", res);
406
407        if (res.empty()) {
408          cerr << "Fatal error: failed to load DyninstAPI_RT library dependency (libc.a)" << endl;
409          return false;
410        }
411
412        std::map<std::string, BinaryEdit *>::iterator bedit_it;
413        for(bedit_it = res.begin(); bedit_it != res.end(); ++bedit_it) {
414           if( bedit_it->second == NULL ) {
415             cerr << "Fatal error: failed to load DyninstAPI_RT library dependency (libc.a)" << endl;
416             return false;
417           }
418        }
419     }
420     
421     return true;
422 }
423
424 func_instance *mapped_object::findGlobalConstructorFunc(const std::string &ctorHandler) {
425     using namespace Dyninst::InstructionAPI;
426
427     const pdvector<func_instance *> *funcs = findFuncVectorByMangled(ctorHandler);
428     if( funcs != NULL ) {
429         return funcs->at(0);
430     }
431     return NULL;
432 }
433
434 func_instance *mapped_object::findGlobalDestructorFunc(const std::string &dtorHandler) {
435     using namespace Dyninst::InstructionAPI;
436
437     const pdvector<func_instance *> *funcs = findFuncVectorByMangled(dtorHandler);
438     if( funcs != NULL ) {
439         return funcs->at(0);
440     }
441     return NULL;
442 }
443
444
445 #endif