2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 /* $OpenBSD: strings.c,v 1.2 1996/06/26 05:39:30 deraadt Exp $ */
24 /* $NetBSD: strings.c,v 1.7 1995/02/15 15:49:19 jtc Exp $ */
27 * Copyright (c) 1980, 1987, 1993
28 * The Regents of the University of California. All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * The NeXT Computer, Inc. strings(1) program that handles fat files, archives
60 * and Mach-O objects files (no BSD a.out files). Some lines of code were
61 * taken and adapted from the BSD release.
63 * CHANGES FROM THE BSD VERSION OF strings(1):
64 * Object files are no longer recognized as objects if read from standard input
65 * but must be a command line argument to be treated as an object file. The
66 * result is if an object is read from standard input the entire file is
67 * searched for strings not just the appropate sections. With the handling of
68 * fat files any file that contains objects has it contents processed as objects
69 * which includes the objects in archives which the 4.3bsd strings(1) did not
70 * process as object files. Object files may be of the form "libx.a(x.o)"
71 * which refer to an archive member.
77 #include <sys/types.h>
79 #include "stuff/bool.h"
80 #include "stuff/ofile.h"
81 #include "stuff/errors.h"
82 #include "stuff/allocate.h"
84 char *progname
= NULL
;
87 enum bool treat_as_data
;
88 enum bool print_offsets
;
90 enum bool all_sections
;
91 uint32_t minimum_length
;
96 static void ofile_processor(
100 static void ofile_find(
104 struct flags
*flags
);
107 struct flags
*flags
);
108 static enum bool dirt(
111 /* apple_version is created by the libstuff/Makefile */
112 extern char apple_version
[];
113 char *version
= apple_version
;
115 /* likewise xtools_version and support_url. */
116 extern char xtools_version
[];
117 extern char package_version
[];
118 extern char support_url
[];
130 struct arch_flag
*arch_flags
;
131 uint32_t narch_flags
;
132 enum bool all_archs
, rest_args_files
, use_member_syntax
;
133 struct stat stat_buf
;
136 pnam
= strrchr(progname
, '/');
137 pnam
= (pnam
)?pnam
+1:progname
;
144 flags
.treat_as_data
= FALSE
;
145 flags
.print_offsets
= FALSE
;
146 flags
.offset_format
= NULL
;
147 flags
.all_sections
= FALSE
;
148 flags
.minimum_length
= 4;
150 rest_args_files
= FALSE
;
151 for(i
= 1; i
< argc
; i
++){
152 if(strcmp(argv
[i
], "--version") == 0){
153 /* Implement a gnu-style --version. */
154 fprintf(stdout
, "xtools-%s %s %s\nBased on Apple Inc. %s\n",
155 xtools_version
, pnam
, package_version
, apple_version
);
157 } else if(strcmp(argv
[i
], "--help") == 0){
158 fprintf(stdout
, "Usage: %s [-] [-a] [-o] [-t format] [-number] "
159 "[-n number] [[-arch <arch_flag>] ...] [--] [file ...]\n",
161 fprintf(stdout
, "Please report bugs to %s\n", support_url
);
164 if(rest_args_files
== FALSE
&& argv
[i
][0] == '-'){
165 if(argv
[i
][1] == '\0')
166 flags
.treat_as_data
= TRUE
;
167 else if(strcmp(argv
[i
], "--") == 0)
168 rest_args_files
= TRUE
;
169 else if(strcmp(argv
[i
], "-arch") == 0){
171 error("missing argument(s) to %s option", argv
[i
]);
174 if(strcmp("all", argv
[i
+1]) == 0){
178 arch_flags
= reallocate(arch_flags
,
179 (narch_flags
+ 1) * sizeof(struct arch_flag
));
180 if(get_arch_from_flag(argv
[i
+1],
181 arch_flags
+ narch_flags
) == 0){
182 error("unknown architecture specification flag: "
183 "%s %s", argv
[i
], argv
[i
+1]);
191 else if(strcmp(argv
[i
], "-n") == 0){
193 error("missing argument to %s option", argv
[i
]);
196 flags
.minimum_length
= strtoul(argv
[i
+1], &endp
, 10);
198 error("invalid decimal number in option: %s %s",
204 else if(strcmp(argv
[i
], "-t") == 0){
206 error("missing argument to %s option", argv
[i
]);
209 if(argv
[i
+1][1] != '\0'){
210 error("invalid argument to option: %s %s",
214 switch(argv
[i
+1][0]){
216 flags
.print_offsets
= TRUE
;
217 flags
.offset_format
= "%d";
220 flags
.print_offsets
= TRUE
;
221 flags
.offset_format
= "%o";
224 flags
.print_offsets
= TRUE
;
225 flags
.offset_format
= "%x";
228 error("invalid argument to option: %s %s",
236 for(j
= 1; argv
[i
][j
] != '\0' && endp
== NULL
; j
++){
239 flags
.print_offsets
= TRUE
;
240 flags
.offset_format
= "%7lu";
243 flags
.all_sections
= TRUE
;
246 if(!isdigit(argv
[i
][j
])){
247 error("unknown flag: %s", argv
[i
]);
250 flags
.minimum_length
= strtoul(argv
[i
]+j
,&endp
,10);
252 error("invalid decimal number in flag: %s",
266 * Process the file or stdin if there are no files.
268 rest_args_files
= FALSE
;
270 for(i
= 1; i
< argc
; i
++){
271 if(argv
[i
][0] != '-' || rest_args_files
== TRUE
){
272 if(flags
.treat_as_data
== TRUE
){
273 if(freopen(argv
[i
], "r", stdin
) == NULL
)
274 system_error("can't open: %s", argv
[i
]);
276 find(UINT_MAX
, &flags
);
280 * If there's a filename that's an exact match then use
281 * that, else fall back to the member syntax.
283 if(stat(argv
[i
], &stat_buf
) == 0)
284 use_member_syntax
= FALSE
;
286 use_member_syntax
= TRUE
;
287 ofile_process(argv
[i
], arch_flags
, narch_flags
,
288 all_archs
, TRUE
, TRUE
, use_member_syntax
,
289 ofile_processor
,&flags
);
292 else if(strcmp(argv
[i
], "-arch") == 0 ||
293 strcmp(argv
[i
], "-n") == 0 ||
294 strcmp(argv
[i
], "-t") == 0)
296 else if(strcmp(argv
[i
], "--") == 0)
297 rest_args_files
= TRUE
;
301 find(UINT_MAX
, &flags
);
304 return(EXIT_SUCCESS
);
306 return(EXIT_FAILURE
);
310 * usage() prints the current usage message and exits indicating failure.
317 fprintf(stderr
, "Usage: %s [-] [-a] [-o] [-t format] [-number] "
318 "[-n number] [[-arch <arch_flag>] ...] [--] [file ...]\n",
324 * ofile_processor() is called by ofile_process() for each ofile to process.
325 * All ofiles that are object files are process by section non-object files
326 * have their logical contents processed entirely. The locical contents may
327 * be a single archive member in an archive, if the name "libx.a(x.o)" was
328 * used or a specific architecture if "-arch <arch_flag> fatfile" was used
329 * which is not the entire physical file. If the entire physical file is
330 * wanted to be searched then the "-" option is used and this routine is not
341 uint64_t offset
, size
;
345 struct load_command
*lc
;
346 struct segment_command
*sg
;
347 struct segment_command_64
*sg64
;
349 struct section_64
*s64
;
351 flags
= (struct flags
*)cookie
;
354 * If the ofile is not an object file then process it without reguard
357 if(ofile
->object_addr
== NULL
|| ofile
->member_type
== OFILE_LLVM_BITCODE
){
358 if(ofile
->file_type
== OFILE_FAT
&& ofile
->arch_flag
.cputype
!= 0){
359 if(ofile
->fat_header
->magic
== FAT_MAGIC_64
){
360 addr
= ofile
->file_addr
+
361 ofile
->fat_archs64
[ofile
->narch
].offset
;
362 size
= ofile
->fat_archs64
[ofile
->narch
].size
;
363 offset
= ofile
->fat_archs64
[ofile
->narch
].offset
;
366 addr
= ofile
->file_addr
+
367 ofile
->fat_archs
[ofile
->narch
].offset
;
368 size
= ofile
->fat_archs
[ofile
->narch
].size
;
369 offset
= ofile
->fat_archs
[ofile
->narch
].offset
;
373 addr
= ofile
->file_addr
;
374 size
= ofile
->file_size
;
377 if(ofile
->member_ar_hdr
!= NULL
) {
378 addr
= addr
+ ofile
->member_offset
;
379 size
= strtoul(ofile
->member_ar_hdr
->ar_size
, NULL
, 10);
380 offset
= offset
+ ofile
->member_offset
;
382 if(offset
>= ofile
->file_size
)
384 else if(offset
+ size
> ofile
->file_size
)
385 size
= ofile
->file_size
- offset
;
386 ofile_find(addr
, size
, offset
, flags
);
391 * The ofile is an object file so process with reguard to it's sections.
393 lc
= ofile
->load_commands
;
394 if(ofile
->mh
!= NULL
)
395 ncmds
= ofile
->mh
->ncmds
;
397 ncmds
= ofile
->mh64
->ncmds
;
398 for(i
= 0; i
< ncmds
; i
++){
399 if(lc
->cmd
== LC_SEGMENT
){
400 sg
= (struct segment_command
*)lc
;
401 s
= (struct section
*)((char *)sg
+
402 sizeof(struct segment_command
));
403 for(j
= 0; j
< sg
->nsects
; j
++){
404 if(flags
->all_sections
){
405 if((s
->flags
& S_ZEROFILL
) != S_ZEROFILL
&&
406 (s
->flags
& S_THREAD_LOCAL_ZEROFILL
) !=
407 S_THREAD_LOCAL_ZEROFILL
){
408 addr
= ofile
->object_addr
+ s
->offset
;
411 if(offset
>= ofile
->file_size
)
413 else if(offset
+ size
> ofile
->file_size
)
414 size
= ofile
->file_size
- offset
;
415 ofile_find(addr
, size
, offset
, flags
);
419 if((s
->flags
& S_ZEROFILL
) != S_ZEROFILL
&&
420 (s
->flags
& S_THREAD_LOCAL_ZEROFILL
) !=
421 S_THREAD_LOCAL_ZEROFILL
&&
422 (strcmp(s
->sectname
, SECT_TEXT
) != 0 ||
423 strcmp(s
->segname
, SEG_TEXT
) != 0)){
424 addr
= ofile
->object_addr
+ s
->offset
;
427 if(offset
>= ofile
->file_size
)
429 else if(offset
+ size
> ofile
->file_size
)
430 size
= ofile
->file_size
- offset
;
431 ofile_find(addr
, size
, offset
, flags
);
437 else if(lc
->cmd
== LC_SEGMENT_64
){
438 sg64
= (struct segment_command_64
*)lc
;
439 s64
= (struct section_64
*)((char *)sg64
+
440 sizeof(struct segment_command_64
));
441 for(j
= 0; j
< sg64
->nsects
; j
++){
442 if(flags
->all_sections
){
443 if((s64
->flags
& S_ZEROFILL
) != S_ZEROFILL
&&
444 (s64
->flags
& S_THREAD_LOCAL_ZEROFILL
) !=
445 S_THREAD_LOCAL_ZEROFILL
){
446 addr
= ofile
->object_addr
+ s64
->offset
;
447 offset
= s64
->offset
;
449 if(offset
>= ofile
->file_size
)
451 else if(offset
+ size
> ofile
->file_size
)
452 size
= ofile
->file_size
- offset
;
453 ofile_find(addr
, size
, offset
, flags
);
457 if((s64
->flags
& S_ZEROFILL
) != S_ZEROFILL
&&
458 (s64
->flags
& S_THREAD_LOCAL_ZEROFILL
) !=
459 S_THREAD_LOCAL_ZEROFILL
&&
460 (strcmp(s64
->sectname
, SECT_TEXT
) != 0 ||
461 strcmp(s64
->segname
, SEG_TEXT
) != 0)){
462 addr
= ofile
->object_addr
+ s64
->offset
;
463 offset
= s64
->offset
;
465 if(offset
>= ofile
->file_size
)
467 else if(offset
+ size
> ofile
->file_size
)
468 size
= ofile
->file_size
- offset
;
469 ofile_find(addr
, size
, offset
, flags
);
475 lc
= (struct load_command
*)((char *)lc
+ lc
->cmdsize
);
480 * ofile_find is used by ofile_processor() to find strings in part of a ofile
481 * that is memory at addr for size. offset is the offset in the file to this
482 * data for use when printing offsets.
492 uint32_t i
, string_length
;
497 for(i
= 0; i
< size
; i
++){
499 if(c
== '\n' || dirt(c
) || i
== size
- 1){
500 if(string_length
>= flags
->minimum_length
){
501 if(flags
->print_offsets
){
502 printf(flags
->offset_format
, offset
+ (string
- addr
));
505 if(i
== size
- 1 && c
!= '\n')
506 printf("%.*s\n", (int)string_length
+ 1, string
);
508 printf("%.*s\n", (int)string_length
, string
);
510 string
= addr
+ i
+ 1;
520 * find() is the original 4.3bsd code that uses the stdin stream. It searches
521 * for strings through a count of cnt bytes.
529 static char buf
[BUFSIZ
];
534 for (; cnt
!= 0; cnt
--) {
536 if (c
== '\n' || dirt(c
) || cnt
== 0) {
537 if (cp
> buf
&& cp
[-1] == '\n')
540 if (cp
> &buf
[flags
->minimum_length
]) {
541 if (flags
->print_offsets
== TRUE
){
542 printf(flags
->offset_format
,
543 ftell(stdin
) - cc
- 1);
550 if (cp
< &buf
[sizeof buf
- 2])
554 if (ferror(stdin
) || feof(stdin
))
560 * dirt() is the original 4.3bsd code that returns TRUE or FALSE if the
561 * character passed to it could be in a part of a printable string.
575 if(c
> 0200 || c
< ' ')