1 | //===-- llvm-strings.cpp - Printable String dumping utility ---------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This program is a utility that works like binutils "strings", that is, it |
10 | // prints out printable strings in a binary, objdump, or archive file. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "Opts.inc" |
15 | #include "llvm/ADT/StringExtras.h" |
16 | #include "llvm/Object/Binary.h" |
17 | #include "llvm/Option/Arg.h" |
18 | #include "llvm/Option/ArgList.h" |
19 | #include "llvm/Option/Option.h" |
20 | #include "llvm/Support/CommandLine.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include "llvm/Support/Format.h" |
23 | #include "llvm/Support/InitLLVM.h" |
24 | #include "llvm/Support/MemoryBuffer.h" |
25 | #include "llvm/Support/Program.h" |
26 | #include "llvm/Support/WithColor.h" |
27 | #include <cctype> |
28 | #include <string> |
29 | |
30 | using namespace llvm; |
31 | using namespace llvm::object; |
32 | |
33 | namespace { |
34 | enum ID { |
35 | OPT_INVALID = 0, // This is not an option ID. |
36 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
37 | #include "Opts.inc" |
38 | #undef OPTION |
39 | }; |
40 | |
41 | #define OPTTABLE_STR_TABLE_CODE |
42 | #include "Opts.inc" |
43 | #undef OPTTABLE_STR_TABLE_CODE |
44 | |
45 | #define OPTTABLE_PREFIXES_TABLE_CODE |
46 | #include "Opts.inc" |
47 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
48 | |
49 | using namespace llvm::opt; |
50 | static constexpr opt::OptTable::Info InfoTable[] = { |
51 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
52 | #include "Opts.inc" |
53 | #undef OPTION |
54 | }; |
55 | |
56 | class StringsOptTable : public opt::GenericOptTable { |
57 | public: |
58 | StringsOptTable() |
59 | : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) { |
60 | setGroupedShortOptions(true); |
61 | setDashDashParsing(true); |
62 | } |
63 | }; |
64 | } // namespace |
65 | |
66 | static StringRef ToolName; |
67 | |
68 | static cl::list<std::string> InputFileNames(cl::Positional, |
69 | cl::desc("<input object files>" )); |
70 | |
71 | static int MinLength = 4; |
72 | static bool PrintFileName; |
73 | |
74 | enum radix { none, octal, hexadecimal, decimal }; |
75 | static radix Radix; |
76 | |
77 | [[noreturn]] static void reportCmdLineError(const Twine &Message) { |
78 | WithColor::error(OS&: errs(), Prefix: ToolName) << Message << "\n" ; |
79 | exit(status: 1); |
80 | } |
81 | |
82 | template <typename T> |
83 | static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { |
84 | if (const opt::Arg *A = Args.getLastArg(Ids: ID)) { |
85 | StringRef V(A->getValue()); |
86 | if (!llvm::to_integer(V, Value, 0) || Value <= 0) |
87 | reportCmdLineError(Message: "expected a positive integer, but got '" + V + "'" ); |
88 | } |
89 | } |
90 | |
91 | static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { |
92 | auto print = [&OS, FileName](unsigned Offset, StringRef L) { |
93 | if (L.size() < static_cast<size_t>(MinLength)) |
94 | return; |
95 | if (PrintFileName) |
96 | OS << FileName << ": " ; |
97 | switch (Radix) { |
98 | case none: |
99 | break; |
100 | case octal: |
101 | OS << format(Fmt: "%7o " , Vals: Offset); |
102 | break; |
103 | case hexadecimal: |
104 | OS << format(Fmt: "%7x " , Vals: Offset); |
105 | break; |
106 | case decimal: |
107 | OS << format(Fmt: "%7u " , Vals: Offset); |
108 | break; |
109 | } |
110 | OS << L << '\n'; |
111 | }; |
112 | |
113 | const char *B = Contents.begin(); |
114 | const char *P = nullptr, *E = nullptr, *S = nullptr; |
115 | for (P = Contents.begin(), E = Contents.end(); P < E; ++P) { |
116 | if (isPrint(C: *P) || *P == '\t') { |
117 | if (S == nullptr) |
118 | S = P; |
119 | } else if (S) { |
120 | print(S - B, StringRef(S, P - S)); |
121 | S = nullptr; |
122 | } |
123 | } |
124 | if (S) |
125 | print(S - B, StringRef(S, E - S)); |
126 | } |
127 | |
128 | int main(int argc, char **argv) { |
129 | InitLLVM X(argc, argv); |
130 | BumpPtrAllocator A; |
131 | StringSaver Saver(A); |
132 | StringsOptTable Tbl; |
133 | ToolName = argv[0]; |
134 | opt::InputArgList Args = |
135 | Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, |
136 | ErrorFn: [&](StringRef Msg) { reportCmdLineError(Message: Msg); }); |
137 | if (Args.hasArg(Ids: OPT_help)) { |
138 | Tbl.printHelp( |
139 | OS&: outs(), |
140 | Usage: (Twine(ToolName) + " [options] <input object files>" ).str().c_str(), |
141 | Title: "llvm string dumper" ); |
142 | // TODO Replace this with OptTable API once it adds extrahelp support. |
143 | outs() << "\nPass @FILE as argument to read options from FILE.\n" ; |
144 | return 0; |
145 | } |
146 | if (Args.hasArg(Ids: OPT_version)) { |
147 | outs() << ToolName << '\n'; |
148 | cl::PrintVersionMessage(); |
149 | return 0; |
150 | } |
151 | |
152 | parseIntArg(Args, ID: OPT_bytes_EQ, Value&: MinLength); |
153 | PrintFileName = Args.hasArg(Ids: OPT_print_file_name); |
154 | StringRef R = Args.getLastArgValue(Id: OPT_radix_EQ); |
155 | if (R.empty()) |
156 | Radix = none; |
157 | else if (R == "o" ) |
158 | Radix = octal; |
159 | else if (R == "d" ) |
160 | Radix = decimal; |
161 | else if (R == "x" ) |
162 | Radix = hexadecimal; |
163 | else |
164 | reportCmdLineError(Message: "--radix value should be one of: '' (no offset), 'o' " |
165 | "(octal), 'd' (decimal), 'x' (hexadecimal)" ); |
166 | |
167 | if (MinLength == 0) { |
168 | errs() << "invalid minimum string length 0\n" ; |
169 | return EXIT_FAILURE; |
170 | } |
171 | |
172 | std::vector<std::string> InputFileNames = Args.getAllArgValues(Id: OPT_INPUT); |
173 | if (InputFileNames.empty()) |
174 | InputFileNames.push_back(x: "-" ); |
175 | |
176 | for (const auto &File : InputFileNames) { |
177 | ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = |
178 | MemoryBuffer::getFileOrSTDIN(Filename: File, /*IsText=*/true); |
179 | if (std::error_code EC = Buffer.getError()) |
180 | errs() << File << ": " << EC.message() << '\n'; |
181 | else |
182 | strings(OS&: llvm::outs(), FileName: File == "-" ? "{standard input}" : File, |
183 | Contents: Buffer.get()->getMemBufferRef().getBuffer()); |
184 | } |
185 | |
186 | return EXIT_SUCCESS; |
187 | } |
188 | |