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