xref: /illumos-gate/usr/src/cmd/sgs/librtld_db/amd64/plt64_resolution.c (revision bb5e3b2f129cc39517b925419c22f69a378ec023)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include	<libelf.h>
30 #include	<sys/reg.h>
31 #include	<rtld_db.h>
32 #include	"_rtld_db.h"
33 #include	"msg.h"
34 
35 
36 /*
37  * On amd64, basically, a PLT entry looks like this:
38  *
39  *	0x00  ff 25 00 00 00 00  jmpq   *func@got(%rip)  ; jmp GOT[N]
40  *	0x06  68 01 00 00 00     pushq  $0x1	       ; push index
41  *	0x0b  e9 00 00 00 00     jmpq   .plt0	       ; jmp plt[0]
42  *	0x10  ...
43  *
44  *  The first time around GOT[N] contains address of pushq; this forces
45  *	first time resolution to go thru PLT's first entry (which is a call)
46  *  The nth time around, the GOT[N] actually contains the resolved
47  *	address of the symbol(name), so the jmp is direct
48  */
49 /* ARGSUSED 3 */
50 rd_err_e
51 plt64_resolution(rd_agent_t *rap, psaddr_t pc, lwpid_t lwpid,
52 	psaddr_t pltbase, rd_plt_info_t *rpi)
53 {
54 	uint32_t	pcrel;
55 	psaddr_t	destaddr;
56 	psaddr_t	pltoff, pltaddr;
57 
58 
59 	if (rtld_db_version >= RD_VERSION3) {
60 		rpi->pi_flags = 0;
61 		rpi->pi_baddr = 0;
62 	}
63 
64 	pltoff = pc - pltbase;
65 	pltaddr = pltbase +
66 		((pltoff / M_PLT_ENTSIZE) * M_PLT_ENTSIZE);
67 	/*
68 	 * This is the target of the jmp instruction
69 	 */
70 	if (ps_pread(rap->rd_psp, pltaddr + 2, (char *)&pcrel,
71 	    sizeof (pcrel)) != PS_OK) {
72 		LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(pltaddr + 2)));
73 		return (RD_ERR);
74 	}
75 
76 	/*
77 	 * the offset to the GOT table entry is
78 	 * PC-relative.
79 	 */
80 	destaddr = pcrel + pltaddr + 6;
81 
82 	/*
83 	 * Find out what's pointed to by @OFFSET_INTO_GOT
84 	 */
85 	if (ps_pread(rap->rd_psp, destaddr, (char *)&destaddr,
86 	    sizeof (destaddr)) != PS_OK) {
87 		LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(destaddr)));
88 		return (RD_ERR);
89 	}
90 	if (destaddr == (pltaddr + 6)) {
91 		rd_err_e	rerr;
92 		/*
93 		 * If GOT[ind] points to PLT+6 then this is the first
94 		 * time through this PLT.
95 		 */
96 		if ((rerr = rd_binder_exit_addr(rap, MSG_ORIG(MSG_SYM_RTBIND),
97 		    &(rpi->pi_target))) != RD_OK) {
98 			return (rerr);
99 		}
100 		rpi->pi_skip_method = RD_RESOLVE_TARGET_STEP;
101 		rpi->pi_nstep = 1;
102 	} else {
103 		/*
104 		 * This is the n'th time through and GOT[ind] points
105 		 * to the final destination.
106 		 */
107 		rpi->pi_skip_method = RD_RESOLVE_STEP;
108 		rpi->pi_nstep = 1;
109 		rpi->pi_target = 0;
110 		if (rtld_db_version >= RD_VERSION3) {
111 			rpi->pi_flags |= RD_FLG_PI_PLTBOUND;
112 			rpi->pi_baddr = destaddr;
113 		}
114 	}
115 
116 	return (RD_OK);
117 }
118