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
64struct aexec ahdr;
65
66/* used to maintain a list of program sections to look in */
67typedef struct sec_name {
68	char	*name;
69	struct	sec_name *next;
70} sec_name_t;
71
72/*
73 * function prototypes
74 */
75static void	Usage();
76static void	find(long);
77static int	ismagic(int, struct aexec *, FILE *);
78static int	tryelf(FILE *);
79static 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
94static	int	tflg;
95static	char	t_format;
96static	int	aflg;
97static	int	minlength = 0;
98static	int	isClocale = 0;
99static	char    *buf = NULL;
100static	char	*tbuf = NULL;
101static	size_t	buf_size = 0;
102static	int	rc = 0; /* exit code */
103
104/*
105 * Returns 0 when sections have been successfully looked through,
106 * otherwise returns 1.
107 */
108static int
109look_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
175int
176main(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
344static void
345find(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
388static int
389dirt(c, cc)
390int	c;
391int	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
475static int
476ismagic(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
497static int
498tryelf(fp)
499FILE *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
550static void
551Usage()
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