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 <sys/mman.h> 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <procfs.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <stdio.h> 34 #include <string.h> 35 #include <limits.h> 36 #include <errno.h> 37 #include <alloca.h> 38 #include "rtld.h" 39 #include "rtc.h" 40 #include "_crle.h" 41 #include "msg.h" 42 43 /* 44 * Routines for dumping alternate objects under CRLE_AUD_DLDUMP mode. 45 */ 46 static Addr membgn = 0; 47 static Addr memend = 0; 48 49 /* 50 * For each file in the configuration file that requires an alternate (dldump()) 51 * version, add the object to the processes main link-map. The process head 52 * may be an application, shared object, or lddstub. In any case this object 53 * may be augmented with other objects defined within the configuration file. 54 * 55 * Each file is initially loaded with RTLD_CONFGEN so that no dependency 56 * analysis, relocation, or user code (.init's) is executed. By skipping 57 * analysis we save time and allow for a family of objects to be dumped that 58 * may not have all relocations satisfied. If necessary, a later call to 59 * dlopen() using RTLD_NOW will force relocations to occur. 60 * 61 * A mapping range is maintained to span the mapping of each objects, and this 62 * range is finally written back to the caller. 63 */ 64 static int 65 /* ARGSUSED1 */ 66 load(const char *opath, const char *npath) 67 { 68 Grp_hdl * ghp; 69 Rt_map * lmp; 70 Addr _membgn, _memend; 71 72 if ((ghp = (Grp_hdl *)dlmopen(LM_ID_BASE, opath, 73 (RTLD_LAZY | RTLD_GLOBAL | RTLD_CONFGEN))) == NULL) { 74 (void) fprintf(stderr, MSG_INTL(MSG_DL_OPEN), 75 MSG_ORIG(MSG_FIL_LIBCRLE), dlerror()); 76 return (1); 77 } 78 lmp = ghp->gh_ownlmp; 79 FLAGS1(lmp) |= FL1_RT_CONFSET; 80 81 /* 82 * Establish the mapping range of the objects dumped so far. 83 */ 84 _membgn = ADDR(lmp); 85 _memend = (ADDR(lmp) + MSIZE(lmp)); 86 87 if (membgn == 0) { 88 membgn = _membgn; 89 memend = _memend; 90 } else { 91 if (membgn > _membgn) 92 membgn = _membgn; 93 if (memend < _memend) 94 memend = _memend; 95 } 96 return (0); 97 } 98 99 /* 100 * dldump(3x) an object that is already part of the main link-map list. 101 */ 102 static int 103 dump(const char *opath, const char *npath) 104 { 105 (void) unlink(npath); 106 107 if (dldump(opath, npath, dlflag) != 0) { 108 (void) fprintf(stderr, MSG_INTL(MSG_DL_DUMP), 109 MSG_ORIG(MSG_FIL_LIBCRLE), dlerror()); 110 return (1); 111 } 112 return (0); 113 } 114 115 /* 116 * Traverse a configuration file directory/file list. Each file within the 117 * list is maintained as both a full pathname and a simple filename - we're 118 * only interested in one. 119 * 120 * This rutine is called twice, once to insure the appropriate objects are 121 * mapped in (fptr == load()) and then once again to dldump(3x) the mapped 122 * objects (fptr == dump()). 123 */ 124 static int 125 scanconfig(Addr addr, int (*fptr)()) 126 { 127 Rtc_head * head = (Rtc_head *)addr; 128 Rtc_obj * obj; 129 Rtc_dir * dirtbl; 130 Rtc_file * filetbl; 131 const char *str, *strtbl; 132 133 /* LINTED */ 134 strtbl = (const char *)((char *)addr + head->ch_str); 135 136 /* 137 * Scan the directory and filename arrays looking for alternatives. 138 */ 139 for (dirtbl = (Rtc_dir *)(head->ch_dir + addr); 140 dirtbl->cd_obj; dirtbl++) { 141 142 obj = (Rtc_obj *)(dirtbl->cd_obj + addr); 143 str = strtbl + obj->co_name; 144 145 if (obj->co_flags & RTC_OBJ_NOEXIST) 146 continue; 147 148 for (filetbl = (Rtc_file *)(dirtbl->cd_file + addr); 149 filetbl->cf_obj; filetbl++) { 150 151 obj = (Rtc_obj *)(filetbl->cf_obj + addr); 152 str = strtbl + obj->co_name; 153 154 if ((obj->co_flags & 155 (RTC_OBJ_DUMP | RTC_OBJ_REALPTH | RTC_OBJ_EXEC)) == 156 (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) { 157 if ((*fptr)(str, strtbl + obj->co_alter) != 0) 158 return (1); 159 } 160 } 161 } 162 163 /* 164 * Are we dumping a specific application. 165 */ 166 if (head->ch_app) { 167 if (fptr == load) { 168 Grp_hdl * ghp; 169 170 /* 171 * Obtain a handle to the application and set the 172 * FL1_RT_CONFSET flag. 173 */ 174 if ((ghp = dlmopen(LM_ID_BASE, 0, 175 (RTLD_NOLOAD | RTLD_CONFGEN))) == 0) 176 return (1); 177 FLAGS1(ghp->gh_ownlmp) |= FL1_RT_CONFSET; 178 179 } else { 180 /* 181 * If we're dumping and this configuration is for a 182 * specific application dump it also. 183 */ 184 /* LINTED */ 185 obj = (Rtc_obj *)((char *)addr + head->ch_app); 186 str = strtbl + obj->co_alter; 187 188 if (dump((const char *)0, str) != 0) 189 return (1); 190 } 191 } 192 193 return (0); 194 } 195 196 /* 197 * Before loading any dependencies determine the present memory mappings being 198 * used and fill any holes between these mappings. This insures that all 199 * dldump()'ed dependencies will live in a single consecutive address range. 200 */ 201 int 202 filladdr(void) 203 { 204 prmap_t *maps, *_maps; 205 struct stat status; 206 int fd = 0, err, num, _num; 207 size_t size, syspagsz; 208 uintptr_t laddr = 0, saddr; 209 pstatus_t prstatus; 210 211 /* 212 * Open /proc/self/status to determine the virtual address of the 213 * process heap. 214 */ 215 if ((fd = open(MSG_ORIG(MSG_PTH_PROCSTATUS), O_RDONLY)) == -1) { 216 err = errno; 217 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 218 MSG_ORIG(MSG_FIL_LIBCRLE), MSG_ORIG(MSG_PTH_PROCSTATUS), 219 strerror(err)); 220 return (1); 221 } 222 if (read(fd, &prstatus, sizeof (pstatus_t)) != sizeof (pstatus_t)) { 223 err = errno; 224 (void) fprintf(stderr, MSG_INTL(MSG_SYS_READ), 225 MSG_ORIG(MSG_FIL_LIBCRLE), MSG_ORIG(MSG_PTH_PROCSTATUS), 226 strerror(err)); 227 (void) close(fd); 228 return (1); 229 } 230 (void) close(fd); 231 232 /* 233 * Round the process heap to the next page boundary so that it can be 234 * used to isolated the executable's mappings (pr_brkbase typically 235 * occurs at the end, but within, the executable's data segment). As 236 * libcrle is used as an audit library, no process user code has run 237 * so there can't be any heap. pr_brksize is added here for 238 * completeness. 239 */ 240 syspagsz = sysconf(_SC_PAGESIZE); 241 saddr = M_PROUND(prstatus.pr_brkbase + prstatus.pr_brksize); 242 243 /* 244 * Open /proc/self/rmap to obtain the processes reserved mappings. 245 */ 246 if ((fd = open(MSG_ORIG(MSG_PTH_PROCRMAP), O_RDONLY)) == -1) { 247 err = errno; 248 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 249 MSG_ORIG(MSG_FIL_LIBCRLE), MSG_ORIG(MSG_PTH_PROCRMAP), 250 strerror(err)); 251 return (1); 252 } 253 (void) fstat(fd, &status); 254 255 /* 256 * Determine number of mappings - use alloca so as not to perturb any 257 * mapping information by a malloc, which itself might add a mapping. 258 */ 259 /* LINTED */ 260 num = (int)(status.st_size / sizeof (prmap_t)); 261 size = num * sizeof (prmap_t); 262 263 if ((maps = alloca(size)) == 0) { 264 (void) fprintf(stderr, MSG_INTL(MSG_SYS_ALLOC), 265 MSG_ORIG(MSG_FIL_LIBCRLE), strerror(ENOMEM)); 266 (void) close(pfd); 267 return (1); 268 } 269 270 if (read(fd, (void *)maps, size) < 0) { 271 err = errno; 272 (void) fprintf(stderr, MSG_INTL(MSG_SYS_READ), 273 MSG_ORIG(MSG_FIL_LIBCRLE), MSG_ORIG(MSG_PTH_PROCRMAP), 274 strerror(err)); 275 (void) close(fd); 276 return (1); 277 } 278 (void) close(fd); 279 280 /* 281 * Use /dev/null for filling holes. 282 */ 283 if ((fd = open(MSG_ORIG(MSG_PTH_DEVNULL), O_RDONLY)) == -1) { 284 err = errno; 285 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 286 MSG_ORIG(MSG_FIL_LIBCRLE), MSG_ORIG(MSG_PTH_DEVNULL), 287 strerror(err)); 288 return (1); 289 } 290 291 /* 292 * Scan each mapping - note it is assummed that the mappings are 293 * presented in order. We fill holes between mappings. On intel 294 * the last mapping is usually the data segment of ld.so.1, after 295 * this comes a red zone into which non-fixed mapping won't get 296 * place. Thus we can simply bail from the loop after seeing the 297 * last mapping. 298 */ 299 for (_num = 0, _maps = maps; _num < num; _num++, _maps++) { 300 /* 301 * Skip all mappings below brkbase, these represent the 302 * executable (and the stack on intel). 303 */ 304 if ((laddr == 0) && 305 ((_maps->pr_vaddr + _maps->pr_size) <= saddr)) 306 continue; 307 308 /* 309 * For each consecutive mapping determine the hole between each 310 * and fill it from /dev/null. 311 */ 312 if (laddr == 0) { 313 laddr = _maps->pr_vaddr + _maps->pr_size; 314 continue; 315 } 316 317 if ((size = _maps->pr_vaddr - laddr) != 0) { 318 if (mmap((void *)laddr, size, PROT_NONE, 319 (MAP_FIXED | MAP_PRIVATE), fd, 0) == MAP_FAILED) { 320 err = errno; 321 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), 322 MSG_ORIG(MSG_FIL_LIBCRLE), 323 MSG_ORIG(MSG_PTH_DEVNULL), strerror(err)); 324 return (1); 325 } 326 } 327 laddr = _maps->pr_vaddr + _maps->pr_size; 328 } 329 330 /* 331 * It's been observed that there may be space between the end of the 332 * last mapping (typically ld.so.1), and the kernel base address. As 333 * there's no interface to determine the kernel base address, keep 334 * filling in pages until we get an error. We'll get ENOMEM once we 335 * hit the kernel base address. 336 */ 337 while (laddr) { 338 if (mmap((void *)laddr, syspagsz, PROT_NONE, 339 (MAP_FIXED | MAP_PRIVATE), fd, 0) == MAP_FAILED) { 340 err = errno; 341 if (err == ENOMEM) 342 break; 343 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), 344 MSG_ORIG(MSG_FIL_LIBCRLE), 345 MSG_ORIG(MSG_PTH_DEVNULL), strerror(err)); 346 return (1); 347 } 348 laddr += syspagsz; 349 } 350 351 /* 352 * Close /dev/null. 353 */ 354 (void) close(fd); 355 return (0); 356 } 357 358 /* 359 * Dump alternative objects as part of building a configuration file. A temp 360 * configuration is already built and made available to the process, and is 361 * located via dlinfo(). Having load()'ed each object, and dump()'ed its image, 362 * the final memory reservation infoamtion is returned to the caller. 363 */ 364 int 365 dumpconfig(void) 366 { 367 char buffer[PATH_MAX]; 368 Addr config; 369 Dl_info info; 370 371 /* 372 * Determine the configuration file and where it is mapped. 373 */ 374 if (dlinfo((void *)NULL, RTLD_DI_CONFIGADDR, &info) == -1) { 375 (void) fprintf(stderr, MSG_INTL(MSG_DL_INFO), 376 MSG_ORIG(MSG_FIL_LIBCRLE), dlerror()); 377 return (1); 378 } 379 config = (Addr)info.dli_fbase; 380 381 /* 382 * Scan the configuration file for alternative entries. 383 */ 384 if (scanconfig(config, load) != 0) 385 return (1); 386 387 /* 388 * Having mapped all objects, relocate them. It would be nice if we 389 * could drop this step altogether, and have dldump() carry out just 390 * those relocations required, but when binding to an application we 391 * need to handle copy relocations - these can affect bindings (in the 392 * case of things like libld.so which have direct bindings) and require 393 * that the data being copied is itself relocated. 394 */ 395 if (dlmopen(LM_ID_BASE, 0, (RTLD_NOW | RTLD_CONFGEN)) == 0) 396 return (1); 397 398 /* 399 * Rescan the configuration dumping out each alternative file. 400 */ 401 if (scanconfig(config, dump) != 0) 402 return (1); 403 404 /* 405 * Having established the memory range of the dumped images and 406 * sucessfully dumped them out, report back to the caller. 407 */ 408 (void) sprintf(buffer, MSG_ORIG(MSG_AUD_RESBGN), EC_ADDR(membgn)); 409 (void) write(pfd, buffer, strlen(buffer)); 410 411 (void) sprintf(buffer, MSG_ORIG(MSG_AUD_RESEND), EC_ADDR(memend)); 412 (void) write(pfd, buffer, strlen(buffer)); 413 414 return (0); 415 } 416