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