diff options
Diffstat (limited to 'third_party/ijar/ijar.cc')
-rw-r--r-- | third_party/ijar/ijar.cc | 292 |
1 files changed, 223 insertions, 69 deletions
diff --git a/third_party/ijar/ijar.cc b/third_party/ijar/ijar.cc index 160645d7ae..a50b97037d 100644 --- a/third_party/ijar/ijar.cc +++ b/third_party/ijar/ijar.cc @@ -15,11 +15,11 @@ // ijar.cpp -- .jar -> _interface.jar tool. // +#include <errno.h> +#include <limits.h> #include <stdio.h> -#include <string.h> #include <stdlib.h> -#include <limits.h> -#include <errno.h> +#include <string.h> #include <memory> #include "third_party/ijar/zip.h" @@ -31,9 +31,9 @@ bool verbose = false; // Reads a JVM class from classdata_in (of the specified length), and // writes out a simplified class to classdata_out, advancing the // pointer. Returns true if the class should be kept. -bool StripClass(u1*& classdata_out, const u1* classdata_in, size_t in_length); +bool StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length); -const char* CLASS_EXTENSION = ".class"; +const char *CLASS_EXTENSION = ".class"; const size_t CLASS_EXTENSION_LENGTH = strlen(CLASS_EXTENSION); const char *MANIFEST_DIR_PATH = "META-INF/"; @@ -51,29 +51,34 @@ const size_t TARGET_LABEL_KEY_LENGTH = strlen(TARGET_LABEL_KEY); const char *INJECTING_RULE_KIND_KEY = "Injecting-Rule-Kind: "; const size_t INJECTING_RULE_KIND_KEY_LENGTH = strlen(INJECTING_RULE_KIND_KEY); +class JarExtractorProcessor : public ZipExtractorProcessor { + public: + // Set the ZipBuilder to add the ijar class to the output zip file. + // This pointer should not be deleted while this class is still in use and + // it should be set before any call to the Process() method. + void SetZipBuilder(ZipBuilder *builder) { this->builder_ = builder; } + virtual void WriteManifest(const char *target_label, + const char *injecting_rule_kind) = 0; + + protected: + // Not owned by JarStripperProcessor, see SetZipBuilder(). + ZipBuilder *builder_; +}; + // ZipExtractorProcessor that select only .class file and use // StripClass to generate an interface class, storing as a new file // in the specified ZipBuilder. -class JarStripperProcessor : public ZipExtractorProcessor { +class JarStripperProcessor : public JarExtractorProcessor { public: JarStripperProcessor() {} virtual ~JarStripperProcessor() {} - virtual void Process(const char* filename, const u4 attr, - const u1* data, const size_t size); - virtual bool Accept(const char* filename, const u4 attr); - - private: - // Not owned by JarStripperProcessor, see SetZipBuilder(). - ZipBuilder* builder; + virtual void Process(const char *filename, const u4 attr, const u1 *data, + const size_t size); + virtual bool Accept(const char *filename, const u4 attr); - public: - // Set the ZipBuilder to add the ijar class to the output zip file. - // This pointer should not be deleted while this class is still in use and - // it should be set before any call to the Process() method. - void SetZipBuilder(ZipBuilder* builder) { - this->builder = builder; - } + virtual void WriteManifest(const char *target_label, + const char *injecting_rule_kind); }; bool JarStripperProcessor::Accept(const char *filename, const u4 /*attr*/) { @@ -86,8 +91,8 @@ bool JarStripperProcessor::Accept(const char *filename, const u4 /*attr*/) { return true; } -static bool IsModuleInfo(const char* filename) { - const char* slash = strrchr(filename, '/'); +static bool IsModuleInfo(const char *filename) { + const char *slash = strrchr(filename, '/'); if (slash == NULL) { slash = filename; } else { @@ -102,36 +107,195 @@ void JarStripperProcessor::Process(const char *filename, const u4 /*attr*/, fprintf(stderr, "INFO: StripClass: %s\n", filename); } if (IsModuleInfo(filename)) { - u1* q = builder->NewFile(filename, 0); + u1 *q = builder_->NewFile(filename, 0); memcpy(q, data, size); - builder->FinishFile(size, false, true); + builder_->FinishFile(size, /* compress: */ false, /* compute_crc: */ true); } else { - u1* buf = reinterpret_cast<u1*>(malloc(size)); - u1* classdata_out = buf; + u1 *buf = reinterpret_cast<u1 *>(malloc(size)); + u1 *classdata_out = buf; if (!StripClass(buf, data, size)) { free(classdata_out); return; } - u1* q = builder->NewFile(filename, 0); + u1 *q = builder_->NewFile(filename, 0); size_t out_length = buf - classdata_out; memcpy(q, classdata_out, out_length); - builder->FinishFile(out_length, false, true); + builder_->FinishFile(out_length, /* compress: */ false, + /* compute_crc: */ true); free(classdata_out); } } -// Copies the string into the buffer without the null terminator, returns length -static size_t WriteStr(u1 *buf, const char *str) { +// Copies the string into the buffer without the null terminator, returns +// updated buffer pointer +static u1 *WriteStr(u1 *buf, const char *str) { size_t len = strlen(str); memcpy(buf, str, len); - return len; + return buf + len; +} + +// Writes a manifest attribute including a "\r\n" line break, returns updated +// buffer pointer. +static u1 *WriteManifestAttr(u1 *buf, const char *key, const char *val) { + buf = WriteStr(buf, key); + buf = WriteStr(buf, val); + *buf++ = '\r'; + *buf++ = '\n'; + return buf; +} + +void JarStripperProcessor::WriteManifest(const char *target_label, + const char *injecting_rule_kind) { + if (target_label == nullptr) { + return; + } + builder_->WriteEmptyFile(MANIFEST_DIR_PATH); + u1 *start = builder_->NewFile(MANIFEST_PATH, 0); + u1 *buf = start; + buf = WriteStr(buf, MANIFEST_HEADER); + buf = WriteManifestAttr(buf, TARGET_LABEL_KEY, target_label); + if (injecting_rule_kind) { + buf = WriteManifestAttr(buf, INJECTING_RULE_KIND_KEY, injecting_rule_kind); + } + size_t total_len = buf - start; + builder_->FinishFile(total_len, /* compress: */ false, + /* compute_crc: */ true); +} + +class JarCopierProcessor : public JarExtractorProcessor { + public: + JarCopierProcessor(const char *jar) : jar_(jar) {} + virtual ~JarCopierProcessor() {} + + virtual void Process(const char *filename, const u4 /*attr*/, const u1 *data, + const size_t size); + virtual bool Accept(const char *filename, const u4 /*attr*/); + + virtual void WriteManifest(const char *target_label, + const char *injecting_rule_kind); + + private: + class ManifestLocator : public ZipExtractorProcessor { + public: + ManifestLocator() : manifest_buf_(nullptr), manifest_size_(0) {} + virtual ~ManifestLocator() { free(manifest_buf_); } + + u1 *manifest_buf_; + size_t manifest_size_; + + virtual bool Accept(const char *filename, const u4 /*attr*/) { + return strcmp(filename, MANIFEST_PATH) == 0; + } + + virtual void Process(const char * /*filename*/, const u4 /*attr*/, + const u1 *data, const size_t size) { + manifest_buf_ = (u1 *)malloc(size); + memmove(manifest_buf_, data, size); + manifest_size_ = size; + } + }; + + const char *jar_; + + u1 *AppendTargetLabelToManifest(u1 *buf, const u1 *manifest_data, + const size_t size, const char *target_label, + const char *injecting_rule_kind); +}; + +void JarCopierProcessor::Process(const char *filename, const u4 /*attr*/, + const u1 *data, const size_t size) { + if (verbose) { + fprintf(stderr, "INFO: CopyFile: %s\n", filename); + } + // We already handled the manifest in WriteManifest + if (strcmp(filename, MANIFEST_DIR_PATH) == 0 || + strcmp(filename, MANIFEST_PATH) == 0) { + return; + } + u1 *q = builder_->NewFile(filename, 0); + memcpy(q, data, size); + builder_->FinishFile(size, /* compress: */ false, /* compute_crc: */ true); +} + +bool JarCopierProcessor::Accept(const char * /*filename*/, const u4 /*attr*/) { + return true; +} + +void JarCopierProcessor::WriteManifest(const char *target_label, + const char *injecting_rule_kind) { + ManifestLocator manifest_locator; + std::unique_ptr<ZipExtractor> in( + ZipExtractor::Create(jar_, &manifest_locator)); + in->ProcessAll(); + + bool wants_manifest = + manifest_locator.manifest_buf_ != nullptr || target_label != nullptr; + if (wants_manifest) { + builder_->WriteEmptyFile(MANIFEST_DIR_PATH); + u1 *start = builder_->NewFile(MANIFEST_PATH, 0); + u1 *buf = start; + // Three cases: + // 1. We need to merge the target label into a pre-existing manifest + // 2. Write a manifest from scratch with a target label + // 3. Copy existing manifest without adding target label + if (manifest_locator.manifest_buf_ != nullptr && target_label != nullptr) { + buf = AppendTargetLabelToManifest(buf, manifest_locator.manifest_buf_, + manifest_locator.manifest_size_, + target_label, injecting_rule_kind); + } else if (target_label != nullptr) { + buf = WriteStr(buf, MANIFEST_HEADER); + buf = WriteManifestAttr(buf, TARGET_LABEL_KEY, target_label); + if (injecting_rule_kind) { + buf = WriteManifestAttr(buf, INJECTING_RULE_KIND_KEY, + injecting_rule_kind); + } + } else { + memcpy(buf, manifest_locator.manifest_buf_, + manifest_locator.manifest_size_); + buf += manifest_locator.manifest_size_; + } + + size_t total_len = buf - start; + builder_->FinishFile(total_len, /* compress: */ false, + /* compute_crc: */ true); + } +} + +u1 *JarCopierProcessor::AppendTargetLabelToManifest( + u1 *buf, const u1 *manifest_data, const size_t size, + const char *target_label, const char *injecting_rule_kind) { + const char *line_start = (const char *)manifest_data; + const char *data_end = (const char *)manifest_data + size; + while (line_start < data_end) { + const char *line_end = strchr(line_start, '\n'); + // Go past return char to point to next line, or to end of data buffer + line_end = line_end != nullptr ? line_end + 1 : data_end; + + // Copy line unless it's Target-Label/Injecting-Rule-Kind and we're writing + // that ourselves + if (strncmp(line_start, TARGET_LABEL_KEY, TARGET_LABEL_KEY_LENGTH) != 0 && + strncmp(line_start, INJECTING_RULE_KIND_KEY, + INJECTING_RULE_KIND_KEY_LENGTH) != 0) { + size_t len = line_end - line_start; + // Skip empty lines + if (len > 0 && line_start[0] != '\r' && line_start[0] != '\n') { + memcpy(buf, line_start, len); + buf += len; + } + } + line_start = line_end; + } + buf = WriteManifestAttr(buf, TARGET_LABEL_KEY, target_label); + if (injecting_rule_kind != nullptr) { + buf = WriteManifestAttr(buf, INJECTING_RULE_KIND_KEY, injecting_rule_kind); + } + return buf; } -// Computes the size of zip file content for the manifest created by // WriteManifest, including zip file format overhead. static size_t EstimateManifestOutputSize(const char *target_label, const char *injecting_rule_kind) { - if (target_label == NULL) { + if (target_label == nullptr) { return 0; } // local headers @@ -152,36 +316,21 @@ static size_t EstimateManifestOutputSize(const char *target_label, return length; } -static void WriteManifest(ZipBuilder *out, const char *target_label, - const char *injecting_rule_kind) { - if (target_label == NULL) { - return; - } - out->WriteEmptyFile(MANIFEST_DIR_PATH); - u1 *start = out->NewFile(MANIFEST_PATH, 0); - u1 *buf = start; - buf += WriteStr(buf, MANIFEST_HEADER); - buf += WriteStr(buf, TARGET_LABEL_KEY); - buf += WriteStr(buf, target_label); - *buf++ = '\r'; - *buf++ = '\n'; - if (injecting_rule_kind) { - buf += WriteStr(buf, INJECTING_RULE_KIND_KEY); - buf += WriteStr(buf, injecting_rule_kind); - *buf++ = '\r'; - *buf++ = '\n'; - } - size_t total_len = buf - start; - out->FinishFile(total_len); -} - // Opens "file_in" (a .jar file) for reading, and writes an interface // .jar to "file_out". static void OpenFilesAndProcessJar(const char *file_out, const char *file_in, - const char *target_label, + bool strip_jar, const char *target_label, const char *injecting_rule_kind) { - JarStripperProcessor processor; - std::unique_ptr<ZipExtractor> in(ZipExtractor::Create(file_in, &processor)); + std::unique_ptr<JarExtractorProcessor> processor; + if (strip_jar) { + processor = + std::unique_ptr<JarExtractorProcessor>(new JarStripperProcessor()); + } else { + processor = + std::unique_ptr<JarExtractorProcessor>(new JarCopierProcessor(file_in)); + } + std::unique_ptr<ZipExtractor> in( + ZipExtractor::Create(file_in, processor.get())); if (in.get() == NULL) { fprintf(stderr, "Unable to open Zip file %s: %s\n", file_in, strerror(errno)); @@ -196,9 +345,8 @@ static void OpenFilesAndProcessJar(const char *file_out, const char *file_in, strerror(errno)); abort(); } - processor.SetZipBuilder(out.get()); - - WriteManifest(out.get(), target_label, injecting_rule_kind); + processor->SetZipBuilder(out.get()); + processor->WriteManifest(target_label, injecting_rule_kind); // Process all files in the zip if (in->ProcessAll() < 0) { @@ -219,9 +367,8 @@ static void OpenFilesAndProcessJar(const char *file_out, const char *file_in, size_t in_length = in->GetSize(); size_t out_length = out->GetSize(); if (verbose) { - fprintf(stderr, "INFO: produced interface jar: %s -> %s (%d%%).\n", - file_in, file_out, - static_cast<int>(100.0 * out_length / in_length)); + fprintf(stderr, "INFO: produced interface jar: %s -> %s (%d%%).\n", file_in, + file_out, static_cast<int>(100.0 * out_length / in_length)); } } } // namespace devtools_ijar @@ -232,13 +379,15 @@ static void OpenFilesAndProcessJar(const char *file_out, const char *file_in, static void usage() { fprintf(stderr, "Usage: ijar " - "[-v] [--target label label] [--injecting_rule_kind kind] " + "[-v] [--[no]strip_jar] " + "[--target label label] [--injecting_rule_kind kind] " "x.jar [x_interface.jar>]\n"); fprintf(stderr, "Creates an interface jar from the specified jar file.\n"); exit(1); } int main(int argc, char **argv) { + bool strip_jar = true; const char *target_label = NULL; const char *injecting_rule_kind = NULL; const char *filename_in = NULL; @@ -247,6 +396,10 @@ int main(int argc, char **argv) { for (int ii = 1; ii < argc; ++ii) { if (strcmp(argv[ii], "-v") == 0) { devtools_ijar::verbose = true; + } else if (strcmp(argv[ii], "--strip_jar") == 0) { + strip_jar = true; + } else if (strcmp(argv[ii], "--nostrip_jar") == 0) { + strip_jar = false; } else if (strcmp(argv[ii], "--target_label") == 0) { if (++ii >= argc) { usage(); @@ -279,7 +432,8 @@ int main(int argc, char **argv) { strcpy(filename_out_buf + len - 4, "-interface.jar"); filename_out = filename_out_buf; } else { - fprintf(stderr, "Can't determine output filename since input filename " + fprintf(stderr, + "Can't determine output filename since input filename " "doesn't end with '.jar'.\n"); return 1; } @@ -289,7 +443,7 @@ int main(int argc, char **argv) { fprintf(stderr, "INFO: writing to '%s'.\n", filename_out); } - devtools_ijar::OpenFilesAndProcessJar(filename_out, filename_in, target_label, - injecting_rule_kind); + devtools_ijar::OpenFilesAndProcessJar(filename_out, filename_in, strip_jar, + target_label, injecting_rule_kind); return 0; } |