1226584Sdim/*
2226584Sdim * Copyright (c) 2010 The FreeBSD Foundation
3226584Sdim * All rights reserved.
4226584Sdim *
5226584Sdim * This software was developed by Rui Paulo under sponsorship from the
6226584Sdim * FreeBSD Foundation.
7226584Sdim *
8226584Sdim * Redistribution and use in source and binary forms, with or without
9226584Sdim * modification, are permitted provided that the following conditions
10226584Sdim * are met:
11226584Sdim * 1. Redistributions of source code must retain the above copyright
12226584Sdim *    notice, this list of conditions and the following disclaimer.
13226584Sdim * 2. Redistributions in binary form must reproduce the above copyright
14226584Sdim *    notice, this list of conditions and the following disclaimer in the
15226584Sdim *    documentation and/or other materials provided with the distribution.
16226584Sdim *
17226584Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18226584Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19226584Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20226584Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21226584Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22226584Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23226584Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24226584Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25226584Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26226584Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27226584Sdim * SUCH DAMAGE.
28226584Sdim */
29226584Sdim#include <sys/cdefs.h>
30226584Sdim__FBSDID("$FreeBSD: releng/10.3/lib/librtld_db/rtld_db.c 269720 2014-08-08 14:53:01Z markj $");
31226584Sdim
32226584Sdim#include <machine/_inttypes.h>
33226584Sdim#include <sys/types.h>
34226584Sdim#include <sys/user.h>
35226584Sdim
36226584Sdim#include <err.h>
37226584Sdim#include <stdio.h>
38226584Sdim#include <stdlib.h>
39226584Sdim#include <string.h>
40226584Sdim#include <limits.h>
41226584Sdim#include <libproc.h>
42226584Sdim#include <libutil.h>
43226584Sdim
44226584Sdim#include "rtld_db.h"
45226584Sdim
46226584Sdimstatic int _librtld_db_debug = 0;
47226584Sdim#define DPRINTF(...) do {				\
48226584Sdim	if (_librtld_db_debug) {			\
49226584Sdim		fprintf(stderr, "librtld_db: DEBUG: ");	\
50226584Sdim		fprintf(stderr, __VA_ARGS__);		\
51226584Sdim	}						\
52226584Sdim} while (0)
53226584Sdim
54226584Sdimvoid
55226584Sdimrd_delete(rd_agent_t *rdap)
56226584Sdim{
57226584Sdim
58226584Sdim	free(rdap);
59226584Sdim}
60226584Sdim
61226584Sdimconst char *
62226584Sdimrd_errstr(rd_err_e rderr)
63226584Sdim{
64226584Sdim
65226584Sdim	switch (rderr) {
66226584Sdim	case RD_ERR:
67226584Sdim		return "generic error";
68226584Sdim	case RD_OK:
69226584Sdim		return "no error";
70226584Sdim	case RD_NOCAPAB:
71226584Sdim		return "capability not supported";
72226584Sdim	case RD_DBERR:
73226584Sdim		return "database error";
74226584Sdim	case RD_NOBASE:
75226584Sdim		return "NOBASE";
76226584Sdim	case RD_NOMAPS:
77226584Sdim		return "NOMAPS";
78226584Sdim	default:
79226584Sdim		return "unknown error";
80226584Sdim	}
81226584Sdim}
82226584Sdim
83226584Sdimrd_err_e
84226584Sdimrd_event_addr(rd_agent_t *rdap, rd_event_e event, rd_notify_t *notify)
85226584Sdim{
86226584Sdim	rd_err_e ret;
87226584Sdim
88226584Sdim	DPRINTF("%s rdap %p event %d notify %p\n", __func__, rdap, event,
89226584Sdim	    notify);
90226584Sdim
91226584Sdim	ret = RD_OK;
92226584Sdim	switch (event) {
93226584Sdim	case RD_NONE:
94226584Sdim		break;
95226584Sdim	case RD_PREINIT:
96226584Sdim		notify->type = RD_NOTIFY_BPT;
97226584Sdim		notify->u.bptaddr = rdap->rda_preinit_addr;
98226584Sdim		break;
99226584Sdim	case RD_POSTINIT:
100226584Sdim		notify->type = RD_NOTIFY_BPT;
101226584Sdim		notify->u.bptaddr = rdap->rda_postinit_addr;
102226584Sdim		break;
103226584Sdim	case RD_DLACTIVITY:
104226584Sdim		notify->type = RD_NOTIFY_BPT;
105226584Sdim		notify->u.bptaddr = rdap->rda_dlactivity_addr;
106226584Sdim		break;
107226584Sdim	default:
108226584Sdim		ret = RD_ERR;
109226584Sdim		break;
110226584Sdim	}
111226584Sdim	return (ret);
112226584Sdim}
113226584Sdim
114226584Sdimrd_err_e
115226584Sdimrd_event_enable(rd_agent_t *rdap __unused, int onoff)
116226584Sdim{
117226584Sdim	DPRINTF("%s onoff %d\n", __func__, onoff);
118226584Sdim
119226584Sdim	return (RD_OK);
120226584Sdim}
121226584Sdim
122226584Sdimrd_err_e
123226584Sdimrd_event_getmsg(rd_agent_t *rdap __unused, rd_event_msg_t *msg)
124226584Sdim{
125226584Sdim	DPRINTF("%s\n", __func__);
126226584Sdim
127226584Sdim	msg->type = RD_POSTINIT;
128226584Sdim	msg->u.state = RD_CONSISTENT;
129226584Sdim
130226584Sdim	return (RD_OK);
131226584Sdim}
132226584Sdim
133226584Sdimrd_err_e
134226584Sdimrd_init(int version)
135226584Sdim{
136226584Sdim	char *debug = NULL;
137226584Sdim
138226584Sdim	if (version == RD_VERSION) {
139226584Sdim		debug = getenv("LIBRTLD_DB_DEBUG");
140226584Sdim		_librtld_db_debug = debug ? atoi(debug) : 0;
141226584Sdim		return (RD_OK);
142226584Sdim	} else
143226584Sdim		return (RD_NOCAPAB);
144226584Sdim}
145226584Sdim
146226584Sdimrd_err_e
147226584Sdimrd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data)
148226584Sdim{
149226584Sdim	int cnt, i, lastvn = 0;
150226584Sdim	rd_loadobj_t rdl;
151226584Sdim	struct kinfo_vmentry *kves, *kve;
152226584Sdim
153226584Sdim	DPRINTF("%s\n", __func__);
154226584Sdim
155226584Sdim        if ((kves = kinfo_getvmmap(proc_getpid(rdap->rda_php), &cnt)) == NULL) {
156226584Sdim		warn("ERROR: kinfo_getvmmap() failed");
157226584Sdim		return (RD_ERR);
158226584Sdim	}
159226584Sdim	for (i = 0; i < cnt; i++) {
160226584Sdim		kve = kves + i;
161226584Sdim		if (kve->kve_type == KVME_TYPE_VNODE)
162226584Sdim			lastvn = i;
163226584Sdim		memset(&rdl, 0, sizeof(rdl));
164226584Sdim		/*
165226584Sdim		 * Map the kinfo_vmentry struct to the rd_loadobj structure.
166226584Sdim		 */
167226584Sdim		rdl.rdl_saddr = kve->kve_start;
168226584Sdim		rdl.rdl_eaddr = kve->kve_end;
169226584Sdim		rdl.rdl_offset = kve->kve_offset;
170226584Sdim		if (kve->kve_protection & KVME_PROT_READ)
171226584Sdim			rdl.rdl_prot |= RD_RDL_R;
172226584Sdim		if (kve->kve_protection & KVME_PROT_WRITE)
173226584Sdim			rdl.rdl_prot |= RD_RDL_W;
174226584Sdim		if (kve->kve_protection & KVME_PROT_EXEC)
175226584Sdim			rdl.rdl_prot |= RD_RDL_X;
176226584Sdim		strlcpy(rdl.rdl_path, kves[lastvn].kve_path,
177226584Sdim			sizeof(rdl.rdl_path));
178226584Sdim		(*cb)(&rdl, clnt_data);
179226584Sdim	}
180226584Sdim	free(kves);
181226584Sdim
182226584Sdim	return (RD_OK);
183226584Sdim}
184226584Sdim
185226584Sdimvoid
186226584Sdimrd_log(const int onoff)
187226584Sdim{
188226584Sdim	DPRINTF("%s\n", __func__);
189226584Sdim
190226584Sdim	(void)onoff;
191226584Sdim}
192226584Sdim
193226584Sdimrd_agent_t *
194226584Sdimrd_new(struct proc_handle *php)
195226584Sdim{
196226584Sdim	rd_agent_t *rdap;
197226584Sdim
198226584Sdim	rdap = malloc(sizeof(rd_agent_t));
199226584Sdim	if (rdap) {
200226584Sdim		memset(rdap, 0, sizeof(rd_agent_t));
201226584Sdim		rdap->rda_php = php;
202226584Sdim		rd_reset(rdap);
203226584Sdim	}
204226584Sdim
205226584Sdim	return (rdap);
206226584Sdim}
207226584Sdim
208226584Sdimrd_err_e
209226584Sdimrd_objpad_enable(rd_agent_t *rdap, size_t padsize)
210226584Sdim{
211226584Sdim	DPRINTF("%s\n", __func__);
212226584Sdim
213226584Sdim	(void)rdap;
214226584Sdim	(void)padsize;
215226584Sdim
216226584Sdim	return (RD_ERR);
217226584Sdim}
218226584Sdim
219226584Sdimrd_err_e
220226584Sdimrd_plt_resolution(rd_agent_t *rdap, uintptr_t pc, struct proc *proc,
221226584Sdim    uintptr_t plt_base, rd_plt_info_t *rpi)
222226584Sdim{
223226584Sdim	DPRINTF("%s\n", __func__);
224226584Sdim
225226584Sdim	(void)rdap;
226226584Sdim	(void)pc;
227226584Sdim	(void)proc;
228226584Sdim	(void)plt_base;
229226584Sdim	(void)rpi;
230226584Sdim
231226584Sdim	return (RD_ERR);
232226584Sdim}
233226584Sdim
234226584Sdimrd_err_e
235226584Sdimrd_reset(rd_agent_t *rdap)
236226584Sdim{
237226584Sdim	GElf_Sym sym;
238226584Sdim
239226584Sdim	if (proc_name2sym(rdap->rda_php, "ld-elf.so.1", "r_debug_state",
240226584Sdim	    &sym) < 0)
241226584Sdim		return (RD_ERR);
242226584Sdim	DPRINTF("found r_debug_state at 0x%lx\n", (unsigned long)sym.st_value);
243226584Sdim	rdap->rda_preinit_addr = sym.st_value;
244226584Sdim	rdap->rda_dlactivity_addr = sym.st_value;
245226584Sdim
246226584Sdim	if (proc_name2sym(rdap->rda_php, "ld-elf.so.1", "_r_debug_postinit",
247226584Sdim	    &sym) < 0)
248226584Sdim		return (RD_ERR);
249226584Sdim	DPRINTF("found _r_debug_postinit at 0x%lx\n",
250226584Sdim	    (unsigned long)sym.st_value);
251226584Sdim	rdap->rda_postinit_addr = sym.st_value;
252226584Sdim
253226584Sdim	return (RD_OK);
254226584Sdim}
255226584Sdim