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 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * 4.x ld.so directory caching: run-time link-editor specific functions.
29 */
30
31 #include <dirent.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include "_a.out.h"
37 #include "cache_a.out.h"
38 #include "_rtld.h"
39 #include "msg.h"
40
41 static int stol();
42 static int rest_ok();
43 static int verscmp();
44 static void fix_lo();
45 static int extract_name();
46 static int hash();
47
48 static struct link_object *get_lo();
49 static struct dbd *new_dbd();
50 static struct db *find_so();
51
52 #define SKIP_DOT(str) ((*str == '.') ? ++str : str)
53 #define EMPTY(str) ((str == NULL) || (*str == '\0'))
54 #define isdigit(c) (((c) >= '0') && ((c) <= '9') ? 1:0)
55
56 static struct dbd *dbd_head = NULL; /* head of data bases */
57
58
59 /*
60 * Given a db - find the highest shared versioned object. The
61 * highest versioned object is the .so with a matching major number
62 * but the highest minor number
63 */
64 char *
ask_db(dbp,file)65 ask_db(dbp, file)
66 struct db *dbp;
67 const char *file;
68 {
69 char *libname, *n;
70 char *mnp;
71 char *mjp;
72 int liblen;
73 int major = 0;
74 int to_min;
75 struct dbe *ep;
76 struct link_object *tlop;
77 int index;
78
79 n = (char *)file;
80 if ((liblen = extract_name(&n)) == -1)
81 return (NULL);
82 if ((libname = malloc(liblen + 1)) == 0)
83 return (NULL);
84 (void) strncpy(libname, n, liblen);
85 libname[liblen] = NULL;
86
87 if (strncmp(MSG_ORIG(MSG_FIL_DOTSODOT), (n + liblen),
88 MSG_FIL_DOTSODOT_SIZE))
89 return (NULL);
90
91 mnp = mjp = ((char *)file + MSG_FIL_LIB_SIZE + liblen +
92 MSG_FIL_DOTSODOT_SIZE);
93 if (!(stol(mjp, '.', &mnp, &major) && (*mnp == '.') &&
94 rest_ok(mnp + 1)))
95 return (NULL);
96 to_min = mnp - file + 1;
97
98 /*
99 * Search appropriate hash bucket for a matching entry.
100 */
101 index = hash(libname, liblen, major);
102 for (ep = (struct dbe *)&(dbp->db_hash[index]); (ep && ep->dbe_lop);
103 ep = ep->dbe_next == 0 ? NULL :
104 /* LINTED */
105 (struct dbe *)&AP(dbp)[ep->dbe_next]) {
106 /* LINTED */
107 tlop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
108 if (tlop->lo_major == major)
109 if (strcmp((char *)&AP(dbp)[tlop->lo_name],
110 libname) == 0)
111 break;
112 }
113
114 /*
115 * If no entry was found, we've lost.
116 */
117 if (!(ep && ep->dbe_lop))
118 return (NULL);
119 if (verscmp(file + to_min,
120 &AP(dbp)[ep->dbe_name] + tlop->lo_minor) > 0)
121 eprintf(&lml_main, ERR_WARNING, MSG_INTL(MSG_GEN_OLDREV),
122 &AP(dbp)[ep->dbe_name], file + to_min);
123 return (&AP(dbp)[ep->dbe_name]);
124 }
125
126 /*
127 * Given a directory name - give back a data base. The data base may have
128 * orginated from the mmapped file or temporarily created
129 */
130 struct db *
lo_cache(const char * ds)131 lo_cache(const char *ds)
132 {
133 struct db *dbp; /* database pointer */
134 struct dbd *dbdp; /* working database descriptor */
135 struct dbd **dbdpp; /* insertion pointer */
136
137 dbdpp = &dbd_head;
138 for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
139 if (strcmp(ds, &AP(dbdp->dbd_db)[dbdp->dbd_db->db_name]) == 0)
140 return (dbdp->dbd_db);
141 dbdpp = &dbdp->dbd_next;
142 }
143 if (dbp = find_so(ds)) {
144 (void) new_dbd(dbdpp, dbp);
145 }
146 return (dbp);
147 }
148
149 /*
150 * Build a database for the directory "ds".
151 */
152 static struct db *
find_so(const char * ds)153 find_so(const char *ds)
154 {
155 int fd; /* descriptor on directory */
156 int n; /* bytes from getdents */
157 char *cp; /* working char * */
158 rtld_stat_t sb; /* buffer for stat'ing directory */
159 struct db *dbp; /* database */
160 static caddr_t buf = NULL; /* buffer for doing getdents */
161 static long bs; /* cached blocksize for getdents */
162 struct link_object *tlop; /* working link object ptr. */
163 struct dirent *dp; /* directory entry ptr. */
164 struct dbe *ep; /* working db_entry ptr. */
165 char *mnp; /* where minor version begins */
166 char *mjp; /* where major version begins */
167 int m; /* the major number */
168 int to_min; /* index into string of minor */
169 int cplen; /* length of X */
170 int index; /* the hash value */
171
172 /*
173 * Try to open directory. Failing that, just return silently.
174 */
175 if ((fd = open(ds, O_RDONLY)) == -1)
176 return ((struct db *)NULL);
177
178 /*
179 * If we have not yet gotten a buffer for reading directories,
180 * allocate it now. Size it according to the most efficient size
181 * for the first directory we open successfully.
182 */
183 if (!buf) {
184 if (rtld_fstat(fd, &sb) == -1) {
185 (void) close(fd);
186 return ((struct db *)NULL);
187 }
188 bs = sb.st_blksize;
189 buf = calloc(bs, 1);
190 }
191
192 /*
193 * Have a directory, have a buffer. Allocate up a database
194 * and initialize it.
195 */
196 dbp = calloc(sizeof (struct db), 1);
197 dbp->db_name = RELPTR(dbp, calloc((strlen(ds) + 1), 1));
198 (void) strcpy((char *)&AP(dbp)[dbp->db_name], ds);
199
200 /*
201 * Scan the directory looking for shared libraries. getdents()
202 * failures are silently ignored and terminate the scan.
203 */
204 /* LINTED */
205 while ((n = getdents(fd, (struct dirent *)buf, bs)) > 0)
206 /* LINTED */
207 for (dp = (struct dirent *)buf;
208 /* LINTED */
209 dp && (dp < (struct dirent *)(buf + n));
210 /* LINTED */
211 dp = (struct dirent *)((dp->d_reclen == 0) ?
212 NULL : (char *)dp + dp->d_reclen)) {
213
214 /*
215 * If file starts with a "lib", then extract the X
216 * from libX.
217 */
218 cp = dp->d_name;
219 if ((cplen = extract_name(&cp)) == -1)
220 continue;
221
222 /*
223 * Is the next component ".so."?
224 */
225 if (strncmp(MSG_ORIG(MSG_FIL_DOTSODOT), (cp + cplen),
226 MSG_FIL_DOTSODOT_SIZE))
227 continue;
228
229 /*
230 * Check if next component is the major number and
231 * whether following components are legal.
232 */
233 mnp = mjp = (dp->d_name + MSG_FIL_LIB_SIZE + cplen +
234 MSG_FIL_DOTSODOT_SIZE);
235 if (!(stol(mjp, '.', &mnp, &m) && (*mnp == '.') &&
236 rest_ok(mnp + 1)))
237 continue;
238 to_min = mnp - dp->d_name + 1;
239
240 /*
241 * Have libX.so.major.minor - attempt to add it to the
242 * cache. If there is another with the same major
243 * number then the chose the object with the highest
244 * minor number
245 */
246 index = hash(cp, cplen, m);
247 ep = &(dbp->db_hash[index]);
248 if (ep->dbe_lop == NULL) {
249 ep->dbe_lop = (long)get_lo(dbp, cp,
250 cplen, m, to_min);
251 /* LINTED */
252 tlop = (struct link_object *)
253 &AP(dbp)[ep->dbe_lop];
254 (void) strcpy(&AP(dbp)[tlop->lo_next],
255 dp->d_name);
256 continue;
257 }
258 for (ep = &(dbp->db_hash[index]); ep;
259 /* LINTED */
260 ep = (struct dbe *)&AP(dbp)[ep->dbe_next]) {
261 /* LINTED */
262 tlop = (struct link_object *)
263 &AP(dbp)[ep->dbe_lop];
264
265 /*
266 * Choose the highest minor version
267 */
268 if ((tlop->lo_major == m) &&
269 (strncmp(&AP(dbp)[tlop->lo_name],
270 cp, cplen) == 0) &&
271 (*(&AP(dbp)[tlop->lo_name +
272 cplen]) == '\0')) {
273 if (verscmp(dp->d_name + to_min,
274 (char *)(&AP(dbp)[tlop->lo_next]
275 + to_min)) > 0)
276 (void) strcpy(&AP(dbp)
277 [tlop->lo_next],
278 dp->d_name);
279 break;
280 }
281 if (ep->dbe_next == NULL) {
282 ep->dbe_next = RELPTR(dbp,
283 calloc(sizeof (struct dbe), 1));
284 /* LINTED */
285 ep = (struct dbe *)
286 &AP(dbp)[ep->dbe_next];
287 ep->dbe_lop = (long)get_lo(dbp,
288 cp, cplen, m, to_min);
289 /* LINTED */
290 tlop = (struct link_object *)
291 &AP(dbp)[ep->dbe_lop];
292 (void) strcpy(&AP(dbp)[tlop->lo_next],
293 dp->d_name);
294 break;
295 }
296 }
297 }
298 fix_lo(dbp);
299 (void) close(fd);
300 return (dbp);
301 }
302
303 /*
304 * Allocate and fill in the fields for a link_object
305 */
306 static struct link_object *
get_lo(dbp,cp,cplen,m,n)307 get_lo(dbp, cp, cplen, m, n)
308 struct db *dbp; /* data base */
309 char *cp; /* ptr. to X of libX */
310 int cplen; /* length of X */
311 int m; /* major version */
312 int n; /* index to minor version */
313 {
314 struct link_object *lop; /* link_object to be returned */
315 struct link_object *tlop; /* working copy of the above */
316
317 /*
318 * Allocate a link object prototype in the database heap.
319 * Store the numeric major (interface) number, but the minor
320 * number is stored in the database as an index to the string
321 * representing the minor version. By keeping the minor version
322 * as a string, "subfields" (i.e., major.minor[.other.fields. etc.])
323 * are permitted. Although not meaningful to the link editor, this
324 * permits run-time substitution of arbitrary customer revisions,
325 * although introducing the confusion of overloading the lo_minor
326 * field in the database (!)
327 */
328 lop = (struct link_object *)RELPTR(dbp,
329 calloc(sizeof (struct link_object), 1));
330 /* LINTED */
331 tlop = (struct link_object *)&AP(dbp)[(long)lop];
332 tlop->lo_major = m;
333 tlop->lo_minor = n;
334
335 /*
336 * Allocate space for the complete path name on the host program's
337 * heap -- as we have to save it from the directory buffer which
338 * might otherwise get re-used on us. Note that this space
339 * is wasted -- we can not assume that it can be reclaimed.
340 */
341 tlop->lo_next = (long)RELPTR(dbp, calloc(MAXNAMLEN, 1));
342
343 /*
344 * Store the prototype name in the link object in the database.
345 */
346 tlop->lo_name = (long)RELPTR(dbp, calloc((cplen + 1), 1));
347 (void) strncpy((char *)&AP(dbp)[tlop->lo_name], cp, cplen);
348 return (lop);
349 }
350
351 /*
352 * Pull the "X" from libX, set name to X and return the
353 * length of X
354 */
355 static int
extract_name(name)356 extract_name(name)
357 char **name;
358 {
359 char *ls; /* string after LIB root */
360 char *dp; /* string before first delimiter */
361
362 if (strncmp(*name, MSG_ORIG(MSG_FIL_LIB), MSG_FIL_LIB_SIZE) == 0) {
363 ls = *name + MSG_FIL_LIB_SIZE;
364 if ((dp = (char *)strchr(ls, '.')) != (char *)0) {
365 *name = ls;
366 return (dp - ls);
367 }
368 }
369 return (-1);
370 }
371
372 /*
373 * Make a pass through the data base to set the dbe_name of a dbe. This
374 * is necessary because there may be several revisions of a library
375 * but only one will be chosen.
376 */
377 static void
fix_lo(dbp)378 fix_lo(dbp)
379 struct db *dbp;
380 {
381 int i; /* loop temporary */
382 int dirlen = strlen(&AP(dbp)[dbp->db_name]);
383 /* length of directory pathname */
384 char *cp; /* working temporary */
385 char *tp; /* working temporary */
386 struct dbe *ep; /* working copy of dbe */
387 struct link_object *lop; /* working copy of link_object */
388
389 for (i = 0; i < DB_HASH; i++) {
390 for (ep = &(dbp->db_hash[i]); ep && ep->dbe_lop;
391 (ep = ep->dbe_next == 0 ? NULL :
392 /* LINTED */
393 (struct dbe *)&AP(dbp)[ep->dbe_next])) {
394 /* LINTED */
395 lop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
396 tp = &AP(dbp)[lop->lo_next];
397 ep->dbe_name = RELPTR(dbp,
398 calloc((dirlen + strlen(tp) + 2), 1));
399 lop->lo_minor += dirlen + 1;
400 cp = strncpy(&AP(dbp)[ep->dbe_name],
401 &AP(dbp)[dbp->db_name], dirlen);
402 cp = strncpy(cp + dirlen, MSG_ORIG(MSG_STR_SLASH),
403 MSG_STR_SLASH_SIZE);
404 (void) strcpy(cp + 1, tp);
405 }
406 }
407 }
408
409 /*
410 * Allocate a new dbd, append it after dbdpp and set the dbd_dbp to dbp.
411 */
412 static struct dbd *
new_dbd(dbdpp,dbp)413 new_dbd(dbdpp, dbp)
414 struct dbd **dbdpp; /* insertion point */
415 struct db *dbp; /* db associated with this dbd */
416 {
417 struct dbd *dbdp; /* working dbd ptr. */
418
419 dbdp = malloc(sizeof (struct dbd));
420 dbdp->dbd_db = dbp;
421 dbdp->dbd_next = NULL;
422 *dbdpp = dbdp;
423 return (dbdp);
424 }
425
426 /*
427 * Calculate hash index for link object.
428 * This is based on X.major from libX.so.major.minor.
429 */
430 static int
hash(np,nchrs,m)431 hash(np, nchrs, m)
432 char *np; /* X of libX */
433 int nchrs; /* no of chrs. to hash on */
434 int m; /* the major version */
435 {
436 int h; /* for loop counter */
437 char *cp; /* working (char *) ptr */
438
439 for (h = 0, cp = np; h < nchrs; h++, cp++)
440 h = (h << 1) + *cp;
441 h += (h << 1) + m;
442 h = ((h & 0x7fffffff) % DB_HASH);
443 return (h);
444 }
445
446 /*
447 * Test whether the string is of digit[.digit]* format
448 */
449 static int
rest_ok(str)450 rest_ok(str)
451 char *str; /* input string */
452 {
453 int dummy; /* integer place holder */
454 int legal = 1; /* return flag */
455
456 while (!EMPTY(str)) {
457 if (!stol(str, '.', &str, &dummy)) {
458 legal = 0;
459 break;
460 }
461 if (EMPTY(str))
462 break;
463 else
464 /* LINTED */
465 (SKIP_DOT(str));
466 }
467 return (legal);
468 }
469
470 /*
471 * Compare 2 strings and test whether they are of the form digit[.digit]*.
472 * It will return -1, 0, or 1 depending on whether c1p is less, equal or
473 * greater than c2p
474 */
475 static int
verscmp(const char * c1p,const char * c2p)476 verscmp(const char *c1p, const char *c2p)
477 {
478 char *l_c1p = (char *)c1p; /* working copy of c1p */
479 char *l_c2p = (char *)c2p; /* working copy of c2p */
480 int l_c1p_ok = 0; /* is c1p a legal string */
481 int c2p_dig = 0; /* int that c1p currently */
482 /* represents */
483 int c1p_dig = 0; /* int that c2p currently */
484 /* represents */
485
486 while (((l_c1p_ok = stol(l_c1p, '.', &l_c1p, &c1p_dig)) == 1) &&
487 stol(l_c2p, '.', &l_c2p, &c2p_dig) && (c2p_dig == c1p_dig)) {
488 if (EMPTY(l_c1p) && EMPTY(l_c2p))
489 return (0);
490 else if (EMPTY(l_c1p) && !EMPTY(l_c2p) &&
491 rest_ok(SKIP_DOT(l_c2p)))
492 return (-1);
493 else if (EMPTY(l_c2p) && !EMPTY(l_c1p) &&
494 rest_ok(SKIP_DOT(l_c1p)))
495 return (1);
496 l_c1p++; l_c2p++;
497 };
498 if (!l_c1p_ok)
499 return (-1);
500 else if (c1p_dig < c2p_dig)
501 return (-1);
502 else if ((c1p_dig > c2p_dig) && rest_ok(SKIP_DOT(l_c1p)))
503 return (1);
504 else return (-1);
505 }
506
507 /*
508 * "stol" attempts to interpret a collection of characters between delimiters
509 * as a decimal digit. It stops interpreting when it reaches a delimiter or
510 * when character does not represent a digit. In the first case it returns
511 * success and the latter failure.
512 */
513 static int
stol(cp,delimit,ptr,i)514 stol(cp, delimit, ptr, i)
515 char *cp; /* ptr to input string */
516 char delimit; /* delimiter */
517 char **ptr; /* left pointing to next del. or */
518 /* illegal character */
519 int *i; /* digit that the string represents */
520 {
521 int c = 0; /* current char */
522 int n = 0; /* working copy of i */
523 int neg = 0; /* is number negative */
524
525 if (ptr != (char **)0)
526 *ptr = cp; /* in case no number is formed */
527
528 if (EMPTY(cp))
529 return (0);
530
531 if (!isdigit(c = *cp) && (c == '-')) {
532 neg++;
533 c = *++cp;
534 };
535 if (EMPTY(cp) || !isdigit(c))
536 return (0);
537
538 while (isdigit(c = *cp) && (*cp++ != '\0')) {
539 n *= 10;
540 n += c - '0';
541 };
542 if (ptr != (char **)0)
543 *ptr = cp;
544
545 if ((*cp == '\0') || (*cp == delimit)) {
546 *i = neg ? -n : n;
547 return (1);
548 };
549 return (0);
550 }
551