Update copyright to LGPL on all files
[dyninst.git] / dyninstAPI / src / syscall-solproc.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: syscall-solproc.C,v 1.22 2008/09/19 00:56:09 jaw Exp $
33
34 #if defined(os_aix)
35 #include <sys/procfs.h>
36 #else
37 #include <procfs.h>
38 #endif
39 #include "common/h/headers.h"
40 #include "dyninstAPI/src/inst.h"
41 #include "dyninstAPI/src/syscallNotification.h"
42 #include "dyninstAPI/src/sol_proc.h"
43 #include "dyninstAPI/src/process.h"
44 #include "dyninstAPI/src/miniTramp.h"
45 #include "dyninstAPI/src/EventHandler.h"
46 #include "dyninstAPI/src/symtab.h"
47
48 #include "dyninstAPI/src/ast.h"
49
50 #define FORK_FUNC "fork"
51 #define FORK_LIB  "libc.a"
52
53 #define LWP_EXIT_FUNC "_thr_exit_common"
54
55 syscallNotification::syscallNotification(syscallNotification *parentSN,
56                                          process *child) :
57     preForkInst(parentSN->preForkInst),
58     postForkInst(parentSN->postForkInst),
59     preExecInst(parentSN->preExecInst),
60     postExecInst(parentSN->postExecInst),
61     preExitInst(parentSN->preExitInst),
62     preLwpExitInst(parentSN->preLwpExitInst),
63     proc(child) {
64
65     // We set PR_FORK in the parent, so everything was copied.
66     
67     if (parentSN->postForkInst &&
68         (parentSN->postForkInst != SYSCALL_INSTALLED))
69         postForkInst = new instMapping(parentSN->postForkInst, child);
70
71     // I thought we needed this for a while, but we don't. Leave it here 
72     // anyway
73     if (parentSN->preLwpExitInst &&
74         (parentSN->preLwpExitInst != SYSCALL_INSTALLED))
75         preLwpExitInst = new instMapping(parentSN->preLwpExitInst, child);
76
77 }
78
79 /////////// Prefork instrumentation 
80
81 bool syscallNotification::installPreFork() {
82     // Get existing flags, add pre-fork, and set
83     SYSSET_DECLAREPID(proc_pid, proc->getPid());
84     
85     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
86     
87     if (!proc->get_entry_syscalls(entryset)) return false;
88
89     if (SYSSET_MAP(SYS_fork, proc_pid) != -1) {
90         praddsysset(entryset, SYSSET_MAP(SYS_fork, proc_pid));
91     }
92     if (SYSSET_MAP(SYS_fork1, proc_pid) != -1) {
93         praddsysset(entryset, SYSSET_MAP(SYS_fork1, proc_pid));
94     }
95     if (SYSSET_MAP(SYS_vfork, proc_pid) != -1) {
96         praddsysset(entryset, SYSSET_MAP(SYS_vfork, proc_pid));
97     }
98     if (!proc->set_entry_syscalls(entryset)) return false;;
99     SYSSET_FREE(entryset);
100     // Make sure our removal code gets run
101     preForkInst = SYSCALL_INSTALLED;
102     return true;
103 }
104
105 /////////// Postfork instrumentation
106
107 bool syscallNotification::installPostFork() 
108 {
109 #if defined(os_aix)
110     /* Block-comment
111        
112     On AIX, for reasons which are unclear, we cannot count on the OS
113     to copy the address space of the parent to the child on fork. In
114     particular, library text and the a.out text are _not_
115     copied. Areas allocated with mmap (data areas, effectively) are.
116
117     Also, if we tell the process to stop (via /proc) on the exit of
118     fork, we see one of two cases: either the child does not stop (AIX
119     5.1), or the child stops in an unmodifiable state (AIX
120     5.2). Neither is acceptable.
121
122     Our solution is to use instrumentation at the exit of
123     fork(). However, this is complicated by the non-copy behavior of
124     AIX. Our solution is relocation; we ensure that the fork() that is
125     executed is located in a data area. As a result, it is copied to
126     the child, and the child picks up executing there.
127     */
128
129
130     AstNodePtr returnVal = AstNode::operandNode(AstNode::ReturnVal, (void *)0);
131     std::string ffunc(FORK_FUNC);
132     std::string ftarget("DYNINST_instForkExit");
133     std::string flib(FORK_LIB);
134     postForkInst = new instMapping(ffunc, ftarget,
135                                    FUNC_EXIT|FUNC_ARG,
136                                    returnVal,
137                                    flib);
138 #if 0
139     postForkInst = new instMapping(FORK_FUNC, "DYNINST_instForkExit",
140                                    FUNC_EXIT|FUNC_ARG,
141                                    returnVal,
142                                    FORK_LIB);
143 #endif
144     postForkInst->dontUseTrampGuard();
145     
146     pdvector<instMapping *> instReqs;
147     instReqs.push_back(postForkInst);
148     
149     proc->installInstrRequests(instReqs);
150     
151     // Check to see if we put anything in the proggie
152     if (postForkInst->miniTramps.size() == 0) {
153        fprintf(stderr, "%s[%d]:  WARNING:  failed to generate inst for post-fork\n", 
154              FILE__, __LINE__);
155         return false;
156     }
157     return true;
158 #else
159     // Get existing flags, add post-fork, and set
160     SYSSET_DECLAREPID(proc_pid,proc->getPid()); 
161     
162     sysset_t *exitset = SYSSET_ALLOC(proc_pid);
163     
164     if (!proc->get_exit_syscalls(exitset)) return false;;
165
166     if (SYSSET_MAP(SYS_fork, proc_pid) != -1) {
167         praddsysset(exitset, SYSSET_MAP(SYS_fork, proc_pid));
168     }
169     if (SYSSET_MAP(SYS_fork1, proc_pid) != -1) {
170         praddsysset(exitset, SYSSET_MAP(SYS_fork1, proc_pid));
171     }
172     if (SYSSET_MAP(SYS_vfork, proc_pid) != -1) {
173         praddsysset(exitset, SYSSET_MAP(SYS_vfork, proc_pid));
174     }
175     if (!proc->set_exit_syscalls(exitset)) return false;;
176     SYSSET_FREE(exitset);
177     // Make sure our removal code gets run
178     postForkInst = SYSCALL_INSTALLED;
179     return true;
180 #endif
181 }    
182
183 /////////// Pre-exec instrumentation
184
185 bool syscallNotification::installPreExec() {
186     // Get existing flags, add pre-exec, and set
187     SYSSET_DECLAREPID(proc_pid,proc->getPid());
188     
189     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
190     
191     if (!proc->get_entry_syscalls(entryset)) return false;;
192
193     if (SYSSET_MAP(SYS_exec, proc_pid) != -1) {
194         praddsysset(entryset, SYSSET_MAP(SYS_exec, proc_pid));
195     }
196     if (SYSSET_MAP(SYS_execve, proc_pid) != -1) {
197         praddsysset(entryset, SYSSET_MAP(SYS_execve, proc_pid));
198     }
199     if (!proc->set_entry_syscalls(entryset)) return false;;
200     SYSSET_FREE(entryset);
201     // Make sure our removal code gets run
202     preExecInst = SYSCALL_INSTALLED;
203     return true;
204 }    
205
206 //////////// Post-exec instrumentation
207
208 bool syscallNotification::installPostExec() {
209     // Get existing flags, add post-exec, and set
210     SYSSET_DECLAREPID(proc_pid,proc->getPid());
211     
212     sysset_t *exitset = SYSSET_ALLOC(proc_pid);
213     
214     if (!proc->get_exit_syscalls(exitset)) return false;;
215
216     if (SYSSET_MAP(SYS_exec, proc_pid) != -1) {
217         praddsysset(exitset, SYSSET_MAP(SYS_exec, proc_pid));
218     }
219     if (SYSSET_MAP(SYS_execve, proc_pid) != -1) {
220         praddsysset(exitset, SYSSET_MAP(SYS_execve, proc_pid));
221     }
222     if (!proc->set_exit_syscalls(exitset)) return false;;
223     SYSSET_FREE(exitset);
224     // Make sure our removal code gets run
225     postExecInst = SYSCALL_INSTALLED;
226     return true;
227 }    
228
229 /////////// Pre-exit instrumentation
230
231 bool syscallNotification::installPreExit() {
232     // Get existing flags, add pre-exit, and set
233     SYSSET_DECLAREPID(proc_pid, proc->getPid());
234     
235     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
236     
237     if (!proc->get_entry_syscalls(entryset)) return false;;
238
239     if (SYSSET_MAP(SYS_exit, proc_pid) != -1) {
240         praddsysset(entryset, SYSSET_MAP(SYS_exit, proc_pid));
241     }
242     if (!proc->set_entry_syscalls(entryset)) return false;;
243     // Make sure our removal code gets run
244     preExitInst = SYSCALL_INSTALLED;
245     SYSSET_FREE(entryset);
246     return true;
247 }    
248
249
250 /////////// Pre-lwp-exit instrumentation
251
252 bool syscallNotification::installPreLwpExit() {
253     // Get existing flags, add pre-lwp-exit, and set
254     SYSSET_DECLAREPID(proc_pid, proc->getPid());
255     
256     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
257     
258     if (!proc->get_entry_syscalls(entryset)) return false;;
259
260     if (SYSSET_MAP(SYS_lwp_exit, proc_pid) != -1) {
261         praddsysset(entryset, SYSSET_MAP(SYS_lwp_exit, proc_pid));
262     }
263     if (!proc->set_entry_syscalls(entryset)) return false;;
264     // Make sure our removal code gets run
265     preLwpExitInst = SYSCALL_INSTALLED;
266     SYSSET_FREE(entryset);
267
268     return true;
269
270 #if 0
271     preLwpExitInst = new instMapping(LWP_EXIT_FUNC, "DYNINST_instLwpExit",
272                                    FUNC_ENTRY);
273     preLwpExitInst->dontUseTrampGuard();
274     
275     pdvector<instMapping *> instReqs;
276     instReqs.push_back(preLwpExitInst);
277     
278     proc->installInstrRequests(instReqs);
279     
280     // Check to see if we put anything in the proggie
281     if (preLwpExitInst->miniTramps.size() == 0)
282         return false;
283     return true;
284 #endif
285 }    
286
287
288 //////////////////////////////////////////////////////
289
290 /////// Remove pre-fork instrumentation
291
292 bool syscallNotification::removePreFork() {
293     if (!preForkInst) return false;
294     if (!proc->isAttached() || proc->execing()) {
295         preForkInst = NULL;
296         return true;
297     }
298     
299     // Get existing flags, add pre-fork, and set
300     SYSSET_DECLAREPID(proc_pid,proc->getPid());
301     
302     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
303     
304     if (!proc->get_entry_syscalls(entryset)) return false;;
305
306     if (SYSSET_MAP(SYS_fork, proc_pid) != -1) {
307         prdelsysset(entryset, SYSSET_MAP(SYS_fork, proc_pid));
308     }
309     if (SYSSET_MAP(SYS_fork1, proc_pid) != -1) {
310         prdelsysset(entryset, SYSSET_MAP(SYS_fork1, proc_pid));
311     }
312     if (SYSSET_MAP(SYS_vfork, proc_pid) != -1) {
313         prdelsysset(entryset, SYSSET_MAP(SYS_vfork, proc_pid));
314     }
315     if (!proc->set_entry_syscalls(entryset)) return false;;
316     SYSSET_FREE(entryset);
317     preForkInst = NULL;
318     return true;
319 }
320
321 /////// Remove post-fork instrumentation
322
323 bool syscallNotification::removePostFork() {
324     if (!postForkInst) return false;
325
326     if (postForkInst != SYSCALL_INSTALLED) {
327         if (!proc->isAttached() || proc->execing()) {
328             delete postForkInst;
329             postForkInst = NULL;
330             return true;
331         }
332         
333         miniTramp *handle;
334         for (unsigned i = 0; i < postForkInst->miniTramps.size(); i++) {
335             handle = postForkInst->miniTramps[i];
336             
337             bool removed = handle->uninstrument();
338             // At some point we should handle a negative return... but I
339             // have no idea how.
340             
341             assert(removed);
342             // The miniTramp is deleted when the miniTramp is freed, so
343             // we don't have to.
344         } 
345         delete postForkInst;
346         postForkInst = NULL;
347         return true;
348     }
349     
350     // else...
351
352     if (!proc->isAttached() || proc->execing()) {
353         postForkInst = NULL;
354         return true;
355     }
356
357     // Get existing flags, add post-fork, and set
358     SYSSET_DECLAREPID(proc_pid,proc->getPid());
359     
360     sysset_t *exitset = SYSSET_ALLOC(proc_pid);
361     
362     if (!proc->get_exit_syscalls(exitset)) return false;;
363
364     if (SYSSET_MAP(SYS_fork, proc_pid) != -1) {
365         prdelsysset(exitset, SYSSET_MAP(SYS_fork, proc_pid));
366     }
367     if (SYSSET_MAP(SYS_fork1, proc_pid) != -1) {
368         prdelsysset(exitset, SYSSET_MAP(SYS_fork1, proc_pid));
369     }
370     if (SYSSET_MAP(SYS_vfork, proc_pid) != -1) {
371         prdelsysset(exitset, SYSSET_MAP(SYS_vfork, proc_pid));
372     }
373     if (!proc->set_exit_syscalls(exitset)) return false;;
374     SYSSET_FREE(exitset);
375     postForkInst = NULL;
376     return true;
377 }
378
379 /////// Remove pre-exec instrumentation
380
381 bool syscallNotification::removePreExec() {
382     if (!preExecInst) {
383         bperr("Tracing never installed\n");
384         return false;
385     }
386     
387     if (!proc->isAttached() || proc->execing()) {
388         preExecInst = NULL;
389         return true;
390     }
391
392     // Get existing flags, add pre-exec, and set
393     SYSSET_DECLAREPID(proc_pid,proc->getPid());
394     
395     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
396     
397     if (!proc->get_entry_syscalls(entryset)) return false;;
398
399     if (SYSSET_MAP(SYS_exec, proc_pid) != -1) {
400         prdelsysset(entryset, SYSSET_MAP(SYS_exec, proc_pid));
401     }
402     if (SYSSET_MAP(SYS_execve, proc_pid) != -1) {
403         prdelsysset(entryset, SYSSET_MAP(SYS_execve, proc_pid));
404     }
405     if (!proc->set_entry_syscalls(entryset)) return false;;
406     SYSSET_FREE(entryset);
407     preExecInst = NULL;
408     return true;
409
410 }    
411
412 /////// Remove post-exec instrumentation
413
414 bool syscallNotification::removePostExec() {
415     if (!postExecInst) return false;
416     // <whistles>
417     if (!proc->isAttached() || proc->execing()) {
418         postExecInst = NULL;
419         return true;
420     }
421
422     // Get existing flags, add post-exec, and set
423     SYSSET_DECLAREPID(proc_pid, proc->getPid());
424     
425     sysset_t *exitset = SYSSET_ALLOC(proc_pid);
426     
427     if (!proc->get_exit_syscalls(exitset)) return false;;
428
429     if (SYSSET_MAP(SYS_exec, proc_pid) != -1) {
430         prdelsysset(exitset, SYSSET_MAP(SYS_exec, proc_pid));
431     }
432     if (SYSSET_MAP(SYS_execve, proc_pid) != -1) {
433         prdelsysset(exitset, SYSSET_MAP(SYS_execve, proc_pid));
434     }
435     if (!proc->set_exit_syscalls(exitset)) return false;;
436     SYSSET_FREE(exitset);
437     postExecInst = NULL;
438     return true;
439 }
440
441 /////// Remove pre-exit instrumentation
442
443 bool syscallNotification::removePreExit() {
444     if (!preExitInst) return false;
445     if (!proc->isAttached() || proc->execing()) {
446         preExitInst = NULL;
447         return true;
448     }
449
450     // Get existing flags, add pre-exit, and set
451     SYSSET_DECLAREPID(proc_pid, proc->getPid());
452     
453     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
454     
455     if (!proc->get_entry_syscalls(entryset)) return false;;
456
457     if (SYSSET_MAP(SYS_exit, proc_pid) != -1) {
458         prdelsysset(entryset, SYSSET_MAP(SYS_exit, proc_pid));
459     }
460     if (!proc->set_entry_syscalls(entryset)) return false;;
461     SYSSET_FREE(entryset);
462     preExitInst = NULL;
463     
464     return true;
465 }
466
467 /////// Remove pre-exit instrumentation
468
469 bool syscallNotification::removePreLwpExit() {
470     if (!preLwpExitInst) return false;
471
472     if (preLwpExitInst != SYSCALL_INSTALLED) {
473         if (!proc->isAttached() || proc->execing()) {
474             delete preLwpExitInst;
475             preLwpExitInst = NULL;
476             return true;
477         }
478         
479         miniTramp *handle;
480         for (unsigned i = 0; i < preLwpExitInst->miniTramps.size(); i++) {
481             handle = preLwpExitInst->miniTramps[i];
482             
483             bool removed = handle->uninstrument();
484             // At some point we should handle a negative return... but I
485             // have no idea how.
486             
487             assert(removed);
488             // The miniTramp is deleted when the miniTramp is freed, so
489             // we don't have to.
490         } 
491         delete preLwpExitInst;
492         preLwpExitInst = NULL;
493         return true;
494     }
495
496     if (!proc->isAttached()) {
497         preLwpExitInst = NULL;
498         return true;
499     }
500
501     // Get existing flags, add pre-exit, and set
502     SYSSET_DECLAREPID(proc_pid, proc->getPid());
503     
504     sysset_t *entryset = SYSSET_ALLOC(proc_pid);
505     
506     if (!proc->get_entry_syscalls(entryset)) return false;;
507
508     if (SYSSET_MAP(SYS_lwp_exit, proc_pid) != -1) {
509         prdelsysset(entryset, SYSSET_MAP(SYS_lwp_exit, proc_pid));
510     }
511     if (!proc->set_entry_syscalls(entryset)) return false;;
512     SYSSET_FREE(entryset);
513     preLwpExitInst = NULL;
514     
515     return true;
516 }
517
518