Update copyright to LGPL on all files
[dyninst.git] / dyninstAPI / src / solarisDL.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 // $Id: solarisDL.C,v 1.54 2008/06/19 19:53:44 legendre Exp $
33
34 #include "dyninstAPI/src/mapped_object.h"
35 #include "dyninstAPI/src/dynamiclinking.h"
36 #include "dyninstAPI/src/dyn_lwp.h"
37 #include "dyninstAPI/src/process.h"
38 #include "dyninstAPI/src/symtab.h"
39 #include "common/h/debugOstream.h"
40 #include "symtabAPI/src/Object.h" //TODO: Remove
41 #include <link.h>
42 #include <libelf.h>
43 #include <sys/types.h>
44 #include <procfs.h>
45 #include <sys/auxv.h>
46 #include <string>
47
48
49 // initialize: perform initialization tasks on a platform-specific level
50 bool dynamic_linking::initialize() {
51     // First, initialize all platform-specific stuff
52     r_debug_addr = 0;
53     r_state = 0;
54     // First, find if we're a dynamic executable
55     std::string dyn_str = std::string("DYNAMIC");
56     int_symbol dyn_sym;
57     if( ! proc->getSymbolInfo(dyn_str, dyn_sym)) {
58         bperr( "Failed to find string DYNAMIC\n");
59         return false; 
60     }
61
62     // step 2: find the base address and file descriptor of ld.so.1
63     // Three-step process.
64     // 1) Examine aux vector for address of the loader
65     Address ld_base = 0;
66     dyn_lwp *replwp = proc->getRepresentativeLWP();
67     if(!(this->get_ld_base_addr(ld_base, replwp->auxv_fd()))) {
68         return false;
69     }
70     
71     // 2) Examine virtual address map for the name of the object (inode style)
72     char ld_name[128+PRMAPSZ];
73     if(!(this->get_ld_name(ld_name, ld_base, replwp->map_fd(),
74                            proc->getPid()))) { 
75         return false;
76     }
77     
78     // 3) Open that file
79     int ld_fd = -1;    
80     ld_fd = open(ld_name, O_RDONLY, 0);
81     if(ld_fd == -1) {
82         perror("LD.SO");
83         return false;
84     } 
85
86     // step 3: get its symbol table and find r_debug
87     if (!(this->find_r_debug(ld_fd,ld_base))) { 
88         return false;
89     }
90
91     if (!(this->find_dlopen(ld_fd,ld_base))) {
92         bperr( "WARNING: we didn't find dlopen in ld.so.1\n");
93     }
94
95     P_close(ld_fd);
96
97    dynlinked = true;
98
99     return true;
100 }
101
102
103 // get_ld_base_addr: This routine returns the base address of ld.so.1
104 // it returns true on success, and false on error
105 bool dynamic_linking::get_ld_base_addr(Address &addr, int auxv_fd){
106
107     auxv_t auxv_elm;
108     lseek(auxv_fd, 0, SEEK_SET);
109     while(read(auxv_fd, &auxv_elm, sizeof(auxv_elm)) == sizeof(auxv_elm)) {
110         if (auxv_elm.a_type == AT_BASE) {
111             addr = (Address)auxv_elm.a_un.a_ptr;
112             lseek(auxv_fd, 0, SEEK_SET);
113             return true;
114         }
115         
116     }
117     return false;
118 }
119
120 // get_ld_name: returns name (in /proc/pid/object/ format) of ld.so.1
121 // it returns true on success, and false on error
122 bool dynamic_linking::get_ld_name(char *ld_name, Address ld_base, int map_fd, int pid)
123 {
124     prmap_t map_elm;
125     lseek(map_fd, 0, SEEK_SET);
126     while(read(map_fd, &map_elm, sizeof(map_elm)) == sizeof(map_elm)) {
127         if (map_elm.pr_vaddr == ld_base) {
128             sprintf(ld_name, "/proc/%d/object/%s", 
129                     pid, map_elm.pr_mapname);
130             lseek(map_fd, 0, SEEK_SET);
131             return true;
132         }
133         
134     }
135     return false;
136 }
137
138 // findFunctionIn_ld_so_1: this routine finds the symbol table for ld.so.1 and 
139 // parses it to find the address of symbol r_debug
140 // it returns false on error
141 bool dynamic_linking::findFunctionIn_ld_so_1(std::string f_name, int ld_fd, 
142                                              Address ld_base_addr, 
143                                              Address *f_addr, int st_type)
144 {
145     bool result = false;
146     Elf_X elf;
147     Elf_X_Shdr shstrscn;
148     Elf_X_Data shstrdata;
149     Elf_X_Shdr symscn;
150     Elf_X_Shdr strscn;
151     Elf_X_Data symdata;
152     Elf_X_Data strdata;
153
154     const char *shnames = NULL;
155
156     lseek(ld_fd, 0, SEEK_SET);
157     elf = Elf_X(ld_fd, ELF_C_READ);
158     if (elf.isValid()) shstrscn = elf.get_shdr( elf.e_shstrndx() );
159     if (shstrscn.isValid()) shstrdata = shstrscn.get_data();
160     if (shstrdata.isValid()) shnames = shstrdata.get_string();
161
162     if (elf.isValid() && shstrscn.isValid() && shstrdata.isValid()) {
163         for (int i = 0; i < elf.e_shnum(); ++i) {
164             Elf_X_Shdr shdr = elf.get_shdr(i);
165             if (!shdr.isValid()) return false;
166             const char* name = (const char *) &shnames[shdr.sh_name()];
167
168             if (P_strcmp(name, ".symtab") == 0) {
169                 symscn = shdr;
170
171             } else if (P_strcmp(name, ".strtab") == 0) {
172                 strscn = shdr;
173             }
174         }
175
176         if (strscn.isValid()) symdata = symscn.get_data();
177         if (symscn.isValid()) strdata = strscn.get_data();
178         if (symdata.isValid() && strdata.isValid()) {
179             Elf_X_Sym syms = symdata.get_sym();
180             const char* strs = strdata.get_string();
181
182             if (f_addr != NULL) *f_addr = 0;
183
184             for (u_int i = 0; i < syms.count(); ++i) {
185                 if (syms.st_shndx(i) != SHN_UNDEF) {
186                     if (syms.ST_TYPE(i) == st_type) {
187                         std::string name = std::string(&strs[ syms.st_name(i) ]);
188                         if (name == f_name) {
189                             if (f_addr != NULL) {
190                                 *f_addr = syms.st_value(i) + ld_base_addr;
191                             }
192                             result = true;
193                             break;
194                         }
195                     }
196                 }
197             }
198         }
199     }
200     elf.end();
201
202     return result;
203 }
204
205 // find_r_debug: this routine finds the symbol table for ld.so.1, and 
206 // parses it to find the address of symbol r_debug
207 // it returns false on error
208 bool dynamic_linking::find_r_debug(int ld_fd, Address ld_base_addr){
209   return findFunctionIn_ld_so_1("r_debug", ld_fd, ld_base_addr, &r_debug_addr,
210         STT_OBJECT);
211 }
212
213 // find_dlopen: this routine finds the symbol table for ld.so.1, and 
214 // parses it to find the address of symbol dlopen
215 // it returns false on error
216 bool dynamic_linking::find_dlopen(int ld_fd, Address ld_base_addr){
217   return findFunctionIn_ld_so_1("_dlopen", ld_fd, ld_base_addr, &dlopen_addr,
218         STT_FUNC);
219 }
220
221 // set_r_brk_point: this routine instruments the code pointed to by
222 // the r_debug.r_brk (the linkmap update routine).  Currently this code  
223 // corresponds to no function in the symbol table and consists of only
224 // 2 instructions on sparc-solaris (retl nop).  Also, the library that 
225 // contains these instrs is the runtime linker which is not parsed like
226 // other shared objects, so adding instrumentation here is ugly. 
227
228 sharedLibHook::sharedLibHook(process *p, sharedLibHookType t, Address b) 
229         : proc_(p), type_(t), breakAddr_(b), loadinst_(NULL) {
230
231
232     // Before putting in the breakpoint, save what is currently at the
233     // location that will be overwritten.
234     proc_->readDataSpace((void *)breakAddr_, SLH_SAVE_BUFFER_SIZE,
235                          (void *)saved_, true);
236 #if defined(arch_sparc)
237     // because there is no pdFunction, instPoint, or image associated with
238     // this instrumentation point we can't use all the normal routines to
239     // insert base tramp and minitrap code here.  Instead we replace the 
240     // retl instruction  with a trap instruction.   Then in paradynd in  
241     // the routine that handles the sigtrap for this case we will simulate
242     // the retl instruction by changing the value of the %o7 register
243     // so that it looks like the retl instruction has executed
244     codeGen gen(instruction::size());
245     instruction::generateTrap(gen);
246     proc_->writeDataSpace((caddr_t)breakAddr_,
247                           gen.used(),
248                           gen.start_ptr());
249 #else // x86
250     instruction trap_insn((const unsigned char*)"\017\013\0220\0220",
251         ILLEGAL, 4);
252     proc_->writeDataSpace((void *)breakAddr_, 4, trap_insn.ptr());
253     //proc->SetIllForTrap();
254 #endif
255 }
256
257 sharedLibHook::~sharedLibHook() 
258 {
259     if (proc_->isAttached() && !proc_->execing())
260         return;
261
262     bool ok = proc_->writeDataSpace((void *)breakAddr_, SLH_SAVE_BUFFER_SIZE, saved_);
263     if (!ok) {
264       //  this fails regularly
265       //fprintf(stderr, "%s[%d]:  !!!!  failed to clean up shared lib hook\n", FILE__, __LINE__);
266     }
267 }
268
269
270 // processLinkMaps: This routine is called by getSharedObjects to  
271 // process all shared objects that have been mapped into the process's
272 // address space.  This routine reads the link maps from the application 
273 // process to find the shared object file base mappings. It returns 0 on error.
274 bool dynamic_linking::processLinkMaps(pdvector<fileDescriptor> &descs) {
275    r_debug debug_elm;
276    if(!proc->readDataSpace((caddr_t)(r_debug_addr),
277                         sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
278       // bperr("read d_ptr_addr failed r_debug_addr = 0x%lx\n",r_debug_addr);
279       return 0;
280    }
281
282    // get each link_map object
283    Link_map *next_link_map = debug_elm.r_map;
284    Address next_addr = (Address)next_link_map; 
285    
286    while(next_addr != 0){
287        Link_map link_elm;
288
289        if(!proc->readDataSpace((caddr_t)(next_addr),
290                                sizeof(Link_map),(caddr_t)&(link_elm),true)) {
291            logLine("read next_link_map failed\n");
292            return 0;
293        }
294        // get file name
295        char f_name[256]; // assume no file names greater than 256 chars
296        // check to see if reading 256 chars will go out of bounds
297        // of data segment
298        u_int f_amount = 256;
299        bool done = false;
300        for(u_int i=0; (i<256) && (!done); i++){
301            if(!proc->readDataSpace((caddr_t)((u_int)(link_elm.l_name)+i),
302                                    sizeof(char),(caddr_t)(&(f_name[i])),true)){
303            }
304            if(f_name[i] == '\0'){
305                done = true;
306                f_amount = i+1;
307            }
308        }
309        f_name[f_amount-1] = '\0';
310        string obj_name = string(f_name);
311
312       parsing_cerr << 
313          "dynamicLinking::processLinkMaps(): file name of next shared obj="
314                      << obj_name << endl;
315
316       // create a mapped_object and add it to the list
317       // kludge: ignore the entry if it has the same name as the
318       // executable file...this seems to be the first link-map entry
319       // VG(09/25/01): also ignore if address is 65536 or name is (unknown)
320       if(obj_name != proc->getAOut()->fileName() && 
321          obj_name != proc->getAOut()->fullName() &&
322          link_elm.l_addr != 65536 &&
323          obj_name != "(unknown)"
324          //strncmp(obj_name.c_str(), "(unknown)", 10)
325          ) {
326           
327           fileDescriptor desc = fileDescriptor(obj_name, link_elm.l_addr,
328                                                link_elm.l_addr,
329                                                true);
330           descs.push_back(desc);          
331       }
332       
333       next_addr = (Address)link_elm.l_next;
334    }
335
336    return true;
337 }
338
339
340 // getLinkMapAddrs: returns a vector of addresses corresponding to all 
341 // base addresses in the link maps.  Returns 0 on error.
342 pdvector<Address> *dynamic_linking::getLinkMapAddrs() {
343
344     r_debug debug_elm;
345     if(!proc->readDataSpace((caddr_t)(r_debug_addr),
346         sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
347         // bperr("read d_ptr_addr failed r_debug_addr = 0x%lx\n",r_debug_addr);
348         return 0;
349     }
350
351     bool first_time = true;
352     Link_map *next_link_map = debug_elm.r_map;
353     Address next_addr = (Address)next_link_map; 
354     pdvector<Address> *link_addresses = new pdvector<Address>;
355     while(next_addr != 0) {
356         Link_map link_elm;
357         if(!proc->readDataSpace((caddr_t)(next_addr),
358             sizeof(Link_map),(caddr_t)&(link_elm),true)) {
359             logLine("read next_link_map failed\n");
360             return 0;
361         }
362         // kludge: ignore the first entry
363         if(!first_time) { 
364             (*link_addresses).push_back(link_elm.l_addr); 
365         }
366         else {
367             // bperr("first link map addr 0x%x\n",link_elm.l_addr);
368         }
369
370         first_time = false;
371         next_addr = (Address)link_elm.l_next;
372     }
373     return link_addresses;
374     link_addresses = 0;
375 }
376
377 /* Return true if the exception was caused by the hook in the linker
378  * code, we'll worry about whether or not any libraries were
379  * added/removed later on when we handle the exception
380  */
381 bool dynamic_linking::decodeIfDueToSharedObjectMapping(EventRecord &,
382                                                        unsigned int & /* change_type */) 
383 {
384    // multi-threaded: possible one of many threads hit the breakpoint
385
386    pdvector<Frame> activeFrames;
387    if (!proc->getAllActiveFrames(activeFrames)) {
388       return false;
389    }
390
391    dyn_lwp *brk_lwp = NULL;
392    sharedLibHook *hook = NULL;
393    for (unsigned frame_iter = 0; frame_iter < activeFrames.size();frame_iter++)
394    {
395        hook = reachedLibHook(activeFrames[frame_iter].getPC());
396        if (hook) {
397            brk_lwp = activeFrames[frame_iter].getLWP();
398            break;
399        }
400    }
401    return (bool) hook;
402 }
403
404 // handleIfDueToSharedObjectMapping: returns true if the trap was caused
405 // by a change to the link maps,  If it is, and if the linkmaps state is
406 // safe, it processes the linkmaps to find out what has changed...if it
407 // is not safe it sets the type of change currently going on (specified by
408 // the value of r_debug.r_state in link.h
409 // The added or removed shared objects are returned in changed_objects
410 // the change_type value is set to indicate if the objects have been added 
411 // or removed
412 bool dynamic_linking::handleIfDueToSharedObjectMapping(EventRecord &ev, 
413                                  pdvector<mapped_object*> &changed_objects,
414                                  pdvector<bool> &is_new_object)
415 {
416
417    struct dyn_saved_regs regs;
418
419    // multi-threaded: possible one of many threads hit the breakpoint
420
421    pdvector<Frame> activeFrames;
422    if (!proc->getAllActiveFrames(activeFrames)) {
423       return false;
424    }
425
426    dyn_lwp *brk_lwp = NULL;
427    sharedLibHook *hook = NULL;
428    for (unsigned frame_iter = 0; frame_iter < activeFrames.size();frame_iter++)
429    {
430        hook = reachedLibHook(activeFrames[frame_iter].getPC());
431        if (hook) {
432            brk_lwp = activeFrames[frame_iter].getLWP();
433            break;
434        }
435    }
436
437    if (brk_lwp || force_library_load) {
438        // find out what has changed in the link map
439       // and process it
440       r_debug debug_elm;
441       if(!proc->readDataSpace((caddr_t)(r_debug_addr),
442                               sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
443           // bperr("read failed r_debug_addr = 0x%x\n",r_debug_addr);
444           return false;
445       }
446       
447       // if the state of the link maps is consistent then we can read
448       // the link maps, otherwise just set the r_state value
449       ev.what = r_state;   // previous state of link maps 
450       r_state = debug_elm.r_state;  // new state of link maps
451
452       if( debug_elm.r_state == 0) {
453           // figure out how link maps have changed, and then create
454          // a list of either all the removed shared objects if this
455          // was a dlclose or the added shared objects if this was a dlopen
456       
457          // kludge: the state of the first add can get screwed up
458          // so if both change_type and r_state are 0 set change_type to 1
459          if(ev.what == 0) ev.what = SHAREDOBJECT_ADDED;
460          findChangeToLinkMaps(changed_objects, is_new_object);
461       } 
462       
463       // Don't need to reset PC
464       if (!force_library_load) {
465           assert(brk_lwp);
466           
467           // Get the registers for this lwp
468           brk_lwp->getRegisters(&regs);
469 #if defined(arch_sparc)
470           // change the pc so that it will look like the retl instr 
471           // completed: set PC to o7 in current frame
472           // we can do this because this retl doesn't correspond to 
473           // an instrumentation point, so we don't have to worry about 
474           // missing any instrumentation code by making it look like the
475           // retl has already happend
476           
477           // first get the value of the stackpointer
478           Address o7reg = regs.theIntRegs[R_O7];
479           o7reg += 2*instruction::size();
480           if(!(brk_lwp->changePC(o7reg, NULL))) {
481               // bperr("error in changePC handleIfDueToSharedObjectMapping\n");
482               return false;
483           }
484 #else //x86
485       // set the pc to the "ret" instruction
486           Address next_pc = regs[R_PC] + instruction::size();
487           if (!brk_lwp->changePC(next_pc))
488               return false;
489 #endif
490       }
491       if (changed_objects.size() == 0) ev.what = 0;
492       return true;
493
494    }
495   return false;
496 }
497
498
499 // This function performs all initialization necessary to catch shared object
500 // loads and unloads (symbol lookups and trap setting)
501 bool dynamic_linking::installTracing()
502 {
503     assert(r_debug_addr);
504     
505     r_debug debug_elm;
506     if(!proc->readDataSpace((caddr_t)(r_debug_addr),
507                             sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
508         // bperr("read d_ptr_addr failed r_debug_addr = 0x%lx\n",r_debug_addr);
509         return 0;
510     }
511
512     sharedLibHook *sharedHook = new sharedLibHook(proc, SLH_UNKNOWN, // not used
513                                                   debug_elm.r_brk);
514     sharedLibHooks_.push_back(sharedHook);
515
516     return true;
517 }
518
519
520