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