xref: /llvm-project/llvm/tools/llvm-strings/llvm-strings.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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(errs(), ToolName) << Message << "\n";
79   exit(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(ID)) {
85     StringRef V(A->getValue());
86     if (!llvm::to_integer(V, Value, 0) || Value <= 0)
87       reportCmdLineError("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("%7o ", Offset);
102       break;
103     case hexadecimal:
104       OS << format("%7x ", Offset);
105       break;
106     case decimal:
107       OS << format("%7u ", 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(*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, argv, OPT_UNKNOWN, Saver,
136                     [&](StringRef Msg) { reportCmdLineError(Msg); });
137   if (Args.hasArg(OPT_help)) {
138     Tbl.printHelp(
139         outs(),
140         (Twine(ToolName) + " [options] <input object files>").str().c_str(),
141         "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(OPT_version)) {
147     outs() << ToolName << '\n';
148     cl::PrintVersionMessage();
149     return 0;
150   }
151 
152   parseIntArg(Args, OPT_bytes_EQ, MinLength);
153   PrintFileName = Args.hasArg(OPT_print_file_name);
154   StringRef R = Args.getLastArgValue(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("--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(OPT_INPUT);
173   if (InputFileNames.empty())
174     InputFileNames.push_back("-");
175 
176   for (const auto &File : InputFileNames) {
177     ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
178         MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/true);
179     if (std::error_code EC = Buffer.getError())
180       errs() << File << ": " << EC.message() << '\n';
181     else
182       strings(llvm::outs(), File == "-" ? "{standard input}" : File,
183               Buffer.get()->getMemBufferRef().getBuffer());
184   }
185 
186   return EXIT_SUCCESS;
187 }
188