dexdump: escape inlined string values in output
Escapes control characters, double-quotes, and backslash.
Fix: 219965275
Test: art/test/dexdump/run-all-tests
Change-Id: I05f98f54d2e89f39482d102b521f1394bd5dcce2
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 409ae55..d80809a 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -41,6 +41,7 @@
#include <iomanip>
#include <memory>
#include <sstream>
+#include <string_view>
#include <vector>
#include "android-base/file.h"
@@ -360,32 +361,92 @@
*out = '\0';
}
+/* clang-format off */
+constexpr char kEscapedLength[256] = {
+ 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 4, 2, 2, 4, 4, // \a, \b, \t, \n, \r
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // ",
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // '0'..'9'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'..'O'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, // 'P'..'Z', '\'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'..'o'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, // 'p'..'z', DEL
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Unicode range, keep
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+/* clang-format on */
+
+/*
+ * Check if a UTF8 string contains characters we should quote.
+ */
+static bool needsEscape(std::string_view s) {
+ for (unsigned char c : s) {
+ if (kEscapedLength[c] != 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string escapeString(std::string_view s) {
+ std::ostringstream oss;
+ for (unsigned char c : s) {
+ switch (kEscapedLength[c]) {
+ case 1:
+ oss << static_cast<char>(c);
+ break;
+ case 2:
+ switch (c) {
+ case '\b':
+ oss << '\\' << 'b';
+ break;
+ case '\f':
+ oss << '\\' << 'f';
+ break;
+ case '\n':
+ oss << '\\' << 'n';
+ break;
+ case '\r':
+ oss << '\\' << 'r';
+ break;
+ case '\t':
+ oss << '\\' << 't';
+ break;
+ case '\"':
+ oss << '\\' << '"';
+ break;
+ case '\\':
+ oss << '\\' << '\\';
+ break;
+ }
+ break;
+ case 4:
+ oss << '\\' << '0' + (c / 64) << '0' + ((c % 64) / 8) << '0' + (c % 8);
+ break;
+ }
+ }
+ return oss.str();
+}
+
/*
* Dumps a string value with some escape characters.
*/
-static void dumpEscapedString(std::string_view p) {
+static void dumpEscapedString(std::string_view s) {
fputs("\"", gOutFile);
- for (char c : p) {
- switch (c) {
- case '\\':
- fputs("\\\\", gOutFile);
- break;
- case '\"':
- fputs("\\\"", gOutFile);
- break;
- case '\t':
- fputs("\\t", gOutFile);
- break;
- case '\n':
- fputs("\\n", gOutFile);
- break;
- case '\r':
- fputs("\\r", gOutFile);
- break;
- default:
- putc(c, gOutFile);
- } // switch
- } // for
+ if (needsEscape(s)) {
+ std::string e = escapeString(s);
+ fputs(e.c_str(), gOutFile);
+ } else {
+ for (char c : s) {
+ fputc(c, gOutFile);
+ }
+ }
fputs("\"", gOutFile);
}
@@ -858,7 +919,13 @@
case Instruction::kIndexStringRef:
if (index < pDexFile->GetHeader().string_ids_size_) {
const char* st = pDexFile->StringDataByIdx(dex::StringIndex(index));
- outSize = snprintf(buf.get(), bufSize, "\"%s\" // string@%0*x", st, width, index);
+ if (needsEscape(std::string_view(st))) {
+ std::string escaped = escapeString(st);
+ outSize =
+ snprintf(buf.get(), bufSize, "\"%s\" // string@%0*x", escaped.c_str(), width, index);
+ } else {
+ outSize = snprintf(buf.get(), bufSize, "\"%s\" // string@%0*x", st, width, index);
+ }
} else {
outSize = snprintf(buf.get(), bufSize, "<string?> // string@%0*x", width, index);
}