1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include	<libelf.h>
28 #include	<sys/reg.h>
29 #include	<rtld_db.h>
30 #include	"_rtld_db.h"
31 #include	"msg.h"
32 
33 
34 /*
35  * On amd64, basically, a PLT entry looks like this:
36  *
37  *	0x00  ff 25 00 00 00 00  jmpq   *func@got(%rip)  ; jmp GOT[N]
38  *	0x06  68 01 00 00 00     pushq  $0x1	       ; push index
39  *	0x0b  e9 00 00 00 00     jmpq   .plt0	       ; jmp plt[0]
40  *	0x10  ...
41  *
42  *  The first time around GOT[N] contains address of pushq; this forces
43  *	first time resolution to go thru PLT's first entry (which is a call)
44  *  The nth time around, the GOT[N] actually contains the resolved
45  *	address of the symbol(name), so the jmp is direct
46  */
47 /* ARGSUSED 3 */
48 rd_err_e
49 plt64_resolution(rd_agent_t *rap, psaddr_t pc, lwpid_t lwpid,
50 	psaddr_t pltbase, rd_plt_info_t *rpi)
51 {
52 	uint32_t	pcrel;
53 	psaddr_t	destaddr;
54 	psaddr_t	pltoff, pltaddr;
55 
56 
57 	if (rtld_db_version >= RD_VERSION3) {
58 		rpi->pi_flags = 0;
59 		rpi->pi_baddr = 0;
60 	}
61 
62 	pltoff = pc - pltbase;
63 	pltaddr = pltbase +
64 		((pltoff / M_PLT_ENTSIZE) * M_PLT_ENTSIZE);
65 	/*
66 	 * This is the target of the jmp instruction
67 	 */
68 	if (ps_pread(rap->rd_psp, pltaddr + 2, (char *)&pcrel,
69 	    sizeof (pcrel)) != PS_OK) {
70 		LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(pltaddr + 2)));
71 		return (RD_ERR);
72 	}
73 
74 	/*
75 	 * the offset to the GOT table entry is
76 	 * PC-relative.
77 	 */
78 	destaddr = pcrel + pltaddr + 6;
79 
80 	/*
81 	 * Find out what's pointed to by @OFFSET_INTO_GOT
82 	 */
83 	if (ps_pread(rap->rd_psp, destaddr, (char *)&destaddr,
84 	    sizeof (destaddr)) != PS_OK) {
85 		LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(destaddr)));
86 		return (RD_ERR);
87 	}
88 	if (destaddr == (pltaddr + 6)) {
89 		rd_err_e	rerr;
90 		/*
91 		 * If GOT[ind] points to PLT+6 then this is the first
92 		 * time through this PLT.
93 		 */
94 		if ((rerr = rd_binder_exit_addr(rap, MSG_ORIG(MSG_SYM_RTBIND),
95 		    &(rpi->pi_target))) != RD_OK) {
96 			return (rerr);
97 		}
98 		rpi->pi_skip_method = RD_RESOLVE_TARGET_STEP;
99 		rpi->pi_nstep = 1;
100 	} else {
101 		/*
102 		 * This is the n'th time through and GOT[ind] points
103 		 * to the final destination.
104 		 */
105 		rpi->pi_skip_method = RD_RESOLVE_STEP;
106 		rpi->pi_nstep = 1;
107 		rpi->pi_target = 0;
108 		if (rtld_db_version >= RD_VERSION3) {
109 			rpi->pi_flags |= RD_FLG_PI_PLTBOUND;
110 			rpi->pi_baddr = destaddr;
111 		}
112 	}
113 
114 	return (RD_OK);
115 }
116