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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
28 * All Rights Reserved
29 */
30
31 /*
32 * Copyright (c) 1987, 1988 Microsoft Corporation
33 * All Rights Reserved
34 */
35
36 /*
37 * Copyright (c) 1979 Regents of the University of California
38 */
39
40 #pragma ident "%Z%%M% %I% %E% SMI"
41
42 #include <stdio.h>
43 #include "a.out.h"
44 #include <ctype.h>
45 #include <wchar.h>
46 #include <wctype.h>
47 #include <libelf.h>
48 #include <sys/elf.h>
49 #include <locale.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include <sys/types.h>
53 #include <unistd.h>
54 #include <limits.h>
55 #include <widec.h>
56 #include <gelf.h>
57 #include <errno.h>
58
59
60 #define NOTOUT 0
61 #define AOUT 1
62 #define ELF 4
63
64 struct aexec ahdr;
65
66 /* used to maintain a list of program sections to look in */
67 typedef struct sec_name {
68 char *name;
69 struct sec_name *next;
70 } sec_name_t;
71
72 /*
73 * function prototypes
74 */
75 static void Usage();
76 static void find(long);
77 static int ismagic(int, struct aexec *, FILE *);
78 static int tryelf(FILE *);
79 static int dirt(int, int);
80
81
82 /*
83 * Strings - extract strings from an object file for whatever
84 *
85 * The algorithm is to look for sequences of "non-junk" characters
86 * The variable "minlen" is the minimum length string printed.
87 * This helps get rid of garbage.
88 * Default minimum string length is 4 characters.
89 *
90 */
91
92 #define DEF_MIN_STRING 4
93
94 static int tflg;
95 static char t_format;
96 static int aflg;
97 static int minlength = 0;
98 static int isClocale = 0;
99 static char *buf = NULL;
100 static char *tbuf = NULL;
101 static size_t buf_size = 0;
102 static int rc = 0; /* exit code */
103
104 /*
105 * Returns 0 when sections have been successfully looked through,
106 * otherwise returns 1.
107 */
108 static int
look_in_sections(char * file,sec_name_t * seclistptr)109 look_in_sections(char *file, sec_name_t *seclistptr)
110 {
111 int fd = fileno(stdin);
112 int found_sec;
113 int rc = 0;
114 Elf *elf;
115 GElf_Ehdr ehdr;
116 Elf_Scn *scn;
117 GElf_Shdr shdr;
118
119 (void) lseek(fd, 0L, 0);
120 elf = elf_begin(fd, ELF_C_READ, NULL);
121 if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *)NULL) {
122 (void) fprintf(stderr, "%s: %s\n", file, elf_errmsg(-1));
123 (void) elf_end(elf);
124 return (1);
125 }
126 scn = 0;
127 while ((scn = elf_nextscn(elf, scn)) != 0) {
128 found_sec = 0;
129 if (gelf_getshdr(scn, &shdr) == (GElf_Shdr *)0) {
130 (void) fprintf(stderr, "%s: %s\n", file,
131 elf_errmsg(-1));
132 rc = 1;
133 continue;
134 }
135
136 if (seclistptr != NULL) {
137 char *scn_name;
138
139 /* Only look in the specified section(s). */
140 if ((scn_name = elf_strptr(elf, ehdr.e_shstrndx,
141 (size_t)shdr.sh_name)) == (char *)NULL) {
142 (void) fprintf(stderr, "%s: %s\n", file,
143 elf_errmsg(-1));
144 rc = 1;
145 continue;
146 } else {
147 sec_name_t *sptr;
148
149 for (sptr = seclistptr; sptr != NULL;
150 sptr = sptr->next) {
151 if (strcmp(scn_name, sptr->name) == 0) {
152 found_sec = 1;
153 break;
154 }
155 }
156 }
157 } else {
158 /*
159 * Look through program sections that are
160 * loaded in memory.
161 */
162 if ((shdr.sh_flags & SHF_ALLOC) &&
163 (shdr.sh_type == SHT_PROGBITS)) {
164 found_sec = 1;
165 }
166 }
167 if (found_sec == 1) {
168 (void) fseek(stdin, (long)shdr.sh_offset, 0);
169 find((long)shdr.sh_size);
170 }
171 }
172 return (rc);
173 }
174
175 int
main(argc,argv)176 main(argc, argv)
177 int argc;
178 char *argv[];
179 {
180 int hsize;
181 int htype;
182 char *locale;
183 int opt;
184 int i;
185 sec_name_t *seclistptr = NULL;
186 sec_name_t *seclistendptr;
187 sec_name_t *sptr;
188
189 (void) setlocale(LC_ALL, "");
190
191 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
192 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
193 #endif
194 (void) textdomain(TEXT_DOMAIN);
195
196 locale = setlocale(LC_CTYPE, NULL);
197 if ((strcmp(locale, "C") == 0) ||
198 (strcmp(locale, "POSIX") == 0)) {
199 isClocale = 1;
200 }
201
202 /* check for non-standard "-" option */
203 for (i = 1; i < argc; i++) {
204 if (strcmp(argv[i], "-") == 0) {
205 aflg++;
206 while (i < argc) {
207 argv[i] = argv[i+1];
208 i++;
209 }
210 argc--;
211 }
212 }
213
214 /* get options */
215 while ((opt = getopt(argc, argv, "1234567890an:N:ot:")) != -1) {
216 switch (opt) {
217 case 'a':
218 aflg++;
219 break;
220
221 case 'n':
222 minlength = (int)strtol(optarg, (char **)NULL,
223 10);
224 break;
225
226 case 'N':
227 if (((sptr = malloc(sizeof (sec_name_t)))
228 == NULL) || ((sptr->name = strdup(optarg))
229 == NULL)) {
230 (void) fprintf(stderr, gettext(
231 "Cannot allocate memory: "
232 "%s\n"), strerror(errno));
233 exit(1);
234 }
235 if (seclistptr == NULL) {
236 seclistptr = sptr;
237 seclistptr->next = NULL;
238 seclistendptr = sptr;
239 } else {
240 seclistendptr->next = sptr;
241 seclistendptr = sptr;
242 }
243 break;
244
245 case 'o':
246 tflg++;
247 t_format = 'd';
248 break;
249
250 case 't':
251 tflg++;
252 t_format = *optarg;
253 if (t_format != 'd' && t_format != 'o' &&
254 t_format != 'x')
255 {
256 (void) fprintf(stderr,
257 gettext("Invalid format\n"));
258 Usage();
259 }
260 break;
261 case '0':
262 case '1':
263 case '2':
264 case '3':
265 case '4':
266 case '5':
267 case '6':
268 case '7':
269 case '8':
270 case '9':
271 minlength *= 10;
272 minlength += opt - '0';
273 break;
274
275 default:
276 Usage();
277 }
278 }
279
280 /* if min string not specified, use default */
281 if (!minlength)
282 minlength = DEF_MIN_STRING;
283
284
285 /* dynamic allocation of char buffer array */
286 buf = (char *)malloc(BUFSIZ);
287 if (buf == NULL) {
288 (void) fprintf(stderr, gettext("Cannot allocate memory: %s\n"),
289 strerror(errno));
290 exit(1);
291 }
292 buf_size = BUFSIZ;
293 tbuf = buf;
294
295
296 /* for each file operand */
297 do {
298 if (argv[optind] != NULL) {
299 if (freopen(argv[optind], "r", stdin) == NULL) {
300 perror(argv[optind]);
301 rc = 1;
302 optind++;
303 continue;
304 }
305 optind++;
306 } else
307 aflg++;
308
309 if (aflg)
310 htype = NOTOUT;
311 else {
312 hsize = fread((char *)&ahdr, sizeof (char),
313 sizeof (ahdr), stdin);
314 htype = ismagic(hsize, &ahdr, stdin);
315 }
316 switch (htype) {
317 case AOUT:
318 (void) fseek(stdin, (long)ADATAPOS(&ahdr), 0);
319 find((long)ahdr.xa_data);
320 continue;
321
322 case ELF:
323 /*
324 * Will take care of COFF M32 and i386 also
325 * As well as ELF M32, i386 and Sparc (32-
326 * and 64-bit)
327 */
328 rc = look_in_sections(argv[optind - 1],
329 seclistptr);
330 continue;
331
332 case NOTOUT:
333 default:
334 if (!aflg)
335 (void) fseek(stdin, (long)0, 0);
336 find(LONG_MAX);
337 continue;
338 }
339 } while (argv[optind] != NULL);
340
341 return (rc);
342 }
343
344 static void
find(cnt)345 find(cnt)
346 long cnt;
347 {
348 int c;
349 int cc;
350 int cr;
351
352 cc = 0;
353 for (c = ~EOF; (cnt > 0) && (c != EOF); cnt--) {
354 c = getc(stdin);
355 if (!(cr = dirt(c, cc))) {
356 if (cc >= minlength) {
357 if (tflg) {
358 switch (t_format) {
359 case 'd':
360 (void) printf("%7ld ",
361 ftell(stdin) - cc - 1);
362 break;
363
364 case 'o':
365 (void) printf("%7lo ",
366 ftell(stdin) - cc - 1);
367 break;
368
369 case 'x':
370 (void) printf("%7lx ",
371 ftell(stdin) - cc - 1);
372 break;
373 }
374 }
375
376 if (cc >= buf_size)
377 buf[buf_size-1] = '\0';
378 else
379 buf[cc] = '\0';
380 (void) puts(buf);
381 }
382 cc = 0;
383 }
384 cc += cr;
385 }
386 }
387
388 static int
dirt(c,cc)389 dirt(c, cc)
390 int c;
391 int cc;
392 {
393 char mbuf[MB_LEN_MAX + 1];
394 int len, len1, i;
395 wchar_t wc;
396 int r_val;
397
398 if (isascii(c)) {
399 if (isprint(c)) {
400 /*
401 * If character count is greater than dynamic
402 * char buffer size, then increase char buffer size.
403 */
404 if (cc >= (buf_size-2)) {
405 if (tbuf != NULL) {
406 buf_size += BUFSIZ;
407 tbuf = (char *)realloc(buf, buf_size);
408 if (tbuf == NULL) {
409 (void) fprintf(stderr,
410 gettext("Cannot allocate memory: %s\n"),
411 strerror(errno));
412 buf_size -= BUFSIZ;
413 rc = 1;
414 return (0);
415 } else {
416 buf = tbuf;
417 }
418 } else {
419 return (0);
420 }
421 }
422 buf[cc] = c;
423 return (1);
424 }
425 return (0);
426 }
427
428 if (isClocale)
429 return (0);
430
431 r_val = 0;
432 mbuf[0] = c;
433 for (len = 1; len < (unsigned int)MB_CUR_MAX; len++) {
434 if ((signed char)
435 (mbuf[len] = getc(stdin)) == -1)
436 break;
437 }
438 mbuf[len] = 0;
439
440 if ((len1 = mbtowc(&wc, mbuf, len)) <= 0) {
441 len1 = 1;
442 goto _unget;
443 }
444
445 if (iswprint(wc)) {
446 if ((cc + len1) >= (buf_size-2)) {
447 if (tbuf != NULL) {
448 buf_size += BUFSIZ;
449 tbuf = (char *)realloc(buf, buf_size);
450 if (tbuf == NULL) {
451 (void) fprintf(stderr,
452 gettext("Cannot allocate memory: %s\n"),
453 strerror(errno));
454 buf_size -= BUFSIZ;
455 rc = 1;
456 return (0);
457 }
458 buf = tbuf;
459 } else {
460 return (0);
461 }
462 }
463 for (i = 0; i < len1; i++, cc++)
464 buf[cc] = mbuf[i];
465 r_val = len1;
466 }
467
468 _unget:
469 for (len--; len >= len1; len--)
470 (void) ungetc(mbuf[len], stdin);
471 return (r_val);
472 }
473
474
475 static int
ismagic(hsize,hdr,fp)476 ismagic(hsize, hdr, fp)
477 int hsize;
478 struct aexec *hdr;
479 FILE *fp;
480 {
481 switch (hdr->xa_magic) {
482 case A_MAGIC1:
483 case A_MAGIC2:
484 case A_MAGIC3:
485 case A_MAGIC4:
486 if (hsize < sizeof (struct aexec))
487 return (NOTOUT);
488 else
489 return (AOUT);
490 default:
491 break;
492 }
493 return (tryelf(fp));
494 }
495
496
497 static int
tryelf(fp)498 tryelf(fp)
499 FILE *fp;
500 {
501 int fd;
502 Elf *elf;
503 GElf_Ehdr ehdr;
504
505 fd = fileno(fp);
506
507 if ((elf_version(EV_CURRENT)) == EV_NONE) {
508 (void) fprintf(stderr, "%s\n", elf_errmsg(-1));
509 return (NOTOUT);
510 }
511
512 (void) lseek(fd, 0L, 0);
513
514 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
515 (void) fprintf(stderr, "%s\n", elf_errmsg(-1));
516 return (NOTOUT);
517 }
518
519 switch (elf_kind(elf)) {
520 case ELF_K_AR:
521 /*
522 * This should try to run strings on each element
523 * of the archive. For now, just search entire
524 * file (-a), as strings has always done
525 * for archives.
526 */
527 case ELF_K_NONE:
528 (void) elf_end(elf);
529 return (NOTOUT);
530 }
531
532 if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *)NULL) {
533 (void) fprintf(stderr, "%s\n", elf_errmsg(-1));
534 (void) elf_end(elf);
535 return (NOTOUT);
536 }
537
538 if ((ehdr.e_type == ET_CORE) || (ehdr.e_type == ET_NONE)) {
539 (void) elf_end(elf);
540 return (NOTOUT);
541 }
542
543 (void) elf_end(elf);
544
545 return (ELF);
546
547 }
548
549
550 static void
Usage()551 Usage()
552 {
553 (void) fprintf(stderr, gettext(
554 "Usage: strings [-a | -] [-t format | -o] [-n number | -number]"
555 "\n\t[-N name] [file]...\n"));
556 exit(1);
557 }
558