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 */
50rd_err_e
51plt64_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