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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 30 #include <libelf.h> 31 #include <sys/regset.h> 32 #include <rtld_db.h> 33 #include <_rtld_db.h> 34 #include <msg.h> 35 #include <stdio.h> 36 37 38 typedef struct { 39 rd_agent_t *rlid_rap; 40 psaddr_t rlid_pltaddr; 41 psaddr_t rlid_gotaddr; 42 rd_err_e rlid_ret; 43 } Rli_data; 44 45 /* 46 * Iterator function for rd_loadobj_iter - we are scaning 47 * each object loaded to try and find the object defining 48 * the current PLT being traversed - when found we return 49 * the GOT pointer for that object. 50 */ 51 static int 52 rli_func(const rd_loadobj_t *rl, void *data) 53 { 54 Ehdr ehdr; 55 Phdr phdr; 56 Rli_data *rli_data; 57 ulong_t off; 58 psaddr_t baseaddr; 59 psaddr_t pltaddr; 60 uint_t i; 61 uint_t found_obj = 0; 62 psaddr_t dynbase = 0; 63 rd_agent_t *rap; 64 rd_err_e rc; 65 66 rli_data = (Rli_data *)data; 67 pltaddr = rli_data->rlid_pltaddr; 68 rap = rli_data->rlid_rap; 69 70 if (ps_pread(rap->rd_psp, rl->rl_base, (char *)&ehdr, 71 sizeof (Ehdr)) != PS_OK) { 72 rli_data->rlid_ret = RD_ERR; 73 LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_X86_1), 74 EC_ADDR(rl->rl_base))); 75 return (0); 76 } 77 if (ehdr.e_type == ET_EXEC) 78 baseaddr = 0; 79 else 80 baseaddr = rl->rl_base; 81 82 off = rl->rl_base + ehdr.e_phoff; 83 for (i = 0; i < ehdr.e_phnum; i++) { 84 if (ps_pread(rap->rd_psp, off, (char *)&phdr, 85 sizeof (Phdr)) != PS_OK) { 86 rli_data->rlid_ret = RD_ERR; 87 LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_X86_1), 88 EC_ADDR(rl->rl_base))); 89 return (0); 90 } 91 if (phdr.p_type == PT_LOAD) { 92 if ((pltaddr >= (phdr.p_vaddr + baseaddr)) && 93 (pltaddr < (phdr.p_vaddr + baseaddr + 94 phdr.p_memsz))) { 95 found_obj = 1; 96 } 97 } else if (phdr.p_type == PT_DYNAMIC) { 98 dynbase = phdr.p_vaddr + baseaddr; 99 } 100 off += ehdr.e_phentsize; 101 102 if (found_obj & dynbase) 103 break; 104 } 105 106 if (found_obj) { 107 Dyn dynent; 108 109 if (dynbase == 0) { 110 LOG(ps_plog(MSG_ORIG(MSG_DB_NODYN_X86))); 111 rli_data->rlid_ret = RD_ERR; 112 return (0); 113 } 114 if ((rc = find_dynamic_ent32(rap, dynbase, DT_PLTGOT, 115 &dynent)) != RD_OK) { 116 rli_data->rlid_ret = rc; 117 return (0); 118 } 119 /* 120 * We've found our gotpntr. Return (0) to stop 121 * the 'iteration'. 122 */ 123 rli_data->rlid_gotaddr = dynent.d_un.d_val + baseaddr; 124 return (0); 125 } 126 127 return (1); 128 } 129 130 131 /* 132 * On x86, basically, a PLT entry looks like this: 133 * 8048738: ff 25 c8 45 05 08 jmp *0x80545c8 < OFFSET_INTO_GOT> 134 * 804873e: 68 20 00 00 00 pushl $0x20 135 * 8048743: e9 70 ff ff ff jmp 0xffffff70 <80486b8> < &.plt > 136 * 137 * The first time around OFFSET_INTO_GOT contains address of pushl; this forces 138 * first time resolution to go thru PLT's first entry (which is a call) 139 * The nth time around, the OFFSET_INTO_GOT actually contains the resolved 140 * address of the symbol(name), so the jmp is direct [VT] 141 * The only complication is when going from a .so to an a.out or to another 142 * .so, we must resolve where the GOT table is for the given object. 143 */ 144 /* ARGSUSED 3 */ 145 rd_err_e 146 plt32_resolution(rd_agent_t *rap, psaddr_t pc, lwpid_t lwpid, 147 psaddr_t pltbase, rd_plt_info_t *rpi) 148 { 149 unsigned addr; 150 unsigned ebx; 151 psaddr_t pltoff, pltaddr; 152 153 154 if (rtld_db_version >= RD_VERSION3) { 155 rpi->pi_flags = 0; 156 rpi->pi_baddr = 0; 157 } 158 159 pltoff = pc - pltbase; 160 pltaddr = pltbase + 161 ((pltoff / M_PLT_ENTSIZE) * M_PLT_ENTSIZE); 162 /* 163 * This is the target of the jmp instruction 164 */ 165 if (ps_pread(rap->rd_psp, pltaddr + 2, (char *)&addr, 166 sizeof (unsigned)) != PS_OK) { 167 LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(pltaddr + 2))); 168 return (RD_ERR); 169 } 170 171 /* 172 * Is this branch %ebx relative 173 */ 174 if (ps_pread(rap->rd_psp, pltaddr + 1, (char *)&ebx, 175 sizeof (unsigned)) != PS_OK) { 176 LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(pltaddr + 1))); 177 return (RD_ERR); 178 } 179 180 /* 181 * If this .plt call is made via a GOT table (pic code), then 182 * in order to resolve the PLT we must determine where the 183 * GOT table is for the object making the call. 184 * 185 * We do this by using the rd_loadobj_iter() logic to scan 186 * all of the objects currently loaded into memory, when we 187 * find one which contains the .PLT table in question - we 188 * find the GOT address for that object. 189 */ 190 if ((ebx & 0xff) == 0xa3) { 191 rd_err_e rderr; 192 Rli_data rli_data; 193 194 rli_data.rlid_ret = RD_OK; 195 rli_data.rlid_pltaddr = pltaddr; 196 rli_data.rlid_rap = rap; 197 rli_data.rlid_gotaddr = 0; 198 if ((rderr = _rd_loadobj_iter32(rap, rli_func, &rli_data)) 199 != RD_OK) { 200 return (rderr); 201 } 202 203 if (rli_data.rlid_ret != RD_OK) { 204 return (rli_data.rlid_ret); 205 } 206 207 if (rli_data.rlid_gotaddr == 0) { 208 LOG(ps_plog(MSG_ORIG(MSG_DB_NOGOT_X86))); 209 return (RD_ERR); 210 } 211 addr += rli_data.rlid_gotaddr; 212 } 213 214 /* 215 * Find out what's pointed to by @OFFSET_INTO_GOT 216 */ 217 if (ps_pread(rap->rd_psp, addr, (char *)&addr, 218 sizeof (unsigned)) != PS_OK) { 219 LOG(ps_plog(MSG_ORIG(MSG_DB_READFAIL_2), EC_ADDR(addr))); 220 return (RD_ERR); 221 } 222 if (addr == (pltaddr + 6)) { 223 rd_err_e rerr; 224 /* 225 * If GOT[ind] points to PLT+6 then this is the first 226 * time through this PLT. 227 */ 228 if ((rerr = rd_binder_exit_addr(rap, MSG_ORIG(MSG_SYM_RTBIND), 229 &(rpi->pi_target))) != RD_OK) { 230 return (rerr); 231 } 232 rpi->pi_skip_method = RD_RESOLVE_TARGET_STEP; 233 rpi->pi_nstep = 1; 234 } else { 235 /* 236 * This is the n'th time through and GOT[ind] points 237 * to the final destination. 238 */ 239 rpi->pi_skip_method = RD_RESOLVE_STEP; 240 rpi->pi_nstep = 1; 241 rpi->pi_target = 0; 242 if (rtld_db_version >= RD_VERSION3) { 243 rpi->pi_flags |= RD_FLG_PI_PLTBOUND; 244 rpi->pi_baddr = addr; 245 } 246 } 247 248 return (RD_OK); 249 } 250