1fcf9fc10SMark Johnston /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 35e53a4f9SPedro F. Giffuni * 4cb314988SRui Paulo * Copyright (c) 2010 The FreeBSD Foundation 5cb314988SRui Paulo * 6cb314988SRui Paulo * This software was developed by Rui Paulo under sponsorship from the 7cb314988SRui Paulo * FreeBSD Foundation. 8cb314988SRui Paulo * 9cb314988SRui Paulo * Redistribution and use in source and binary forms, with or without 10cb314988SRui Paulo * modification, are permitted provided that the following conditions 11cb314988SRui Paulo * are met: 12cb314988SRui Paulo * 1. Redistributions of source code must retain the above copyright 13cb314988SRui Paulo * notice, this list of conditions and the following disclaimer. 14cb314988SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 15cb314988SRui Paulo * notice, this list of conditions and the following disclaimer in the 16cb314988SRui Paulo * documentation and/or other materials provided with the distribution. 17cb314988SRui Paulo * 18cb314988SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19cb314988SRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20cb314988SRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21cb314988SRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22cb314988SRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23cb314988SRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24cb314988SRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25cb314988SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26cb314988SRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27cb314988SRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28cb314988SRui Paulo * SUCH DAMAGE. 29cb314988SRui Paulo */ 30a7e13d50SMark Johnston 31a7e13d50SMark Johnston #include <sys/param.h> 32a7e13d50SMark Johnston #include <sys/sysctl.h> 33cb314988SRui Paulo #include <sys/user.h> 34cb314988SRui Paulo 35a7e13d50SMark Johnston #include <assert.h> 36cb314988SRui Paulo #include <err.h> 37a7e13d50SMark Johnston #include <fcntl.h> 38a7e13d50SMark Johnston #include <limits.h> 39cb314988SRui Paulo #include <stdio.h> 40cb314988SRui Paulo #include <stdlib.h> 41cb314988SRui Paulo #include <string.h> 42a7e13d50SMark Johnston #include <unistd.h> 43a7e13d50SMark Johnston 44a7e13d50SMark Johnston #include <machine/elf.h> 45a7e13d50SMark Johnston 46a7e13d50SMark Johnston #include <libelf.h> 47cb314988SRui Paulo #include <libproc.h> 48a7e13d50SMark Johnston #include <libprocstat.h> 49cb314988SRui Paulo #include <libutil.h> 50cb314988SRui Paulo 51cb314988SRui Paulo #include "rtld_db.h" 52cb314988SRui Paulo 53cb314988SRui Paulo static int _librtld_db_debug = 0; 54cb314988SRui Paulo #define DPRINTF(...) do { \ 55cb314988SRui Paulo if (_librtld_db_debug) { \ 56cb314988SRui Paulo fprintf(stderr, "librtld_db: DEBUG: "); \ 57cb314988SRui Paulo fprintf(stderr, __VA_ARGS__); \ 58cb314988SRui Paulo } \ 59cb314988SRui Paulo } while (0) 60cb314988SRui Paulo 61cb314988SRui Paulo void 62cb314988SRui Paulo rd_delete(rd_agent_t *rdap) 63cb314988SRui Paulo { 64cb314988SRui Paulo 65a7e13d50SMark Johnston if (rdap->rda_procstat != NULL) 66a7e13d50SMark Johnston procstat_close(rdap->rda_procstat); 67cb314988SRui Paulo free(rdap); 68cb314988SRui Paulo } 69cb314988SRui Paulo 70cb314988SRui Paulo const char * 71cb314988SRui Paulo rd_errstr(rd_err_e rderr) 72cb314988SRui Paulo { 73cb314988SRui Paulo 74cb314988SRui Paulo switch (rderr) { 75cb314988SRui Paulo case RD_ERR: 76cb314988SRui Paulo return "generic error"; 77cb314988SRui Paulo case RD_OK: 78cb314988SRui Paulo return "no error"; 79cb314988SRui Paulo case RD_NOCAPAB: 80cb314988SRui Paulo return "capability not supported"; 81cb314988SRui Paulo case RD_DBERR: 82cb314988SRui Paulo return "database error"; 83cb314988SRui Paulo case RD_NOBASE: 84cb314988SRui Paulo return "NOBASE"; 85cb314988SRui Paulo case RD_NOMAPS: 86cb314988SRui Paulo return "NOMAPS"; 87cb314988SRui Paulo default: 88cb314988SRui Paulo return "unknown error"; 89cb314988SRui Paulo } 90cb314988SRui Paulo } 91cb314988SRui Paulo 92cb314988SRui Paulo rd_err_e 93b252f278SMark Johnston rd_event_addr(rd_agent_t *rdap, rd_event_e event, rd_notify_t *notify) 94cb314988SRui Paulo { 95b252f278SMark Johnston rd_err_e ret; 96cb314988SRui Paulo 97b252f278SMark Johnston DPRINTF("%s rdap %p event %d notify %p\n", __func__, rdap, event, 98b252f278SMark Johnston notify); 99b252f278SMark Johnston 100b252f278SMark Johnston ret = RD_OK; 101b252f278SMark Johnston switch (event) { 102b252f278SMark Johnston case RD_NONE: 103b252f278SMark Johnston break; 104b252f278SMark Johnston case RD_PREINIT: 105cb314988SRui Paulo notify->type = RD_NOTIFY_BPT; 106b252f278SMark Johnston notify->u.bptaddr = rdap->rda_preinit_addr; 107b252f278SMark Johnston break; 108b252f278SMark Johnston case RD_POSTINIT: 109b252f278SMark Johnston notify->type = RD_NOTIFY_BPT; 110b252f278SMark Johnston notify->u.bptaddr = rdap->rda_postinit_addr; 111b252f278SMark Johnston break; 112b252f278SMark Johnston case RD_DLACTIVITY: 113b252f278SMark Johnston notify->type = RD_NOTIFY_BPT; 114b252f278SMark Johnston notify->u.bptaddr = rdap->rda_dlactivity_addr; 115b252f278SMark Johnston break; 116b252f278SMark Johnston default: 117b252f278SMark Johnston ret = RD_ERR; 118b252f278SMark Johnston break; 119b252f278SMark Johnston } 120b252f278SMark Johnston return (ret); 121cb314988SRui Paulo } 122cb314988SRui Paulo 123cb314988SRui Paulo rd_err_e 124cb314988SRui Paulo rd_event_enable(rd_agent_t *rdap __unused, int onoff) 125cb314988SRui Paulo { 126cb314988SRui Paulo DPRINTF("%s onoff %d\n", __func__, onoff); 127cb314988SRui Paulo 128cb314988SRui Paulo return (RD_OK); 129cb314988SRui Paulo } 130cb314988SRui Paulo 131cb314988SRui Paulo rd_err_e 132cb314988SRui Paulo rd_event_getmsg(rd_agent_t *rdap __unused, rd_event_msg_t *msg) 133cb314988SRui Paulo { 134cb314988SRui Paulo DPRINTF("%s\n", __func__); 135cb314988SRui Paulo 136cb314988SRui Paulo msg->type = RD_POSTINIT; 137cb314988SRui Paulo msg->u.state = RD_CONSISTENT; 138cb314988SRui Paulo 139cb314988SRui Paulo return (RD_OK); 140cb314988SRui Paulo } 141cb314988SRui Paulo 142cb314988SRui Paulo rd_err_e 143cb314988SRui Paulo rd_init(int version) 144cb314988SRui Paulo { 145cb314988SRui Paulo char *debug = NULL; 146cb314988SRui Paulo 147cb314988SRui Paulo if (version == RD_VERSION) { 148cb314988SRui Paulo debug = getenv("LIBRTLD_DB_DEBUG"); 149cb314988SRui Paulo _librtld_db_debug = debug ? atoi(debug) : 0; 150cb314988SRui Paulo return (RD_OK); 151cb314988SRui Paulo } else 152cb314988SRui Paulo return (RD_NOCAPAB); 153cb314988SRui Paulo } 154cb314988SRui Paulo 155cb314988SRui Paulo rd_err_e 156cb314988SRui Paulo rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data) 157cb314988SRui Paulo { 158cb314988SRui Paulo struct kinfo_vmentry *kves, *kve; 15913c2d789SMark Johnston const char *path; 16013c2d789SMark Johnston uint64_t fileid; 161a7e13d50SMark Johnston rd_loadobj_t rdl; 162a7e13d50SMark Johnston rd_err_e ret; 16313c2d789SMark Johnston uintptr_t base; 164dfd00261SChuck Silvers uint32_t offset; 16513c2d789SMark Johnston int cnt, i; 166cb314988SRui Paulo 167cb314988SRui Paulo DPRINTF("%s\n", __func__); 168cb314988SRui Paulo 169cb314988SRui Paulo if ((kves = kinfo_getvmmap(proc_getpid(rdap->rda_php), &cnt)) == NULL) { 170cb314988SRui Paulo warn("ERROR: kinfo_getvmmap() failed"); 171cb314988SRui Paulo return (RD_ERR); 172cb314988SRui Paulo } 173a7e13d50SMark Johnston 17413c2d789SMark Johnston base = 0; 17513c2d789SMark Johnston fileid = 0; 17613c2d789SMark Johnston path = NULL; 177a7e13d50SMark Johnston ret = RD_OK; 178cb314988SRui Paulo for (i = 0; i < cnt; i++) { 17913c2d789SMark Johnston kve = &kves[i]; 18013c2d789SMark Johnston /* 18113c2d789SMark Johnston * Cache the base offset of the file mapping. The kve_offset 18213c2d789SMark Johnston * field gives the file offset of a particular mapping into the 18313c2d789SMark Johnston * file, but we want the mapping offset relative to the base 18413c2d789SMark Johnston * mapping. 18513c2d789SMark Johnston */ 18613776bf7SMark Johnston if (kve->kve_type == KVME_TYPE_VNODE) { 18713776bf7SMark Johnston if (kve->kve_vn_fileid != fileid) { 18813c2d789SMark Johnston base = kve->kve_start; 18913c2d789SMark Johnston fileid = kve->kve_vn_fileid; 19013c2d789SMark Johnston } 191dfd00261SChuck Silvers path = kve->kve_path; 192dfd00261SChuck Silvers offset = kve->kve_start - base; 19313776bf7SMark Johnston } else { 19413776bf7SMark Johnston path = NULL; 195dfd00261SChuck Silvers offset = 0; 19613776bf7SMark Johnston } 197cb314988SRui Paulo memset(&rdl, 0, sizeof(rdl)); 198cb314988SRui Paulo /* 199cb314988SRui Paulo * Map the kinfo_vmentry struct to the rd_loadobj structure. 200cb314988SRui Paulo */ 201cb314988SRui Paulo rdl.rdl_saddr = kve->kve_start; 202cb314988SRui Paulo rdl.rdl_eaddr = kve->kve_end; 203dfd00261SChuck Silvers rdl.rdl_offset = offset; 204cb314988SRui Paulo if (kve->kve_protection & KVME_PROT_READ) 205cb314988SRui Paulo rdl.rdl_prot |= RD_RDL_R; 206cb314988SRui Paulo if (kve->kve_protection & KVME_PROT_WRITE) 207cb314988SRui Paulo rdl.rdl_prot |= RD_RDL_W; 208cb314988SRui Paulo if (kve->kve_protection & KVME_PROT_EXEC) 209cb314988SRui Paulo rdl.rdl_prot |= RD_RDL_X; 21013776bf7SMark Johnston if (path != NULL) 21113c2d789SMark Johnston strlcpy(rdl.rdl_path, path, sizeof(rdl.rdl_path)); 212a7e13d50SMark Johnston if ((*cb)(&rdl, clnt_data) != 0) { 213a7e13d50SMark Johnston ret = RD_ERR; 214a7e13d50SMark Johnston break; 215a7e13d50SMark Johnston } 216cb314988SRui Paulo } 217cb314988SRui Paulo free(kves); 218a7e13d50SMark Johnston return (ret); 219cb314988SRui Paulo } 220cb314988SRui Paulo 221cb314988SRui Paulo void 222cb314988SRui Paulo rd_log(const int onoff) 223cb314988SRui Paulo { 224cb314988SRui Paulo DPRINTF("%s\n", __func__); 225cb314988SRui Paulo 226cb314988SRui Paulo (void)onoff; 227cb314988SRui Paulo } 228cb314988SRui Paulo 229cb314988SRui Paulo rd_agent_t * 230cb314988SRui Paulo rd_new(struct proc_handle *php) 231cb314988SRui Paulo { 232cb314988SRui Paulo rd_agent_t *rdap; 233cb314988SRui Paulo 234a7e13d50SMark Johnston rdap = malloc(sizeof(*rdap)); 235a7e13d50SMark Johnston if (rdap == NULL) 236a7e13d50SMark Johnston return (NULL); 237a7e13d50SMark Johnston 238cb314988SRui Paulo memset(rdap, 0, sizeof(rd_agent_t)); 239cb314988SRui Paulo rdap->rda_php = php; 240a7e13d50SMark Johnston rdap->rda_procstat = procstat_open_sysctl(); 241cb314988SRui Paulo 242a7e13d50SMark Johnston if (rd_reset(rdap) != RD_OK) { 243a7e13d50SMark Johnston rd_delete(rdap); 244a7e13d50SMark Johnston rdap = NULL; 245a7e13d50SMark Johnston } 246cb314988SRui Paulo return (rdap); 247cb314988SRui Paulo } 248cb314988SRui Paulo 249cb314988SRui Paulo rd_err_e 250cb314988SRui Paulo rd_objpad_enable(rd_agent_t *rdap, size_t padsize) 251cb314988SRui Paulo { 252cb314988SRui Paulo DPRINTF("%s\n", __func__); 253cb314988SRui Paulo 254cb314988SRui Paulo (void)rdap; 255cb314988SRui Paulo (void)padsize; 256cb314988SRui Paulo 257cb314988SRui Paulo return (RD_ERR); 258cb314988SRui Paulo } 259cb314988SRui Paulo 260cb314988SRui Paulo rd_err_e 261cb314988SRui Paulo rd_plt_resolution(rd_agent_t *rdap, uintptr_t pc, struct proc *proc, 262cb314988SRui Paulo uintptr_t plt_base, rd_plt_info_t *rpi) 263cb314988SRui Paulo { 264cb314988SRui Paulo DPRINTF("%s\n", __func__); 265cb314988SRui Paulo 266cb314988SRui Paulo (void)rdap; 267cb314988SRui Paulo (void)pc; 268cb314988SRui Paulo (void)proc; 269cb314988SRui Paulo (void)plt_base; 270cb314988SRui Paulo (void)rpi; 271cb314988SRui Paulo 272cb314988SRui Paulo return (RD_ERR); 273cb314988SRui Paulo } 274cb314988SRui Paulo 275a7e13d50SMark Johnston static int 276a7e13d50SMark Johnston rtld_syms(rd_agent_t *rdap, const char *rtldpath, u_long base) 277a7e13d50SMark Johnston { 278a7e13d50SMark Johnston GElf_Shdr shdr; 279a7e13d50SMark Johnston GElf_Sym sym; 280a7e13d50SMark Johnston Elf *e; 281a7e13d50SMark Johnston Elf_Data *data; 282a7e13d50SMark Johnston Elf_Scn *scn; 283a7e13d50SMark Johnston const char *symname; 284a7e13d50SMark Johnston Elf64_Word strscnidx; 285a7e13d50SMark Johnston int fd, i, ret; 286a7e13d50SMark Johnston 287a7e13d50SMark Johnston ret = 1; 288a7e13d50SMark Johnston e = NULL; 289a7e13d50SMark Johnston 290a7e13d50SMark Johnston fd = open(rtldpath, O_RDONLY); 291a7e13d50SMark Johnston if (fd < 0) 292a7e13d50SMark Johnston goto err; 293a7e13d50SMark Johnston 294a7e13d50SMark Johnston if (elf_version(EV_CURRENT) == EV_NONE) 295a7e13d50SMark Johnston goto err; 296a7e13d50SMark Johnston e = elf_begin(fd, ELF_C_READ, NULL); 29748da4e20SMark Johnston if (e == NULL) 298a7e13d50SMark Johnston goto err; 299a7e13d50SMark Johnston 300a7e13d50SMark Johnston scn = NULL; 301a7e13d50SMark Johnston while ((scn = elf_nextscn(e, scn)) != NULL) { 302a7e13d50SMark Johnston gelf_getshdr(scn, &shdr); 303a7e13d50SMark Johnston if (shdr.sh_type == SHT_DYNSYM) 304a7e13d50SMark Johnston break; 305a7e13d50SMark Johnston } 306a7e13d50SMark Johnston if (scn == NULL) 307a7e13d50SMark Johnston goto err; 308a7e13d50SMark Johnston 309a7e13d50SMark Johnston strscnidx = shdr.sh_link; 310a7e13d50SMark Johnston data = elf_getdata(scn, NULL); 311a7e13d50SMark Johnston if (data == NULL) 312a7e13d50SMark Johnston goto err; 313a7e13d50SMark Johnston 314a7e13d50SMark Johnston for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { 315a7e13d50SMark Johnston if (GELF_ST_TYPE(sym.st_info) != STT_FUNC || 316a7e13d50SMark Johnston GELF_ST_BIND(sym.st_info) != STB_GLOBAL) 317a7e13d50SMark Johnston continue; 318a7e13d50SMark Johnston symname = elf_strptr(e, strscnidx, sym.st_name); 319a7e13d50SMark Johnston if (symname == NULL) 320a7e13d50SMark Johnston continue; 321a7e13d50SMark Johnston 322a7e13d50SMark Johnston if (strcmp(symname, "r_debug_state") == 0) { 323a7e13d50SMark Johnston rdap->rda_preinit_addr = sym.st_value + base; 324a7e13d50SMark Johnston rdap->rda_dlactivity_addr = sym.st_value + base; 325a7e13d50SMark Johnston } else if (strcmp(symname, "_r_debug_postinit") == 0) { 326a7e13d50SMark Johnston rdap->rda_postinit_addr = sym.st_value + base; 327a7e13d50SMark Johnston } 328a7e13d50SMark Johnston } 329a7e13d50SMark Johnston 330a7e13d50SMark Johnston if (rdap->rda_preinit_addr != 0 && 331a7e13d50SMark Johnston rdap->rda_postinit_addr != 0 && 332a7e13d50SMark Johnston rdap->rda_dlactivity_addr != 0) 333a7e13d50SMark Johnston ret = 0; 334a7e13d50SMark Johnston 335a7e13d50SMark Johnston err: 336a7e13d50SMark Johnston if (e != NULL) 337a7e13d50SMark Johnston (void)elf_end(e); 338a7e13d50SMark Johnston if (fd >= 0) 339a7e13d50SMark Johnston (void)close(fd); 340a7e13d50SMark Johnston return (ret); 341a7e13d50SMark Johnston } 342a7e13d50SMark Johnston 343cb314988SRui Paulo rd_err_e 344cb314988SRui Paulo rd_reset(rd_agent_t *rdap) 345cb314988SRui Paulo { 346a7e13d50SMark Johnston struct kinfo_proc *kp; 347a7e13d50SMark Johnston struct kinfo_vmentry *kve; 348a7e13d50SMark Johnston Elf_Auxinfo *auxv; 349a7e13d50SMark Johnston const char *rtldpath; 350a7e13d50SMark Johnston u_long base; 351a7e13d50SMark Johnston rd_err_e rderr; 352a7e13d50SMark Johnston int count, i; 353cb314988SRui Paulo 354a7e13d50SMark Johnston kp = NULL; 355a7e13d50SMark Johnston auxv = NULL; 356a7e13d50SMark Johnston kve = NULL; 357a7e13d50SMark Johnston rderr = RD_ERR; 358a7e13d50SMark Johnston 359a7e13d50SMark Johnston kp = procstat_getprocs(rdap->rda_procstat, KERN_PROC_PID, 360a7e13d50SMark Johnston proc_getpid(rdap->rda_php), &count); 361a7e13d50SMark Johnston if (kp == NULL) 362cb314988SRui Paulo return (RD_ERR); 363a7e13d50SMark Johnston assert(count == 1); 364b252f278SMark Johnston 365a7e13d50SMark Johnston auxv = procstat_getauxv(rdap->rda_procstat, kp, &count); 366a7e13d50SMark Johnston if (auxv == NULL) 367a7e13d50SMark Johnston goto err; 368cb314988SRui Paulo 369a7e13d50SMark Johnston base = 0; 370a7e13d50SMark Johnston for (i = 0; i < count; i++) { 371a7e13d50SMark Johnston if (auxv[i].a_type == AT_BASE) { 372a7e13d50SMark Johnston base = auxv[i].a_un.a_val; 373a7e13d50SMark Johnston break; 374a7e13d50SMark Johnston } 375a7e13d50SMark Johnston } 376a7e13d50SMark Johnston if (i == count) 377a7e13d50SMark Johnston goto err; 378a7e13d50SMark Johnston 379a7e13d50SMark Johnston rtldpath = NULL; 380a7e13d50SMark Johnston kve = procstat_getvmmap(rdap->rda_procstat, kp, &count); 381a7e13d50SMark Johnston if (kve == NULL) 382a7e13d50SMark Johnston goto err; 383a7e13d50SMark Johnston for (i = 0; i < count; i++) { 384a7e13d50SMark Johnston if (kve[i].kve_start == base) { 385a7e13d50SMark Johnston rtldpath = kve[i].kve_path; 386a7e13d50SMark Johnston break; 387a7e13d50SMark Johnston } 388a7e13d50SMark Johnston } 389a7e13d50SMark Johnston if (i == count) 390a7e13d50SMark Johnston goto err; 391a7e13d50SMark Johnston 392a7e13d50SMark Johnston if (rtld_syms(rdap, rtldpath, base) != 0) 393a7e13d50SMark Johnston goto err; 394a7e13d50SMark Johnston 395a7e13d50SMark Johnston rderr = RD_OK; 396a7e13d50SMark Johnston 397a7e13d50SMark Johnston err: 398a7e13d50SMark Johnston if (kve != NULL) 399a7e13d50SMark Johnston procstat_freevmmap(rdap->rda_procstat, kve); 400a7e13d50SMark Johnston if (auxv != NULL) 401a7e13d50SMark Johnston procstat_freeauxv(rdap->rda_procstat, auxv); 402a7e13d50SMark Johnston if (kp != NULL) 403a7e13d50SMark Johnston procstat_freeprocs(rdap->rda_procstat, kp); 404a7e13d50SMark Johnston return (rderr); 405cb314988SRui Paulo } 406