1 | /* |
2 | * Copyright (c) 2010 The FreeBSD Foundation |
3 | * All rights reserved. |
4 | * |
5 | * This software was developed by Rui Paulo under sponsorship from the |
6 | * FreeBSD Foundation. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 | * SUCH DAMAGE. |
28 | */ |
29 | #include <sys/cdefs.h> |
30 | #ifdef __FBSDID |
31 | __FBSDID("$FreeBSD: head/lib/librtld_db/rtld_db.c 272488 2014-10-03 23:20:37Z markj $" ); |
32 | #else |
33 | __RCSID("$NetBSD: rtld_db.c,v 1.3 2016/04/26 14:26:49 chs Exp $" ); |
34 | #endif |
35 | |
36 | #include <sys/types.h> |
37 | #include <sys/sysctl.h> |
38 | |
39 | #include <inttypes.h> |
40 | #include <err.h> |
41 | #include <stdio.h> |
42 | #include <stdlib.h> |
43 | #include <string.h> |
44 | #include <limits.h> |
45 | #include <libproc.h> |
46 | #include <util.h> |
47 | |
48 | #include "rtld_db.h" |
49 | |
50 | static int _librtld_db_debug = 0; |
51 | #define DPRINTF(...) do { \ |
52 | if (_librtld_db_debug) { \ |
53 | fprintf(stderr, "librtld_db: DEBUG: "); \ |
54 | fprintf(stderr, __VA_ARGS__); \ |
55 | } \ |
56 | } while (0) |
57 | |
58 | void |
59 | rd_delete(rd_agent_t *rdap) |
60 | { |
61 | |
62 | free(rdap); |
63 | } |
64 | |
65 | const char * |
66 | rd_errstr(rd_err_e rderr) |
67 | { |
68 | |
69 | switch (rderr) { |
70 | case RD_ERR: |
71 | return "generic error" ; |
72 | case RD_OK: |
73 | return "no error" ; |
74 | case RD_NOCAPAB: |
75 | return "capability not supported" ; |
76 | case RD_DBERR: |
77 | return "database error" ; |
78 | case RD_NOBASE: |
79 | return "NOBASE" ; |
80 | case RD_NOMAPS: |
81 | return "NOMAPS" ; |
82 | default: |
83 | return "unknown error" ; |
84 | } |
85 | } |
86 | |
87 | rd_err_e |
88 | rd_event_addr(rd_agent_t *rdap, rd_event_e event, rd_notify_t *notify) |
89 | { |
90 | rd_err_e ret; |
91 | |
92 | DPRINTF("%s rdap %p event %d notify %p\n" , __func__, rdap, event, |
93 | notify); |
94 | |
95 | ret = RD_OK; |
96 | switch (event) { |
97 | case RD_NONE: |
98 | break; |
99 | case RD_PREINIT: |
100 | notify->type = RD_NOTIFY_BPT; |
101 | notify->u.bptaddr = rdap->rda_preinit_addr; |
102 | break; |
103 | case RD_POSTINIT: |
104 | notify->type = RD_NOTIFY_BPT; |
105 | notify->u.bptaddr = rdap->rda_postinit_addr; |
106 | break; |
107 | case RD_DLACTIVITY: |
108 | notify->type = RD_NOTIFY_BPT; |
109 | notify->u.bptaddr = rdap->rda_dlactivity_addr; |
110 | break; |
111 | default: |
112 | ret = RD_ERR; |
113 | break; |
114 | } |
115 | return (ret); |
116 | } |
117 | |
118 | rd_err_e |
119 | rd_event_enable(rd_agent_t *rdap __unused, int onoff) |
120 | { |
121 | DPRINTF("%s onoff %d\n" , __func__, onoff); |
122 | |
123 | return (RD_OK); |
124 | } |
125 | |
126 | rd_err_e |
127 | rd_event_getmsg(rd_agent_t *rdap __unused, rd_event_msg_t *msg) |
128 | { |
129 | DPRINTF("%s\n" , __func__); |
130 | |
131 | msg->type = RD_POSTINIT; |
132 | msg->u.state = RD_CONSISTENT; |
133 | |
134 | return (RD_OK); |
135 | } |
136 | |
137 | rd_err_e |
138 | rd_init(int version) |
139 | { |
140 | char *debug = NULL; |
141 | |
142 | if (version == RD_VERSION) { |
143 | debug = getenv("LIBRTLD_DB_DEBUG" ); |
144 | _librtld_db_debug = debug ? atoi(debug) : 0; |
145 | return (RD_OK); |
146 | } else |
147 | return (RD_NOCAPAB); |
148 | } |
149 | |
150 | rd_err_e |
151 | rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data) |
152 | { |
153 | size_t cnt, i, lastvn = 0; |
154 | rd_loadobj_t rdl; |
155 | struct kinfo_vmentry *kves, *kve; |
156 | |
157 | DPRINTF("%s\n" , __func__); |
158 | |
159 | if ((kves = kinfo_getvmmap(proc_getpid(rdap->rda_php), &cnt)) == NULL) { |
160 | warn("ERROR: kinfo_getvmmap() failed" ); |
161 | return (RD_ERR); |
162 | } |
163 | for (i = 0; i < cnt; i++) { |
164 | kve = kves + i; |
165 | if (kve->kve_type == KVME_TYPE_VNODE) |
166 | lastvn = i; |
167 | memset(&rdl, 0, sizeof(rdl)); |
168 | /* |
169 | * Map the kinfo_vmentry struct to the rd_loadobj structure. |
170 | */ |
171 | rdl.rdl_saddr = kve->kve_start; |
172 | rdl.rdl_eaddr = kve->kve_end; |
173 | rdl.rdl_offset = kve->kve_offset; |
174 | if (kve->kve_protection & KVME_PROT_READ) |
175 | rdl.rdl_prot |= RD_RDL_R; |
176 | if (kve->kve_protection & KVME_PROT_WRITE) |
177 | rdl.rdl_prot |= RD_RDL_W; |
178 | if (kve->kve_protection & KVME_PROT_EXEC) |
179 | rdl.rdl_prot |= RD_RDL_X; |
180 | strlcpy(rdl.rdl_path, kves[lastvn].kve_path, |
181 | sizeof(rdl.rdl_path)); |
182 | (*cb)(&rdl, clnt_data); |
183 | } |
184 | free(kves); |
185 | |
186 | return (RD_OK); |
187 | } |
188 | |
189 | void |
190 | rd_log(const int onoff) |
191 | { |
192 | DPRINTF("%s\n" , __func__); |
193 | |
194 | (void)onoff; |
195 | } |
196 | |
197 | rd_agent_t * |
198 | rd_new(struct proc_handle *php) |
199 | { |
200 | rd_agent_t *rdap; |
201 | |
202 | rdap = malloc(sizeof(rd_agent_t)); |
203 | if (rdap) { |
204 | memset(rdap, 0, sizeof(rd_agent_t)); |
205 | rdap->rda_php = php; |
206 | rd_reset(rdap); |
207 | } |
208 | |
209 | return (rdap); |
210 | } |
211 | |
212 | rd_err_e |
213 | rd_objpad_enable(rd_agent_t *rdap, size_t padsize) |
214 | { |
215 | DPRINTF("%s\n" , __func__); |
216 | |
217 | (void)rdap; |
218 | (void)padsize; |
219 | |
220 | return (RD_ERR); |
221 | } |
222 | |
223 | rd_err_e |
224 | rd_plt_resolution(rd_agent_t *rdap, uintptr_t pc, struct proc *proc, |
225 | uintptr_t plt_base, rd_plt_info_t *rpi) |
226 | { |
227 | DPRINTF("%s\n" , __func__); |
228 | |
229 | (void)rdap; |
230 | (void)pc; |
231 | (void)proc; |
232 | (void)plt_base; |
233 | (void)rpi; |
234 | |
235 | return (RD_ERR); |
236 | } |
237 | |
238 | rd_err_e |
239 | rd_reset(rd_agent_t *rdap) |
240 | { |
241 | GElf_Sym sym; |
242 | |
243 | /* |
244 | * preinit and dlactivity events are not supported yet. |
245 | */ |
246 | |
247 | rdap->rda_preinit_addr = (uintptr_t)-1; |
248 | rdap->rda_dlactivity_addr = (uintptr_t)-1; |
249 | |
250 | if (proc_name2sym(rdap->rda_php, "ld.elf_so" , "_rtld_debug_state" , |
251 | &sym, NULL) < 0) |
252 | return (RD_ERR); |
253 | DPRINTF("found _rtld_debug_state at 0x%lx\n" , |
254 | (unsigned long)sym.st_value); |
255 | rdap->rda_postinit_addr = sym.st_value; |
256 | |
257 | return (RD_OK); |
258 | } |
259 | |