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