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 PREFIX(NAME, VALUE) \ |
42 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
43 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
44 | std::size(NAME##_init) - 1); |
45 | #include "Opts.inc" |
46 | #undef PREFIX |
47 | |
48 | using namespace llvm::opt; |
49 | static constexpr opt::OptTable::Info InfoTable[] = { |
50 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
51 | #include "Opts.inc" |
52 | #undef OPTION |
53 | }; |
54 | |
55 | class StringsOptTable : public opt::GenericOptTable { |
56 | public: |
57 | StringsOptTable() : GenericOptTable(InfoTable) { |
58 | setGroupedShortOptions(true); |
59 | setDashDashParsing(true); |
60 | } |
61 | }; |
62 | } // namespace |
63 | |
64 | static StringRef ToolName; |
65 | |
66 | static cl::list<std::string> InputFileNames(cl::Positional, |
67 | cl::desc("<input object files>" )); |
68 | |
69 | static int MinLength = 4; |
70 | static bool PrintFileName; |
71 | |
72 | enum radix { none, octal, hexadecimal, decimal }; |
73 | static radix Radix; |
74 | |
75 | [[noreturn]] static void reportCmdLineError(const Twine &Message) { |
76 | WithColor::error(OS&: errs(), Prefix: ToolName) << Message << "\n" ; |
77 | exit(status: 1); |
78 | } |
79 | |
80 | template <typename T> |
81 | static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { |
82 | if (const opt::Arg *A = Args.getLastArg(Ids: ID)) { |
83 | StringRef V(A->getValue()); |
84 | if (!llvm::to_integer(V, Value, 0) || Value <= 0) |
85 | reportCmdLineError(Message: "expected a positive integer, but got '" + V + "'" ); |
86 | } |
87 | } |
88 | |
89 | static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { |
90 | auto print = [&OS, FileName](unsigned Offset, StringRef L) { |
91 | if (L.size() < static_cast<size_t>(MinLength)) |
92 | return; |
93 | if (PrintFileName) |
94 | OS << FileName << ": " ; |
95 | switch (Radix) { |
96 | case none: |
97 | break; |
98 | case octal: |
99 | OS << format(Fmt: "%7o " , Vals: Offset); |
100 | break; |
101 | case hexadecimal: |
102 | OS << format(Fmt: "%7x " , Vals: Offset); |
103 | break; |
104 | case decimal: |
105 | OS << format(Fmt: "%7u " , Vals: Offset); |
106 | break; |
107 | } |
108 | OS << L << '\n'; |
109 | }; |
110 | |
111 | const char *B = Contents.begin(); |
112 | const char *P = nullptr, *E = nullptr, *S = nullptr; |
113 | for (P = Contents.begin(), E = Contents.end(); P < E; ++P) { |
114 | if (isPrint(C: *P) || *P == '\t') { |
115 | if (S == nullptr) |
116 | S = P; |
117 | } else if (S) { |
118 | print(S - B, StringRef(S, P - S)); |
119 | S = nullptr; |
120 | } |
121 | } |
122 | if (S) |
123 | print(S - B, StringRef(S, E - S)); |
124 | } |
125 | |
126 | int main(int argc, char **argv) { |
127 | InitLLVM X(argc, argv); |
128 | BumpPtrAllocator A; |
129 | StringSaver Saver(A); |
130 | StringsOptTable Tbl; |
131 | ToolName = argv[0]; |
132 | opt::InputArgList Args = |
133 | Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, |
134 | ErrorFn: [&](StringRef Msg) { reportCmdLineError(Message: Msg); }); |
135 | if (Args.hasArg(Ids: OPT_help)) { |
136 | Tbl.printHelp( |
137 | OS&: outs(), |
138 | Usage: (Twine(ToolName) + " [options] <input object files>" ).str().c_str(), |
139 | Title: "llvm string dumper" ); |
140 | // TODO Replace this with OptTable API once it adds extrahelp support. |
141 | outs() << "\nPass @FILE as argument to read options from FILE.\n" ; |
142 | return 0; |
143 | } |
144 | if (Args.hasArg(Ids: OPT_version)) { |
145 | outs() << ToolName << '\n'; |
146 | cl::PrintVersionMessage(); |
147 | return 0; |
148 | } |
149 | |
150 | parseIntArg(Args, ID: OPT_bytes_EQ, Value&: MinLength); |
151 | PrintFileName = Args.hasArg(Ids: OPT_print_file_name); |
152 | StringRef R = Args.getLastArgValue(Id: OPT_radix_EQ); |
153 | if (R.empty()) |
154 | Radix = none; |
155 | else if (R == "o" ) |
156 | Radix = octal; |
157 | else if (R == "d" ) |
158 | Radix = decimal; |
159 | else if (R == "x" ) |
160 | Radix = hexadecimal; |
161 | else |
162 | reportCmdLineError(Message: "--radix value should be one of: '' (no offset), 'o' " |
163 | "(octal), 'd' (decimal), 'x' (hexadecimal)" ); |
164 | |
165 | if (MinLength == 0) { |
166 | errs() << "invalid minimum string length 0\n" ; |
167 | return EXIT_FAILURE; |
168 | } |
169 | |
170 | std::vector<std::string> InputFileNames = Args.getAllArgValues(Id: OPT_INPUT); |
171 | if (InputFileNames.empty()) |
172 | InputFileNames.push_back(x: "-" ); |
173 | |
174 | for (const auto &File : InputFileNames) { |
175 | ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = |
176 | MemoryBuffer::getFileOrSTDIN(Filename: File); |
177 | if (std::error_code EC = Buffer.getError()) |
178 | errs() << File << ": " << EC.message() << '\n'; |
179 | else |
180 | strings(OS&: llvm::outs(), FileName: File == "-" ? "{standard input}" : File, |
181 | Contents: Buffer.get()->getMemBufferRef().getBuffer()); |
182 | } |
183 | |
184 | return EXIT_SUCCESS; |
185 | } |
186 | |