*** empty log message ***
[dyninst.git] / dyninstAPI / src / linuxDL.C
1 /*
2  * Copyright (c) 1996 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 #include "dyninstAPI/src/sharedobject.h"
42 #include "dyninstAPI/src/solarisDL.h"
43 #include "dyninstAPI/src/process.h"
44 #include "dyninstAPI/src/symtab.h"
45 #include "util/h/debugOstream.h"
46
47 extern debug_ostream sharedobj_cerr;
48
49 #include <link.h>
50 #include <libelf.h>
51 #include <sys/types.h>
52 #include <sys/procfs.h>
53 #include <sys/auxv.h>
54
55 // get_ld_base_addr: This routine returns the base address of ld.so.1
56 // it returns true on success, and false on error
57 bool dynamic_linking::get_ld_base_addr(u_int &addr, int proc_fd){
58
59     // get number of auxv_t structs
60     int auxvnum;
61     if(ioctl(proc_fd, PIOCNAUXV, &auxvnum) != 0) { return false;}    
62     if(auxvnum < 1) { return false; }
63
64     // get the array of auxv_t structs
65     auxv_t *auxv_elms = new auxv_t[auxvnum];
66     // auxv_t auxv_elms[auxvnum];
67     if(ioctl(proc_fd, PIOCAUXV, auxv_elms) != 0) { return false; }
68
69     // find the base address of ld.so.1
70     for(int i=0; i < auxvnum; i++){
71         if(auxv_elms[i].a_type == AT_BASE) {
72             addr = (u_int)(auxv_elms[i].a_un.a_ptr);
73             // cout << "ld.so.1 base addr = " << addr << "num " 
74             // << auxvnum << endl;
75             delete [] auxv_elms;
76             return true;
77     } }
78     delete [] auxv_elms;
79     return false;
80 }
81
82 // findFunctionIn_ld_so_1: this routine finds the symbol table for ld.so.1 and 
83 // parses it to find the address of symbol r_debug
84 // it returns false on error
85 bool dynamic_linking::findFunctionIn_ld_so_1(string f_name, u_int ld_fd, u_int ld_base_addr, u_int *f_addr, int st_type){
86
87     Elf *elfp = 0;
88     if ((elfp = elf_begin(ld_fd, ELF_C_READ, 0)) == 0) {return false;}
89     Elf32_Ehdr *phdr = elf32_getehdr(elfp);
90     if(!phdr){ elf_end(elfp); return false;}
91
92     Elf_Scn*    shstrscnp  = 0;
93     Elf_Scn*    symscnp = 0;
94     Elf_Scn*    strscnp = 0;
95     Elf_Data*   shstrdatap = 0;
96     if ((shstrscnp = elf_getscn(elfp, phdr->e_shstrndx)) == 0) {
97         elf_end(elfp); 
98         return false;
99     }
100     if((shstrdatap = elf_getdata(shstrscnp, 0)) == 0) {
101         elf_end(elfp); 
102         return false;
103     }
104     const char* shnames = (const char *) shstrdatap->d_buf;
105     Elf_Scn*    scnp    = 0;
106     while ((scnp = elf_nextscn(elfp, scnp)) != 0) {
107         Elf32_Shdr* shdrp = elf32_getshdr(scnp);
108         if (!shdrp) { elf_end(elfp); return false; }
109         const char* name = (const char *) &shnames[shdrp->sh_name];
110         if (strcmp(name, ".symtab") == 0) {
111             symscnp = scnp;
112         }
113         else if (strcmp(name, ".strtab") == 0) {
114             strscnp = scnp;
115         }
116     }
117     if (!strscnp || !symscnp) { elf_end(elfp); return false;}
118
119     Elf_Data* symdatap = elf_getdata(symscnp, 0);
120     Elf_Data* strdatap = elf_getdata(strscnp, 0);
121     if (!symdatap || !strdatap) { elf_end(elfp); return false;}
122     u_int nsyms = symdatap->d_size / sizeof(Elf32_Sym);
123     Elf32_Sym*  syms   = (Elf32_Sym *) symdatap->d_buf;
124     const char* strs   = (const char *) strdatap->d_buf;
125
126     if (f_addr != NULL) *f_addr = 0;
127     for(u_int i=0; i < nsyms; i++){
128         if (syms[i].st_shndx != SHN_UNDEF) {
129             if(ELF32_ST_TYPE(syms[i].st_info) == st_type){
130                 string name = string(&strs[syms[i].st_name]);
131                 if(name == f_name){
132                   if (f_addr != NULL) {
133                     *f_addr = syms[i].st_value + ld_base_addr; 
134                   }
135                   break;
136                 } 
137     } } }
138     elf_end(elfp);
139     if((f_addr != NULL) && ((*f_addr)==0)) { return false; }
140     return true;
141 }
142
143 // find_r_debug: this routine finds the symbol table for ld.so.1, and 
144 // parses it to find the address of symbol r_debug
145 // it returns false on error
146 bool dynamic_linking::find_r_debug(u_int ld_fd,u_int ld_base_addr){
147   return findFunctionIn_ld_so_1("r_debug", ld_fd, ld_base_addr, &r_debug_addr, STT_OBJECT);
148 }
149
150 // find_dlopen: this routine finds the symbol table for ld.so.1, and 
151 // parses it to find the address of symbol dlopen
152 // it returns false on error
153 bool dynamic_linking::find_dlopen(u_int ld_fd,u_int ld_base_addr){
154   return findFunctionIn_ld_so_1("_dlopen", ld_fd, ld_base_addr, &dlopen_addr, STT_FUNC);
155 }
156
157 // set_r_brk_point: this routine instruments the code pointed to by
158 // the r_debug.r_brk (the linkmap update routine).  Currently this code  
159 // corresponds to no function in the symbol table and consists of only
160 // 2 instructions on sparc-solaris (retl nop).  Also, the library that 
161 // contains these instrs is the runtime linker which is not parsed like
162 // other shared objects, so adding instrumentation here is ugly. 
163 // TODO: add support for this on x86
164 bool dynamic_linking::set_r_brk_point(process *proc) {
165
166     if(brkpoint_set) return true;
167     if(!r_brk_addr) return false;
168
169 #if defined(sparc_sun_solaris2_4)
170     // because there is no pdFunction, instPoint, or image associated with
171     // this instrumentation point we can't use all the normal routines to
172     // insert base tramp and minitrap code here.  Instead we replace the 
173     // retl instruction  with a trap instruction.   Then in paradynd in  
174     // the routine that handles the sigtrap for this case we will simulate
175     // the retl instruction by changing the value of the o7 register
176     // so that it looks like the retl instruction has executed
177     instruction trap_insn;
178     trap_insn.raw = BREAK_POINT_INSN;
179     if(!proc->writeDataSpace((caddr_t)r_brk_addr,
180         sizeof(instruction),&trap_insn.raw)){
181         return false;
182     }
183 #else
184     // currently we don't support this on x86 
185     return false;
186 #endif
187
188     brkpoint_set = true;
189     return true;
190 }
191
192
193 // processLinkMaps: This routine is called by getSharedObjects to  
194 // process all shared objects that have been mapped into the process's
195 // address space.  This routine reads the link maps from the application 
196 // process to find the shared object file base mappings. It returns 0 on error.
197 vector<shared_object *> *dynamic_linking::processLinkMaps(process *p) {
198
199     r_debug debug_elm;
200     if(!p->readDataSpace((caddr_t)(r_debug_addr),
201         sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
202         // printf("read d_ptr_addr failed r_debug_addr = 0x%x\n",r_debug_addr);
203         return 0;
204     }
205
206     r_brk_addr = debug_elm.r_brk;
207
208     // get each link_map object
209     bool first_time = true;
210     Link_map *next_link_map = debug_elm.r_map;
211     Address next_addr = (Address)next_link_map; 
212     vector<shared_object*> *shared_objects = new vector<shared_object*>;
213     while(next_addr != 0){
214         Link_map link_elm;
215         if(!p->readDataSpace((caddr_t)(next_addr),
216             sizeof(Link_map),(caddr_t)&(link_elm),true)) {
217             logLine("read next_link_map failed\n");
218             return 0;
219         }
220         // get file name
221         char f_name[256]; // assume no file names greater than 256 chars
222         // check to see if reading 256 chars will go out of bounds
223         // of data segment
224         u_int f_amount = 256;
225         bool done = false;
226         for(u_int i=0; (i<256) && (!done); i++){
227             if(!p->readDataSpace((caddr_t)((u_int)(link_elm.l_name)+i),
228                 sizeof(char),(caddr_t)(&(f_name[i])),true)){
229             }
230             if(f_name[i] == '\0'){
231                 done = true;
232                 f_amount = i+1;
233             }
234         }
235         f_name[f_amount-1] = '\0';
236         string obj_name = string(f_name);
237
238         sharedobj_cerr << 
239             "dynamicLinking::processLinkMaps(): file name of next shared obj="
240             << obj_name << endl;
241
242         // create a shared_object and add it to the list
243         // kludge: ignore the entry if it has the same name as the
244         // executable file...this seems to be the first link-map entry
245         if(obj_name != p->getImage()->file() && 
246            obj_name != p->getImage()->name() &&
247            obj_name != p->getArgv0()) {
248            sharedobj_cerr << 
249                "file name doesn't match image, so not ignoring it...firsttime=" 
250                << first_time << endl;
251
252            // kludge for when an exec occurs...the first element
253            // in the link maps is the file name of the parent process
254            // so in this case, we ignore the first entry
255            if((!(p->wasExeced())) || (p->wasExeced() && !first_time)){ 
256                 shared_object *newobj = new shared_object(obj_name,
257                         link_elm.l_addr,false,true,true,0);
258                 *shared_objects += newobj;
259             }
260         }
261         else {
262            sharedobj_cerr << 
263                "file name matches that of image, so ignoring...firsttime=" 
264                << first_time << endl;
265         }
266
267         first_time = false;
268         next_addr = (u_int)link_elm.l_next;
269     }
270     p->setDynamicLinking();
271     dynlinked = true;
272     return shared_objects;
273     shared_objects = 0;
274 }
275
276
277 // getLinkMapAddrs: returns a vector of addresses corresponding to all 
278 // base addresses in the link maps.  Returns 0 on error.
279 vector<u_int> *dynamic_linking::getLinkMapAddrs(process *p) {
280
281     r_debug debug_elm;
282     if(!p->readDataSpace((caddr_t)(r_debug_addr),
283         sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
284         // printf("read d_ptr_addr failed r_debug_addr = 0x%x\n",r_debug_addr);
285         return 0;
286     }
287
288     bool first_time = true;
289     Link_map *next_link_map = debug_elm.r_map;
290     Address next_addr = (Address)next_link_map; 
291     vector<u_int> *link_addresses = new vector<u_int>;
292     while(next_addr != 0) {
293         Link_map link_elm;
294         if(!p->readDataSpace((caddr_t)(next_addr),
295             sizeof(Link_map),(caddr_t)&(link_elm),true)) {
296             logLine("read next_link_map failed\n");
297             return 0;
298         }
299         // kludge: ignore the first entry
300         if(!first_time) { 
301             *link_addresses += link_elm.l_addr; 
302         }
303         else {
304             // printf("first link map addr 0x%x\n",link_elm.l_addr);
305         }
306
307         first_time = false;
308         next_addr = (u_int)link_elm.l_next;
309     }
310     return link_addresses;
311     link_addresses = 0;
312 }
313
314 // getNewSharedObjects: returns a vector of shared_object one element for
315 // newly mapped shared object.  old_addrs contains the addresses of the
316 // currently mapped shared objects. Sets error_occured to true, and 
317 // returns 0 on error.
318 vector<shared_object *> *dynamic_linking::getNewSharedObjects(process *p,
319                                                 vector<u_int> *old_addrs,
320                                                 bool &error_occured){
321
322     r_debug debug_elm;
323     if(!p->readDataSpace((caddr_t)(r_debug_addr),
324         sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
325         // printf("read d_ptr_addr failed r_debug_addr = 0x%x\n",r_debug_addr);
326         error_occured = true;
327         return 0;
328     }
329
330     // get each link_map object
331     bool first_time = true;
332     Link_map *next_link_map = debug_elm.r_map;
333     Address next_addr = (Address)next_link_map; 
334     vector<shared_object*> *new_shared_objects = new vector<shared_object*>;
335     while(next_addr != 0){
336         Link_map link_elm;
337         if(!p->readDataSpace((caddr_t)(next_addr),
338             sizeof(Link_map),(caddr_t)&(link_elm),true)) {
339             logLine("read next_link_map failed\n");
340             delete new_shared_objects;
341             error_occured = true;
342             return 0;
343         }
344
345         // kludge: ignore the entry 
346         if(!first_time){
347             // check to see if this is a new shared object 
348             bool found = false;
349             for(u_int i=0; i < old_addrs->size(); i++){
350                 if((*old_addrs)[i] == link_elm.l_addr) {
351                     found = true; 
352                     break;
353                 }
354             }
355             if (!found) {  
356                 // this is a new shared object, create a shrared_object for it 
357                 char f_name[256];// assume no file names greater than 256 chars
358                 // check to see if reading 256 chars will go out of bounds
359                 // of data segment
360                 u_int f_amount = 256;
361                 bool done = false;
362                 for(u_int i=0; (i<256) && (!done); i++){
363                     if(!p->readDataSpace((caddr_t)((u_int)(link_elm.l_name)+i),
364                         sizeof(char),(caddr_t)(&(f_name[i])),true)){
365                     }
366                     if(f_name[i] == '\0'){
367                         done = true;
368                         f_amount = i+1;
369                     }
370                 }
371                 f_name[f_amount-1] = '\0';
372                 string obj_name = string(f_name);
373                 shared_object *newobj = new shared_object(obj_name,
374                         link_elm.l_addr,false,true,true,0);
375                 *new_shared_objects += newobj;
376             }
377         }
378         first_time = false;
379         next_addr = (u_int)link_elm.l_next;
380     }
381     error_occured = false;
382     return new_shared_objects;
383     new_shared_objects = 0;
384 }
385
386
387 // getSharedObjects: This routine is called after attaching to
388 // an already running process p, or when a process reaches the breakpoint at
389 // the entry point of main().  It gets all shared objects that have been
390 // mapped into the process's address space, and returns 0 on error or if 
391 // there are no shared objects.
392 // The assumptions are that the dynamic linker has already run, and that
393 // a /proc file descriptor has been opened for the application process.
394 // This is a very kludgy way to get this stuff, but it is the only way to
395 // do it until verision 2.6 of Solaris is released.
396 // TODO: this should also set a breakpoint in the r_brk routine that will
397 // catch future changes to the linkmaps (from dlopen and dlclose)
398 // dlopen events should result in a call to addSharedObject
399 // dlclose events should result in a call to removeASharedObject
400 vector< shared_object *> *dynamic_linking::getSharedObjects(process *p) {
401
402     // step 1: figure out if this is a dynamic executable
403     string dyn_str = string("DYNAMIC");
404     internalSym dyn_sym;
405     bool flag = p->findInternalSymbol(dyn_str,true, dyn_sym);
406     if(!flag){ return 0;}
407     int proc_fd = p->getProcFileDescriptor();
408     if(!proc_fd){ return 0;}
409
410     // step 2: find the base address and file descriptor of ld.so.1
411     u_int ld_base = 0;
412     int ld_fd = -1;
413     if(!(this->get_ld_base_addr(ld_base,proc_fd))) { return 0;}
414     if((ld_fd = ioctl(proc_fd, PIOCOPENM, (caddr_t *)&ld_base)) == -1) { 
415         return 0;
416     } 
417
418     // step 3: get its symbol table and find r_debug
419     if (!(this->find_r_debug(ld_fd,ld_base))) { return 0; }
420     close(ld_fd);
421
422     // step 4: get link-maps and process them
423     vector<shared_object *> *result = this->processLinkMaps(p);
424
425     // step 5: set brkpoint in r_brk to catch dlopen and dlclose events
426     if(!(this->set_r_brk_point(p))){ 
427         // printf("error after step5 in getSharedObjects\n");
428     }
429
430     // additional step: find dlopen - naim
431     if(!(this->get_ld_base_addr(ld_base,proc_fd))) { return 0;}
432     if((ld_fd = ioctl(proc_fd, PIOCOPENM, (caddr_t *)&ld_base)) == -1) { 
433         return 0;
434     }
435     if (!(this->find_dlopen(ld_fd,ld_base))) {
436       logLine("WARNING: we didn't find dlopen in ld.so.1\n");
437     }
438     close(ld_fd);
439
440     fflush(stdout);
441     return (result);
442     result = 0;
443 }
444
445
446 // findChangeToLinkMaps: This routine returns a vector of shared objects
447 // that have been deleted or added to the link maps as indicated by
448 // change_type.  If an error occurs it sets error_occured to true.
449 vector <shared_object *> *dynamic_linking::findChangeToLinkMaps(process *p, 
450                                                    u_int change_type,
451                                                    bool &error_occured) {
452
453     // get list of current shared objects
454     vector<shared_object *> *curr_list = p->sharedObjects();
455     if((change_type == 2) && !curr_list) {
456         error_occured = true;
457         return 0;
458     }
459
460     // if change_type is add then figure out what has been added
461     if(change_type == 1){
462         // create a vector of addresses of the current set of shared objects
463         vector<u_int> *addr_list =  new vector<u_int>;
464         for (u_int i=0; i < curr_list->size(); i++) {
465             *addr_list += ((*curr_list)[i])->getBaseAddress();
466         }
467         vector <shared_object *> *new_shared_objs = 
468                                 getNewSharedObjects(p, addr_list,error_occured);
469         if(!error_occured){
470             delete addr_list;
471             return new_shared_objs; 
472             new_shared_objs = 0;
473         }
474     }
475     // if change_type is remove then figure out what has been removed
476     else if((change_type == 2) && curr_list) {
477         // create a list of base addresses from the linkmaps and
478         // compare them to the addr in vector of shared object to see
479         // what has been removed
480         vector<u_int> *addr_list = getLinkMapAddrs(p);
481         if(addr_list) {
482             vector <shared_object *> *remove_list = new vector<shared_object*>;
483             // find all shared objects that have been removed
484             for(u_int i=0; i < curr_list->size(); i++){
485                 u_int curr_addr = ((*curr_list)[i])->getBaseAddress(); 
486                 bool found = false;
487                 for(u_int j=0; j < addr_list->size(); j++){
488                     if(curr_addr == (*addr_list)[j]){
489                         found = true;
490                         break;
491                     }
492                 }
493                 if(!found) {
494                     *remove_list += (*curr_list)[i];
495                 }
496             }
497             delete addr_list;
498             return remove_list; 
499             remove_list = 0;
500         }
501     }
502     error_occured = true;
503     return 0;
504 }
505
506
507
508
509 // handleIfDueToSharedObjectMapping: returns true if the trap was caused
510 // by a change to the link maps,  If it is, and if the linkmaps state is
511 // safe, it processes the linkmaps to find out what has changed...if it
512 // is not safe it sets the type of change currently going on (specified by
513 // the value of r_debug.r_state in link.h
514 // The added or removed shared objects are returned in changed_objects
515 // the change_type value is set to indicate if the objects have been added 
516 // or removed
517 bool dynamic_linking::handleIfDueToSharedObjectMapping(process *proc,
518                                 vector<shared_object*>  **changed_objects,
519                                 u_int &change_type,
520                                 bool &error_occured){ 
521
522 #if defined(sparc_sun_solaris2_4) 
523   prgregset_t regs;
524   error_occured = false;
525   int proc_fd = proc->getProcFileDescriptor(); 
526   if (ioctl (proc_fd, PIOCGREG, &regs) != -1) {
527     // is the trap instr at r_brk_addr?
528     if(regs[R_PC] == (int)r_brk_addr){ 
529         // find out what has changed in the link map
530         // and process it
531         r_debug debug_elm;
532         if(!proc->readDataSpace((caddr_t)(r_debug_addr),
533             sizeof(r_debug),(caddr_t)&(debug_elm),true)) {
534             // printf("read failed r_debug_addr = 0x%x\n",r_debug_addr);
535             error_occured = true;
536             return true;
537         }
538
539         // if the state of the link maps is consistent then we can read
540         // the link maps, otherwise just set the r_state value
541         change_type = r_state;   // previous state of link maps 
542         r_state = debug_elm.r_state;  // new state of link maps
543         if( debug_elm.r_state == 0){
544             // figure out how link maps have changed, and then create
545             // a list of either all the removed shared objects if this
546             // was a dlclose or the added shared objects if this was a dlopen
547
548             // kludge: the state of the first add can get screwed up
549             // so if both change_type and r_state are 0 set change_type to 1
550             if(change_type == 0) change_type = 1;
551             *changed_objects = findChangeToLinkMaps(proc, change_type,
552                                                   error_occured);
553         } 
554
555         // change the pc so that it will look like the retl instr 
556         // completed: set PC to o7 in current frame
557         // we can do this because this retl doesn't correspond to 
558         // an instrumentation point, so we don't have to worry about 
559         // missing any instrumentation code by making it look like the
560         // retl has already happend
561
562         // first get the value of the stackpointer
563         u_int o7reg = regs[R_O7];  
564         o7reg += 8;
565         if(!(proc->changePC(o7reg))) {
566               // printf("error in changePC handleIfDueToSharedObjectMapping\n");
567               error_occured = true;
568               return true;
569         }
570         return true;
571     }
572   }
573 #endif
574
575   return false; 
576 }
577
578
579