xref: /illumos-gate/usr/src/cmd/strings/strings.c (revision 2b6389efff8da018d426952e20428e7e60d568a3)
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
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
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
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
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
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
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
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