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