1030ff0f2SSaleem Abdulrasool //===-- llvm-strings.cpp - Printable String dumping utility ---------------===//
2030ff0f2SSaleem Abdulrasool //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6030ff0f2SSaleem Abdulrasool //
7030ff0f2SSaleem Abdulrasool //===----------------------------------------------------------------------===//
8030ff0f2SSaleem Abdulrasool //
9030ff0f2SSaleem Abdulrasool // This program is a utility that works like binutils "strings", that is, it
10030ff0f2SSaleem Abdulrasool // prints out printable strings in a binary, objdump, or archive file.
11030ff0f2SSaleem Abdulrasool //
12030ff0f2SSaleem Abdulrasool //===----------------------------------------------------------------------===//
13030ff0f2SSaleem Abdulrasool 
1498f07832SFangrui Song #include "Opts.inc"
15030ff0f2SSaleem Abdulrasool #include "llvm/Object/Binary.h"
1698f07832SFangrui Song #include "llvm/Option/Arg.h"
1798f07832SFangrui Song #include "llvm/Option/ArgList.h"
1898f07832SFangrui Song #include "llvm/Option/Option.h"
19030ff0f2SSaleem Abdulrasool #include "llvm/Support/CommandLine.h"
20030ff0f2SSaleem Abdulrasool #include "llvm/Support/Error.h"
21ba048618SSaleem Abdulrasool #include "llvm/Support/Format.h"
22197194b6SRui Ueyama #include "llvm/Support/InitLLVM.h"
23030ff0f2SSaleem Abdulrasool #include "llvm/Support/MemoryBuffer.h"
24030ff0f2SSaleem Abdulrasool #include "llvm/Support/Program.h"
2598f07832SFangrui Song #include "llvm/Support/WithColor.h"
262dcea63bSSaleem Abdulrasool #include <cctype>
27030ff0f2SSaleem Abdulrasool #include <string>
28030ff0f2SSaleem Abdulrasool 
29030ff0f2SSaleem Abdulrasool using namespace llvm;
30030ff0f2SSaleem Abdulrasool using namespace llvm::object;
31030ff0f2SSaleem Abdulrasool 
3298f07832SFangrui Song namespace {
3398f07832SFangrui Song enum ID {
3498f07832SFangrui Song   OPT_INVALID = 0, // This is not an option ID.
3598f07832SFangrui Song #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
3698f07832SFangrui Song                HELPTEXT, METAVAR, VALUES)                                      \
3798f07832SFangrui Song   OPT_##ID,
3898f07832SFangrui Song #include "Opts.inc"
3998f07832SFangrui Song #undef OPTION
4098f07832SFangrui Song };
4198f07832SFangrui Song 
4298f07832SFangrui Song #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
4398f07832SFangrui Song #include "Opts.inc"
4498f07832SFangrui Song #undef PREFIX
4598f07832SFangrui Song 
468189c4eeSFangrui Song const opt::OptTable::Info InfoTable[] = {
4798f07832SFangrui Song #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
4898f07832SFangrui Song                HELPTEXT, METAVAR, VALUES)                                      \
4998f07832SFangrui Song   {                                                                            \
5098f07832SFangrui Song       PREFIX,      NAME,      HELPTEXT,                                        \
5198f07832SFangrui Song       METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
5298f07832SFangrui Song       PARAM,       FLAGS,     OPT_##GROUP,                                     \
5398f07832SFangrui Song       OPT_##ALIAS, ALIASARGS, VALUES},
5498f07832SFangrui Song #include "Opts.inc"
5598f07832SFangrui Song #undef OPTION
5698f07832SFangrui Song };
5798f07832SFangrui Song 
5898f07832SFangrui Song class StringsOptTable : public opt::OptTable {
5998f07832SFangrui Song public:
StringsOptTable()6098f07832SFangrui Song   StringsOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
6198f07832SFangrui Song };
6298f07832SFangrui Song } // namespace
6398f07832SFangrui Song 
64b30a18f4Sgbreynoo static StringRef ToolName;
6598f07832SFangrui Song 
66030ff0f2SSaleem Abdulrasool static cl::list<std::string> InputFileNames(cl::Positional,
67*95a13425SFangrui Song                                             cl::desc("<input object files>"));
68030ff0f2SSaleem Abdulrasool 
6998f07832SFangrui Song static int MinLength = 4;
7098f07832SFangrui Song static bool PrintFileName;
716835cac2SMartin Storsjo 
72ba048618SSaleem Abdulrasool enum radix { none, octal, hexadecimal, decimal };
7398f07832SFangrui Song static radix Radix;
74ba048618SSaleem Abdulrasool 
reportCmdLineError(const Twine & Message)756da3d8b1SFangrui Song [[noreturn]] static void reportCmdLineError(const Twine &Message) {
7698f07832SFangrui Song   WithColor::error(errs(), ToolName) << Message << "\n";
7798f07832SFangrui Song   exit(1);
7898f07832SFangrui Song }
7998f07832SFangrui Song 
8098f07832SFangrui Song template <typename T>
parseIntArg(const opt::InputArgList & Args,int ID,T & Value)8198f07832SFangrui Song static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
8298f07832SFangrui Song   if (const opt::Arg *A = Args.getLastArg(ID)) {
8398f07832SFangrui Song     StringRef V(A->getValue());
8498f07832SFangrui Song     if (!llvm::to_integer(V, Value, 0) || Value <= 0)
8598f07832SFangrui Song       reportCmdLineError("expected a positive integer, but got '" + V + "'");
8698f07832SFangrui Song   }
8798f07832SFangrui Song }
889485b265SJames Henderson 
strings(raw_ostream & OS,StringRef FileName,StringRef Contents)89f10a8714SSaleem Abdulrasool static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
90ba048618SSaleem Abdulrasool   auto print = [&OS, FileName](unsigned Offset, StringRef L) {
91f7009b42SSaleem Abdulrasool     if (L.size() < static_cast<size_t>(MinLength))
92f7009b42SSaleem Abdulrasool       return;
93f10a8714SSaleem Abdulrasool     if (PrintFileName)
94f10a8714SSaleem Abdulrasool       OS << FileName << ": ";
95ba048618SSaleem Abdulrasool     switch (Radix) {
96ba048618SSaleem Abdulrasool     case none:
97ba048618SSaleem Abdulrasool       break;
98ba048618SSaleem Abdulrasool     case octal:
99dcf1f8e7SJordan Rupprecht       OS << format("%7o ", Offset);
100ba048618SSaleem Abdulrasool       break;
101ba048618SSaleem Abdulrasool     case hexadecimal:
102dcf1f8e7SJordan Rupprecht       OS << format("%7x ", Offset);
103ba048618SSaleem Abdulrasool       break;
104ba048618SSaleem Abdulrasool     case decimal:
105dcf1f8e7SJordan Rupprecht       OS << format("%7u ", Offset);
106ba048618SSaleem Abdulrasool       break;
107ba048618SSaleem Abdulrasool     }
108dcf1f8e7SJordan Rupprecht     OS << L << '\n';
109f10a8714SSaleem Abdulrasool   };
110f10a8714SSaleem Abdulrasool 
111ba048618SSaleem Abdulrasool   const char *B = Contents.begin();
1122729786fSSaleem Abdulrasool   const char *P = nullptr, *E = nullptr, *S = nullptr;
1132729786fSSaleem Abdulrasool   for (P = Contents.begin(), E = Contents.end(); P < E; ++P) {
1145b2e9682SJames Henderson     if (isPrint(*P) || *P == '\t') {
115030ff0f2SSaleem Abdulrasool       if (S == nullptr)
116030ff0f2SSaleem Abdulrasool         S = P;
117030ff0f2SSaleem Abdulrasool     } else if (S) {
118ba048618SSaleem Abdulrasool       print(S - B, StringRef(S, P - S));
119030ff0f2SSaleem Abdulrasool       S = nullptr;
120030ff0f2SSaleem Abdulrasool     }
121030ff0f2SSaleem Abdulrasool   }
122f7009b42SSaleem Abdulrasool   if (S)
123ba048618SSaleem Abdulrasool     print(S - B, StringRef(S, E - S));
124030ff0f2SSaleem Abdulrasool }
125030ff0f2SSaleem Abdulrasool 
main(int argc,char ** argv)126030ff0f2SSaleem Abdulrasool int main(int argc, char **argv) {
127197194b6SRui Ueyama   InitLLVM X(argc, argv);
12898f07832SFangrui Song   BumpPtrAllocator A;
12998f07832SFangrui Song   StringSaver Saver(A);
13098f07832SFangrui Song   StringsOptTable Tbl;
131b30a18f4Sgbreynoo   ToolName = argv[0];
13298f07832SFangrui Song   opt::InputArgList Args =
13398f07832SFangrui Song       Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
13498f07832SFangrui Song                     [&](StringRef Msg) { reportCmdLineError(Msg); });
13598f07832SFangrui Song   if (Args.hasArg(OPT_help)) {
13698f07832SFangrui Song     Tbl.printHelp(
13798f07832SFangrui Song         outs(),
13898f07832SFangrui Song         (Twine(ToolName) + " [options] <input object files>").str().c_str(),
13998f07832SFangrui Song         "llvm string dumper");
14098f07832SFangrui Song     // TODO Replace this with OptTable API once it adds extrahelp support.
14198f07832SFangrui Song     outs() << "\nPass @FILE as argument to read options from FILE.\n";
14298f07832SFangrui Song     return 0;
14398f07832SFangrui Song   }
14498f07832SFangrui Song   if (Args.hasArg(OPT_version)) {
14598f07832SFangrui Song     outs() << ToolName << '\n';
14698f07832SFangrui Song     cl::PrintVersionMessage();
14798f07832SFangrui Song     return 0;
14898f07832SFangrui Song   }
149030ff0f2SSaleem Abdulrasool 
15098f07832SFangrui Song   parseIntArg(Args, OPT_bytes_EQ, MinLength);
15198f07832SFangrui Song   PrintFileName = Args.hasArg(OPT_print_file_name);
15298f07832SFangrui Song   StringRef R = Args.getLastArgValue(OPT_radix_EQ);
15398f07832SFangrui Song   if (R.empty())
15498f07832SFangrui Song     Radix = none;
15598f07832SFangrui Song   else if (R == "o")
15698f07832SFangrui Song     Radix = octal;
15798f07832SFangrui Song   else if (R == "d")
15898f07832SFangrui Song     Radix = decimal;
15998f07832SFangrui Song   else if (R == "x")
16098f07832SFangrui Song     Radix = hexadecimal;
16198f07832SFangrui Song   else
16298f07832SFangrui Song     reportCmdLineError("--radix value should be one of: '' (no offset), 'o' "
16398f07832SFangrui Song                        "(octal), 'd' (decimal), 'x' (hexadecimal)");
16498f07832SFangrui Song 
165f7009b42SSaleem Abdulrasool   if (MinLength == 0) {
166f7009b42SSaleem Abdulrasool     errs() << "invalid minimum string length 0\n";
167f7009b42SSaleem Abdulrasool     return EXIT_FAILURE;
168f7009b42SSaleem Abdulrasool   }
169030ff0f2SSaleem Abdulrasool 
17098f07832SFangrui Song   std::vector<std::string> InputFileNames = Args.getAllArgValues(OPT_INPUT);
171030ff0f2SSaleem Abdulrasool   if (InputFileNames.empty())
172030ff0f2SSaleem Abdulrasool     InputFileNames.push_back("-");
173030ff0f2SSaleem Abdulrasool 
174be3a2919SSaleem Abdulrasool   for (const auto &File : InputFileNames) {
175be3a2919SSaleem Abdulrasool     ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
176be3a2919SSaleem Abdulrasool         MemoryBuffer::getFileOrSTDIN(File);
177be3a2919SSaleem Abdulrasool     if (std::error_code EC = Buffer.getError())
178be3a2919SSaleem Abdulrasool       errs() << File << ": " << EC.message() << '\n';
179be3a2919SSaleem Abdulrasool     else
180f10a8714SSaleem Abdulrasool       strings(llvm::outs(), File == "-" ? "{standard input}" : File,
181f10a8714SSaleem Abdulrasool               Buffer.get()->getMemBufferRef().getBuffer());
182030ff0f2SSaleem Abdulrasool   }
183030ff0f2SSaleem Abdulrasool 
184be3a2919SSaleem Abdulrasool   return EXIT_SUCCESS;
185be3a2919SSaleem Abdulrasool }
186