/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "base/common_art_test.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "dex/art_dex_file_loader.h" #include "dex/base64_test_util.h" #include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dexlayout.h" #include "exec_utils.h" #include "profile/profile_compilation_info.h" namespace art { static const char kDexFileLayoutInputDex[] = "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH" "AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAUAQAAAAEAADAB" "AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA" "AAAAAAABAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAdQEAAAAAAAABAAAA" "AAAAAAIAAAAAAAAAAgAAAAAAAAB/AQAAAAAAAAEAAQABAAAAaQEAAAQAAABwEAIAAAAOAAEAAQAB" "AAAAbwEAAAQAAABwEAIAAAAOAAY8aW5pdD4ABkEuamF2YQAGQi5qYXZhAANMQTsAA0xCOwASTGph" "dmEvbGFuZy9PYmplY3Q7AAFWAAQABw48AAQABw48AAAAAQAAgIAEgAIAAAEAAYCABJgCAAAACwAA" "AAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAAEAAAAjAAAAAMAAAABAAAAnAAAAAUAAAADAAAA" "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC" "AAAAdQEAAAAQAAABAAAAjAEAAA=="; // Dex file with catch handler unreferenced by try blocks. // Constructed by building a dex file with try/catch blocks and hex editing. static const char kUnreferencedCatchHandlerInputDex[] = "ZGV4CjAzNQD+exd52Y0f9nY5x5GmInXq5nXrO6Kl2RV4AwAAcAAAAHhWNBIAAAAAAAAAANgCAAAS" "AAAAcAAAAAgAAAC4AAAAAwAAANgAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAA0AgAARAEAANYB" "AADeAQAA5gEAAO4BAAAAAgAADwIAACYCAAA9AgAAUQIAAGUCAAB5AgAAfwIAAIUCAACIAgAAjAIA" "AKECAACnAgAArAIAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAwAAAAOAAAADAAAAAYAAAAAAAAA" "DQAAAAYAAADIAQAADQAAAAYAAADQAQAABQABABAAAAAAAAAAAAAAAAAAAgAPAAAAAQABABEAAAAD" "AAAAAAAAAAAAAAABAAAAAwAAAAAAAAADAAAAAAAAAMgCAAAAAAAAAQABAAEAAAC1AgAABAAAAHAQ" "AwAAAA4AAwABAAIAAgC6AgAAIQAAAGIAAAAaAQoAbiACABAAYgAAABoBCwBuIAIAEAAOAA0AYgAA" "ABoBAQBuIAIAEAAo8A0AYgAAABoBAgBuIAIAEAAo7gAAAAAAAAcAAQAHAAAABwABAAIBAg8BAhgA" "AQAAAAQAAAABAAAABwAGPGluaXQ+AAZDYXRjaDEABkNhdGNoMgAQSGFuZGxlclRlc3QuamF2YQAN" "TEhhbmRsZXJUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABVMamF2YS9sYW5nL0V4Y2VwdGlv" "bjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5" "c3RlbTsABFRyeTEABFRyeTIAAVYAAlZMABNbTGphdmEvbGFuZy9TdHJpbmc7AARtYWluAANvdXQA" "B3ByaW50bG4AAQAHDgAEAQAHDn17AncdHoseAAAAAgAAgYAExAIBCdwCAAANAAAAAAAAAAEAAAAA" "AAAAAQAAABIAAABwAAAAAgAAAAgAAAC4AAAAAwAAAAMAAADYAAAABAAAAAEAAAD8AAAABQAAAAQA" "AAAEAQAABgAAAAEAAAAkAQAAASAAAAIAAABEAQAAARAAAAIAAADIAQAAAiAAABIAAADWAQAAAyAA" "AAIAAAC1AgAAACAAAAEAAADIAgAAABAAAAEAAADYAgAA"; // Dex file with 0-size (catch all only) catch handler unreferenced by try blocks. // Constructed by building a dex file with try/catch blocks and hex editing. static const char kUnreferenced0SizeCatchHandlerInputDex[] = "ZGV4CjAzNQCEbEEvMstSNpQpjPdfMEfUBS48cis2QRJoAwAAcAAAAHhWNBIAAAAAAAAAAMgCAAAR" "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAQAAAD8AAAAAQAAABwBAAAsAgAAPAEAAOoB" "AADyAQAABAIAABMCAAAqAgAAPgIAAFICAABmAgAAaQIAAG0CAACCAgAAhgIAAIoCAACQAgAAlQIA" "AJ4CAACiAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACQAAAAcAAAAFAAAAAAAAAAgAAAAFAAAA" "3AEAAAgAAAAFAAAA5AEAAAQAAQANAAAAAAAAAAAAAAAAAAIADAAAAAEAAQAOAAAAAgAAAAAAAAAA" "AAAAAQAAAAIAAAAAAAAAAQAAAAAAAAC5AgAAAAAAAAEAAQABAAAApgIAAAQAAABwEAMAAAAOAAQA" "AQACAAIAqwIAAC8AAABiAAAAGgEPAG4gAgAQAGIAAAAaAQoAbiACABAAYgAAABoBEABuIAIAEABi" "AAAAGgELAG4gAgAQAA4ADQBiAQAAGgIKAG4gAgAhACcADQBiAQAAGgILAG4gAgAhACcAAAAAAAAA" "BwABAA4AAAAHAAEAAgAdACYAAAABAAAAAwAAAAEAAAAGAAY8aW5pdD4AEEhhbmRsZXJUZXN0Lmph" "dmEADUxIYW5kbGVyVGVzdDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" "Y3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwABVgACVkwAE1tMamF2" "YS9sYW5nL1N0cmluZzsAAmYxAAJmMgAEbWFpbgADb3V0AAdwcmludGxuAAJ0MQACdDIAAQAHDgAE" "AQAHDnl7eXkCeB2bAAAAAgAAgYAEvAIBCdQCAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAAC" "AAAABwAAALQAAAADAAAAAwAAANAAAAAEAAAAAQAAAPQAAAAFAAAABAAAAPwAAAAGAAAAAQAAABwB" "AAABIAAAAgAAADwBAAABEAAAAgAAANwBAAACIAAAEQAAAOoBAAADIAAAAgAAAKYCAAAAIAAAAQAA" "ALkCAAAAEAAAAQAAAMgCAAA="; // Dex file with an unreferenced catch handler at end of code item. // Constructed by building a dex file with try/catch blocks and hex editing. static const char kUnreferencedEndingCatchHandlerInputDex[] = "ZGV4CjAzNQCEflufI6xGTDDRmLpbfYi6ujPrDLIwvYcEBAAAcAAAAHhWNBIAAAAAAAAAAGQDAAAT" "AAAAcAAAAAgAAAC8AAAAAwAAANwAAAABAAAAAAEAAAUAAAAIAQAAAQAAADABAAC0AgAAUAEAAE4C" "AABWAgAAXgIAAGYCAAB4AgAAhwIAAJ4CAAC1AgAAyQIAAN0CAADxAgAA9wIAAP0CAAAAAwAABAMA" "ABkDAAAcAwAAIgMAACcDAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAADgAAAAwAAAAGAAAA" "AAAAAA0AAAAGAAAAQAIAAA0AAAAGAAAASAIAAAUAAQARAAAAAAAAAAAAAAAAAAAADwAAAAAAAgAQ" "AAAAAQABABIAAAADAAAAAAAAAAAAAAABAAAAAwAAAAAAAAADAAAAAAAAAFADAAAAAAAAAQABAAEA" "AAAwAwAABAAAAHAQBAAAAA4AAgAAAAIAAgA1AwAAIQAAAGIAAAAaAQoAbiADABAAYgAAABoBCwBu" "IAMAEAAOAA0AYgAAABoBAQBuIAMAEAAo8A0AYgAAABoBAgBuIAMAEAAo7gAAAAAAAAcAAQAHAAAA" "BwABAAIBAg8BAhgAAwABAAIAAgBCAwAAIQAAAGIAAAAaAQoAbiADABAAYgAAABoBCwBuIAMAEAAO" "AA0AYgAAABoBAQBuIAMAEAAo8A0AYgAAABoBAgBuIAMAEAAo7gAAAAAAAAcAAQAHAAAABwABAAIB" "Ag8BAhgAAQAAAAQAAAABAAAABwAGPGluaXQ+AAZDYXRjaDEABkNhdGNoMgAQSGFuZGxlclRlc3Qu" "amF2YQANTEhhbmRsZXJUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABVMamF2YS9sYW5nL0V4" "Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9s" "YW5nL1N5c3RlbTsABFRyeTEABFRyeTIAAVYAAlZMABNbTGphdmEvbGFuZy9TdHJpbmc7AAFhAARt" "YWluAANvdXQAB3ByaW50bG4AAQAHDgAEAAcOfHsCeB0eih4AEQEABw59ewJ3HR6LHgAAAAMAAIGA" "BNACAQnoAgEJ1AMAAA0AAAAAAAAAAQAAAAAAAAABAAAAEwAAAHAAAAACAAAACAAAALwAAAADAAAA" "AwAAANwAAAAEAAAAAQAAAAABAAAFAAAABQAAAAgBAAAGAAAAAQAAADABAAABIAAAAwAAAFABAAAB" "EAAAAgAAAEACAAACIAAAEwAAAE4CAAADIAAAAwAAADADAAAAIAAAAQAAAFADAAAAEAAAAQAAAGQD" "AAA="; // Dex file with multiple code items that have the same debug_info_off_. Constructed by a modified // dexlayout on XandY. static const char kDexFileDuplicateOffset[] = "ZGV4CjAzNwAQfXfPCB8qCxo7MqdFhmHZQwCv8+udHD8MBAAAcAAAAHhWNBIAAAAAAAAAAFQDAAAT" "AAAAcAAAAAgAAAC8AAAAAQAAANwAAAABAAAA6AAAAAUAAADwAAAAAwAAABgBAACUAgAAeAEAABQC" "AAAeAgAAJgIAACsCAAAyAgAANwIAAFsCAAB7AgAAngIAALICAAC1AgAAvQIAAMUCAADIAgAA1QIA" "AOkCAADvAgAA9QIAAPwCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAkAAAAHAAAA" "AAAAAAIAAQASAAAAAAAAAAEAAAABAAAAAQAAAAIAAAAAAAAAAgAAAAEAAAAGAAAAAQAAAAAAAAAA" "AAAABgAAAAAAAAAKAAAAAAAAACsDAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAsAAAD0AQAANQMAAAAA" "AAACAAAAAAAAAAAAAAAAAAAACwAAAAQCAAA/AwAAAAAAAAIAAAAUAwAAGgMAAAEAAAAjAwAAAQAB" "AAEAAAAFAAAABAAAAHAQBAAAAA4AAQABAAEAAAAFAAAABAAAAHAQBAAAAA4AAQAAAAEAAAAFAAAA" "CAAAACIAAQBwEAEAAABpAAAADgABAAEAAQAAAAUAAAAEAAAAcBAAAAAADgB4AQAAAAAAAAAAAAAA" "AAAAhAEAAAAAAAAAAAAAAAAAAAg8Y2xpbml0PgAGPGluaXQ+AANMWDsABUxZJFo7AANMWTsAIkxk" "YWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5l" "ckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNzZXM7ABJMamF2YS9sYW5nL09i" "amVjdDsAAVYABlguamF2YQAGWS5qYXZhAAFaAAthY2Nlc3NGbGFncwASZW1pdHRlcjogamFjay00" "LjI1AARuYW1lAAR0aGlzAAV2YWx1ZQABegARAAcOABMABw4AEgAHDnYAEQAHDgACAwERGAICBAIN" "BAgPFwwCBQERHAEYAQAAAQAAgIAEjAMAAAEAAYCABKQDAQACAAAIAoiABLwDAYCABNwDAAAADwAA" "AAAAAAABAAAAAAAAAAEAAAATAAAAcAAAAAIAAAAIAAAAvAAAAAMAAAABAAAA3AAAAAQAAAABAAAA" "6AAAAAUAAAAFAAAA8AAAAAYAAAADAAAAGAEAAAMQAAACAAAAeAEAAAEgAAAEAAAAjAEAAAYgAAAC" "AAAA9AEAAAIgAAATAAAAFAIAAAMgAAAEAAAA/wIAAAQgAAADAAAAFAMAAAAgAAADAAAAKwMAAAAQ" "AAABAAAAVAMAAA=="; // Dex file with null value for annotations_off in the annotation_set_ref_list. // Constructed by building a dex file with annotations and hex editing. static const char kNullSetRefListElementInputDex[] = "ZGV4CjAzNQB1iA+7ZwgkF+7E6ZesYFc2lRAR3qnRAanwAwAAcAAAAHhWNBIAAAAAAAAAACADAAAS" "AAAAcAAAAAgAAAC4AAAAAwAAANgAAAABAAAA/AAAAAQAAAAEAQAAAgAAACQBAACMAgAAZAEAAOgB" "AADwAQAAAAIAAAMCAAAQAgAAIAIAADQCAABIAgAAawIAAI0CAAC1AgAAyAIAANECAADUAgAA2QIA" "ANwCAADjAgAA6QIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAAAgAAAAMAAAAAAAAA" "DAAAAAcAAAAAAAAADQAAAAcAAADgAQAABgAGAAsAAAAAAAEAAAAAAAAAAgAOAAAAAQAAABAAAAAC" "AAEAAAAAAAAAAAAAAAAAAgAAAAAAAAABAAAAsAEAAAgDAAAAAAAAAQAAAAEmAAACAAAA2AEAAAoA" "AADIAQAAFgMAAAAAAAACAAAAAAAAAHwBAAABAAAA/AIAAAAAAAABAAAAAgMAAAEAAQABAAAA8AIA" "AAQAAABwEAMAAAAOAAIAAgAAAAAA9QIAAAEAAAAOAAAAAAAAAAAAAAAAAAAAAQAAAAEAAABkAQAA" "cAEAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAgAAAAMAAwAGPGluaXQ+AA5Bbm5vQ2xhc3MuamF2YQAB" "TAALTEFubm9DbGFzczsADkxNeUFubm90YXRpb247ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" "L2xhbmcvU3RyaW5nOwAhTGphdmEvbGFuZy9hbm5vdGF0aW9uL0Fubm90YXRpb247ACBMamF2YS9s" "YW5nL2Fubm90YXRpb24vUmV0ZW50aW9uOwAmTGphdmEvbGFuZy9hbm5vdGF0aW9uL1JldGVudGlv" "blBvbGljeTsAEU15QW5ub3RhdGlvbi5qYXZhAAdSVU5USU1FAAFWAANWTEwAAWEABWFOYW1lAARu" "YW1lAAV2YWx1ZQABAAcOAAICAAAHDgABBQERGwABAQEQFw8AAAIAAICABIQDAQmcAwAAAAECgQgA" "AAARAAAAAAAAAAEAAAAAAAAAAQAAABIAAABwAAAAAgAAAAgAAAC4AAAAAwAAAAMAAADYAAAABAAA" "AAEAAAD8AAAABQAAAAQAAAAEAQAABgAAAAIAAAAkAQAAAhAAAAEAAABkAQAAAxAAAAMAAABwAQAA" "ASAAAAIAAACEAQAABiAAAAIAAACwAQAAARAAAAIAAADYAQAAAiAAABIAAADoAQAAAyAAAAIAAADw" "AgAABCAAAAIAAAD8AgAAACAAAAIAAAAIAwAAABAAAAEAAAAgAwAA"; // Dex file with shared empty class data item for multiple class defs. // Constructing by building a dex file with multiple classes and hex editing. static const char kMultiClassDataInputDex[] = "ZGV4CjAzNQALJgF9TtnLq748xVe/+wyxETrT9lTEiW6YAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAI" "AAAAcAAAAAQAAACQAAAAAAAAAAAAAAACAAAAoAAAAAAAAAAAAAAAAgAAALAAAACoAAAA8AAAAPAA" "AAD4AAAAAAEAAAMBAAAIAQAADQEAACEBAAAkAQAAAgAAAAMAAAAEAAAABQAAAAEAAAAGAAAAAgAA" "AAcAAAABAAAAAQYAAAMAAAAAAAAAAAAAAAAAAAAnAQAAAAAAAAIAAAABBgAAAwAAAAAAAAABAAAA" "AAAAACcBAAAAAAAABkEuamF2YQAGQi5qYXZhAAFJAANMQTsAA0xCOwASTGphdmEvbGFuZy9PYmpl" "Y3Q7AAFhAAFiAAAAAAABAAAAARkAAAAIAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQA" "AACQAAAABAAAAAIAAACgAAAABgAAAAIAAACwAAAAAiAAAAgAAADwAAAAACAAAAIAAAAnAQAAABAA" "AAEAAAA0AQAA"; // Dex file with code info followed by non 4-byte aligned section. // Constructed a dex file with code info followed by string data and hex edited. static const char kUnalignedCodeInfoInputDex[] = "ZGV4CjAzNQDXJzXNb4iWn2SLhmLydW/8h1K9moERIw7UAQAAcAAAAHhWNBIAAAAAAAAAAEwBAAAG" "AAAAcAAAAAMAAACIAAAAAQAAAJQAAAAAAAAAAAAAAAMAAACgAAAAAQAAALgAAAD8AAAA2AAAAAIB" "AAAKAQAAEgEAABcBAAArAQAALgEAAAIAAAADAAAABAAAAAQAAAACAAAAAAAAAAAAAAAAAAAAAAAA" "AAUAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAABAAAAAAAAADsBAAAAAAAAAQABAAEAAAAxAQAA" "BAAAAHAQAgAAAA4AAQABAAAAAAA2AQAAAQAAAA4ABjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZh" "L2xhbmcvT2JqZWN0OwABVgABYQABAAcOAAMABw4AAAABAQCBgATYAQEB8AEAAAALAAAAAAAAAAEA" "AAAAAAAAAQAAAAYAAABwAAAAAgAAAAMAAACIAAAAAwAAAAEAAACUAAAABQAAAAMAAACgAAAABgAA" "AAEAAAC4AAAAASAAAAIAAADYAAAAAiAAAAYAAAACAQAAAyAAAAIAAAAxAQAAACAAAAEAAAA7AQAA" "ABAAAAEAAABMAQAA"; // Dex file with class data section preceding code items. // Constructed by passing dex file through dexmerger tool and hex editing. static const char kClassDataBeforeCodeInputDex[] = "ZGV4CjAzNQCZKmCu3XXn4zvxCh5VH0gZNNobEAcsc49EAgAAcAAAAHhWNBIAAAAAAAAAAAQBAAAJ" "AAAAcAAAAAQAAACUAAAAAgAAAKQAAAAAAAAAAAAAAAUAAAC8AAAAAQAAAOQAAABAAQAABAEAAPgB" "AAAAAgAACAIAAAsCAAAQAgAAJAIAACcCAAAqAgAALQIAAAIAAAADAAAABAAAAAUAAAACAAAAAAAA" "AAAAAAAFAAAAAwAAAAAAAAABAAEAAAAAAAEAAAAGAAAAAQAAAAcAAAABAAAACAAAAAIAAQAAAAAA" "AQAAAAEAAAACAAAAAAAAAAEAAAAAAAAAjAEAAAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAkAAABw" "AAAAAgAAAAQAAACUAAAAAwAAAAIAAACkAAAABQAAAAUAAAC8AAAABgAAAAEAAADkAAAAABAAAAEA" "AAAEAQAAACAAAAEAAACMAQAAASAAAAQAAACkAQAAAiAAAAkAAAD4AQAAAyAAAAQAAAAwAgAAAAAB" "AwCBgASkAwEBvAMBAdADAQHkAwAAAQABAAEAAAAwAgAABAAAAHAQBAAAAA4AAgABAAAAAAA1AgAA" "AgAAABIQDwACAAEAAAAAADoCAAACAAAAEiAPAAIAAQAAAAAAPwIAAAIAAAASMA8ABjxpbml0PgAG" "QS5qYXZhAAFJAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABYQABYgABYwABAAcOAAMABw4A" "BgAHDgAJAAcOAA=="; // Dex file with local info containing a null type descriptor. // Constructed a dex file with debug info sequence containing DBG_RESTART_LOCAL without any // DBG_START_LOCAL to give it a declared type. static const char kUnknownTypeDebugInfoInputDex[] = "ZGV4CjAzNQBtKqZfzjHLNSNwW2A6Bz9FuCEX0sL+FF38AQAAcAAAAHhWNBIAAAAAAAAAAHQBAAAI" "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAMAQAA8AAAABwB" "AAAkAQAALAEAAC8BAAA0AQAASAEAAEsBAABOAQAAAgAAAAMAAAAEAAAABQAAAAIAAAAAAAAAAAAA" "AAUAAAADAAAAAAAAAAEAAQAAAAAAAQAAAAYAAAACAAEAAAAAAAEAAAABAAAAAgAAAAAAAAABAAAA" "AAAAAGMBAAAAAAAAAQABAAEAAABUAQAABAAAAHAQAgAAAA4AAgABAAAAAABZAQAAAgAAABIQDwAG" "PGluaXQ+AAZBLmphdmEAAUkAA0xBOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAFhAAR0aGlzAAEA" "Bw4AAwAHDh4GAAYAAAAAAQEAgYAE8AEBAYgCAAAACwAAAAAAAAABAAAAAAAAAAEAAAAIAAAAcAAA" "AAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAAuAAAAAYAAAABAAAA0AAAAAEgAAACAAAA" "8AAAAAIgAAAIAAAAHAEAAAMgAAACAAAAVAEAAAAgAAABAAAAYwEAAAAQAAABAAAAdAEAAA=="; // Dex file with multiple class data items pointing to the same code item. // Constructed by hex editing. static const char kDuplicateCodeItemInputDex[] = "ZGV4CjAzNQCwKtVglQOmLWuHwldN5jkBOInC7mTMhJMAAgAAcAAAAHhWNBIAAAAAAAAAAHgBAAAH" "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAAcAQAA5AAAACQB" "AAAsAQAANAEAADkBAABNAQAAUAEAAFMBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA" "AAAAAAAFAAAAAAAAAAYAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAABAAAAAAAAAGUBAAAAAAAA" "AQABAAEAAABWAQAABAAAAHAQAwAAAA4AAQABAAAAAABbAQAAAQAAAA4AAAABAAEAAAAAAGABAAAB" "AAAADgAAAAY8aW5pdD4ABkEuamF2YQADTEE7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAAWEAAWIA" "AQAHDgADAAcOAAUABw4AAAABAgCBgATkAQEA/AEBAPwBAAsAAAAAAAAAAQAAAAAAAAABAAAABwAA" "AHAAAAACAAAAAwAAAIwAAAADAAAAAQAAAJgAAAAFAAAABAAAAKQAAAAGAAAAAQAAAMQAAAABIAAA" "AwAAAOQAAAACIAAABwAAACQBAAADIAAAAwAAAFYBAAAAIAAAAQAAAGUBAAAAEAAAAQAAAHgBAAA="; // Returns the default compact dex option for dexlayout based on kDefaultCompactDexLevel. static std::vector DefaultCompactDexOption() { return (kDefaultCompactDexLevel == CompactDexLevel::kCompactDexLevelFast) ? std::vector{"-x", "fast"} : std::vector{"-x", "none"}; } static void WriteBase64ToFile(const char* base64, File* file) { // Decode base64. CHECK(base64 != nullptr); size_t length; std::unique_ptr bytes(DecodeBase64(base64, &length)); CHECK(bytes != nullptr); if (!file->WriteFully(bytes.get(), length)) { PLOG(FATAL) << "Failed to write base64 as file"; } } static void WriteFileBase64(const char* base64, const char* location) { // Write to provided file. std::unique_ptr file(OS::CreateEmptyFile(location)); CHECK(file != nullptr); WriteBase64ToFile(base64, file.get()); if (file->FlushCloseOrErase() != 0) { PLOG(FATAL) << "Could not flush and close test file."; } } class DexLayoutTest : public CommonArtTest { protected: std::string GetDexLayoutPath() { return GetArtBinDir() + "/dexlayoutd"; } // Runs FullPlainOutput test. bool FullPlainOutputExec(std::string* error_msg) { ScratchFile dexdump_output; const std::string& dexdump_filename = dexdump_output.GetFilename(); std::string dexdump = GetArtBinDir() + "/dexdump"; EXPECT_TRUE(OS::FileExists(dexdump.c_str())) << dexdump << " should be a valid file path"; ScratchFile dexlayout_output; const std::string& dexlayout_filename = dexlayout_output.GetFilename(); for (const std::string& dex_file : GetLibCoreDexFileNames()) { std::vector dexdump_exec_argv = { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file }; std::vector dexlayout_args = { "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; if (!::art::Exec(dexdump_exec_argv, error_msg)) { return false; } if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } std::vector diff_exec_argv = { "/usr/bin/diff", dexdump_filename, dexlayout_filename }; if (!::art::Exec(diff_exec_argv, error_msg)) { return false; } } return true; } // Runs DexFileOutput test. bool DexFileOutputExec(std::string* error_msg) { ScratchFile tmp_file; const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); std::string unzip_dir = tmp_dir + "unzip/"; for (const std::string& dex_file : GetLibCoreDexFileNames()) { std::vector dexlayout_args = { "-w", tmp_dir, "-o", tmp_name, dex_file }; if (!DexLayoutExec(dexlayout_args, error_msg, /*pass_default_cdex_option=*/ false)) { return false; } std::string dex_file_name = "classes.dex"; std::vector unzip_exec_argv = { "/usr/bin/unzip", dex_file, "classes.dex", "-d", unzip_dir}; if (!::art::Exec(unzip_exec_argv, error_msg)) { return false; } std::vector diff_exec_argv = { "/usr/bin/diff", tmp_dir + "classes.dex" , unzip_dir + dex_file_name }; if (!::art::Exec(diff_exec_argv, error_msg)) { return false; } if (!UnlinkFile(unzip_dir + "classes.dex")) { return false; } if (!UnlinkFile(tmp_dir + dex_file_name)) { return false; } // Remove the unzip temp directory so that unlinking android_data doesn't fail. EXPECT_EQ(rmdir(unzip_dir.c_str()), 0); } return true; } // Create a profile with some subset of methods and classes. void CreateProfile(const std::string& input_dex, const std::string& out_profile) { std::vector> dex_files; std::string error_msg; const ArtDexFileLoader dex_file_loader; bool result = dex_file_loader.Open(input_dex.c_str(), input_dex, /*verify=*/ true, /*verify_checksum=*/ false, &error_msg, &dex_files); ASSERT_TRUE(result) << error_msg; ASSERT_GE(dex_files.size(), 1u); size_t profile_methods = 0; size_t profile_classes = 0; ProfileCompilationInfo pfi; for (const std::unique_ptr& dex_file : dex_files) { for (uint32_t i = 0; i < dex_file->NumMethodIds(); i += 2) { uint8_t flags = 0u; if ((i & 3) != 0) { flags |= ProfileCompilationInfo::MethodHotness::kFlagHot; ++profile_methods; } else if ((i & 2) != 0) { flags |= ProfileCompilationInfo::MethodHotness::kFlagStartup; ++profile_methods; } pfi.AddMethod(ProfileMethodInfo(MethodReference(dex_file.get(), /*index=*/ i)), static_cast(flags)); } // Add every even class too. std::set classes; for (uint32_t i = 0; i < dex_file->NumClassDefs(); i += 1) { if ((i & 2) == 0) { classes.insert(dex::TypeIndex(dex_file->GetClassDef(i).class_idx_)); ++profile_classes; } } if (!classes.empty()) { pfi.AddClassesForDex(dex_file.get(), classes.begin(), classes.end()); } } // Write to provided file. std::unique_ptr file(OS::CreateEmptyFile(out_profile.c_str())); ASSERT_TRUE(file != nullptr); pfi.Save(file->Fd()); if (file->FlushCloseOrErase() != 0) { PLOG(FATAL) << "Could not flush and close test file."; } EXPECT_GE(profile_methods, 0u); EXPECT_GE(profile_classes, 0u); } // Runs DexFileLayout test. bool DexFileLayoutExec(std::string* error_msg) { ScratchFile tmp_file; const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); // Write inputs and expected outputs. std::string dex_file = tmp_dir + "classes.dex"; WriteFileBase64(kDexFileLayoutInputDex, dex_file.c_str()); std::string profile_file = tmp_dir + "primary.prof"; CreateProfile(dex_file, profile_file); // WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str()); std::string output_dex = tmp_dir + "classes.dex.new"; std::vector dexlayout_args = { "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } // -v makes sure that the layout did not corrupt the dex file. if (!UnlinkFile(dex_file) || !UnlinkFile(profile_file) || !UnlinkFile(output_dex)) { return false; } return true; } // Runs DexFileLayout test twice (second time is run on output of first time) // for behavior consistency. bool DexFileLayoutFixedPointExec(std::string* error_msg) { ScratchFile tmp_file; const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); // Unzip the test dex file to the classes.dex destination. It is required to unzip since // opening from jar recalculates the dex location checksum. std::string dex_file = tmp_dir + "classes.dex"; std::vector unzip_args = { "/usr/bin/unzip", GetTestDexFileName("ManyMethods"), "classes.dex", "-d", tmp_dir, }; if (!art::Exec(unzip_args, error_msg)) { LOG(ERROR) << "Failed to unzip dex"; return false; } std::string profile_file = tmp_dir + "primary.prof"; CreateProfile(dex_file, profile_file); std::string output_dex = tmp_dir + "classes.dex.new"; std::string second_output_dex = tmp_dir + "classes.dex.new.new"; // -v makes sure that the layout did not corrupt the dex file. std::vector dexlayout_args = { "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; if (!DexLayoutExec(dexlayout_args, error_msg, /*pass_default_cdex_option=*/ false)) { return false; } // Recreate the profile with the new dex location. This is required so that the profile dex // location matches. CreateProfile(output_dex, profile_file); // -v makes sure that the layout did not corrupt the dex file. // -i since the checksum won't match from the first layout. std::vector second_dexlayout_args = { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex }; if (!DexLayoutExec(second_dexlayout_args, error_msg, /*pass_default_cdex_option=*/ false)) { return false; } bool diff_result = true; std::vector diff_exec_argv = { "/usr/bin/diff", output_dex, second_output_dex }; if (!::art::Exec(diff_exec_argv, error_msg)) { diff_result = false; } std::vector test_files = { dex_file, profile_file, output_dex, second_output_dex }; for (const std::string& test_file : test_files) { if (!UnlinkFile(test_file)) { return false; } } return diff_result; } // Runs UnreferencedCatchHandlerTest & Unreferenced0SizeCatchHandlerTest. bool UnreferencedCatchHandlerExec(std::string* error_msg, const char* filename) { ScratchFile tmp_file; const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); // Write inputs and expected outputs. std::string input_dex = tmp_dir + "classes.dex"; WriteFileBase64(filename, input_dex.c_str()); std::string output_dex = tmp_dir + "classes.dex.new"; std::vector dexlayout_args = { "-w", tmp_dir, "-o", "/dev/null", input_dex }; if (!DexLayoutExec(dexlayout_args, error_msg, /*pass_default_cdex_option=*/ false)) { return false; } // Diff input and output. They should be the same. std::vector diff_exec_argv = { "/usr/bin/diff", input_dex, output_dex }; if (!::art::Exec(diff_exec_argv, error_msg)) { return false; } std::vector dex_files = { input_dex, output_dex }; for (const std::string& dex_file : dex_files) { if (!UnlinkFile(dex_file)) { return false; } } return true; } bool DexLayoutExec(ScratchFile* dex_file, const char* dex_filename, ScratchFile* profile_file, const std::vector& dexlayout_args) { if (dex_filename != nullptr) { WriteBase64ToFile(dex_filename, dex_file->GetFile()); EXPECT_EQ(dex_file->GetFile()->Flush(), 0); } if (profile_file != nullptr) { CreateProfile(dex_file->GetFilename(), profile_file->GetFilename()); } std::string error_msg; const bool result = DexLayoutExec(dexlayout_args, &error_msg); if (!result) { LOG(ERROR) << "Error: " << error_msg; return false; } return true; } bool DexLayoutExec(const std::vector& dexlayout_args, std::string* error_msg, bool pass_default_cdex_option = true) { std::vector argv; std::string dexlayout = GetDexLayoutPath(); CHECK(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; argv.push_back(dexlayout); if (pass_default_cdex_option) { std::vector cdex_level = DefaultCompactDexOption(); argv.insert(argv.end(), cdex_level.begin(), cdex_level.end()); } argv.insert(argv.end(), dexlayout_args.begin(), dexlayout_args.end()); return ::art::Exec(argv, error_msg); } bool UnlinkFile(const std::string& file_path) { return unix_file::FdFile(file_path, 0, false).Unlink(); } }; TEST_F(DexLayoutTest, FullPlainOutput) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(FullPlainOutputExec(&error_msg)) << error_msg; } TEST_F(DexLayoutTest, DexFileOutput) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(DexFileOutputExec(&error_msg)) << error_msg; } TEST_F(DexLayoutTest, DexFileLayout) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(DexFileLayoutExec(&error_msg)) << error_msg; } TEST_F(DexLayoutTest, DexFileLayoutFixedPoint) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(DexFileLayoutFixedPointExec(&error_msg)) << error_msg; } TEST_F(DexLayoutTest, UnreferencedCatchHandler) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(UnreferencedCatchHandlerExec(&error_msg, kUnreferencedCatchHandlerInputDex)) << error_msg; } TEST_F(DexLayoutTest, Unreferenced0SizeCatchHandler) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(UnreferencedCatchHandlerExec(&error_msg, kUnreferenced0SizeCatchHandlerInputDex)) << error_msg; } TEST_F(DexLayoutTest, UnreferencedEndingCatchHandler) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(UnreferencedCatchHandlerExec(&error_msg, kUnreferencedEndingCatchHandlerInputDex)) << error_msg; } TEST_F(DexLayoutTest, DuplicateOffset) { ScratchFile temp_dex; std::vector dexlayout_args = { "-a", "-i", "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kDexFileDuplicateOffset, /* profile_file= */ nullptr, dexlayout_args)); } TEST_F(DexLayoutTest, NullSetRefListElement) { ScratchFile temp_dex; std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kNullSetRefListElementInputDex, /* profile_file= */ nullptr, dexlayout_args)); } TEST_F(DexLayoutTest, MultiClassData) { ScratchFile temp_dex; ScratchFile temp_profile; std::vector dexlayout_args = { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kMultiClassDataInputDex, &temp_profile, dexlayout_args)); } TEST_F(DexLayoutTest, UnalignedCodeInfo) { ScratchFile temp_dex; ScratchFile temp_profile; std::vector dexlayout_args = { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kUnalignedCodeInfoInputDex, &temp_profile, dexlayout_args)); } TEST_F(DexLayoutTest, ClassDataBeforeCode) { ScratchFile temp_dex; ScratchFile temp_profile; std::vector dexlayout_args = { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kClassDataBeforeCodeInputDex, &temp_profile, dexlayout_args)); } TEST_F(DexLayoutTest, UnknownTypeDebugInfo) { ScratchFile temp_dex; std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kUnknownTypeDebugInfoInputDex, /* profile_file= */ nullptr, dexlayout_args)); } TEST_F(DexLayoutTest, DuplicateCodeItem) { ScratchFile temp_dex; std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kDuplicateCodeItemInputDex, /* profile_file= */ nullptr, dexlayout_args)); } // Test that instructions that go past the end of the code items don't cause crashes. TEST_F(DexLayoutTest, CodeItemOverrun) { ScratchFile temp_dex; MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [] (DexFile* dex) { bool mutated_successfully = false; // Change the dex instructions to make an opcode that spans past the end of the code item. for (ClassAccessor accessor : dex->GetClasses()) { for (const ClassAccessor::Method& method : accessor.GetMethods()) { dex::CodeItem* item = const_cast(method.GetCodeItem()); if (item != nullptr) { CodeItemInstructionAccessor instructions(*dex, item); if (instructions.begin() != instructions.end()) { DexInstructionIterator last_instruction = instructions.begin(); for (auto dex_it = instructions.begin(); dex_it != instructions.end(); ++dex_it) { last_instruction = dex_it; } if (last_instruction->SizeInCodeUnits() == 1) { // Set the opcode to something that will go past the end of the code item. const_cast(last_instruction.Inst()).SetOpcode( Instruction::CONST_STRING_JUMBO); mutated_successfully = true; // Test that the safe iterator doesn't go past the end. SafeDexInstructionIterator it2(instructions.begin(), instructions.end()); while (!it2.IsErrorState()) { ++it2; } EXPECT_TRUE(it2 == last_instruction); EXPECT_TRUE(it2 < instructions.end()); } } } } } CHECK(mutated_successfully) << "Failed to find candidate code item with only one code unit in last instruction."; }); std::string error_msg; ScratchFile tmp_file; const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); ScratchFile profile_file; std::vector dexlayout_args = { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file.GetFilename(), temp_dex.GetFilename() }; // -v makes sure that the layout did not corrupt the dex file. ASSERT_TRUE(DexLayoutExec(&temp_dex, /*dex_filename=*/ nullptr, &profile_file, dexlayout_args)); ASSERT_TRUE(UnlinkFile(temp_dex.GetFilename() + ".new")); } // Test that link data is written out (or at least the header is updated). TEST_F(DexLayoutTest, LinkData) { TEST_DISABLED_FOR_TARGET(); ScratchFile temp_dex; size_t file_size = 0; MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) { DexFile::Header& header = const_cast(dex->GetHeader()); header.link_off_ = header.file_size_; header.link_size_ = 16 * KB; header.file_size_ += header.link_size_; file_size = header.file_size_; }); TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size)); std::string error_msg; ScratchFile tmp_file; const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); ScratchFile profile_file; std::vector dexlayout_args = { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file.GetFilename(), temp_dex.GetFilename() }; // -v makes sure that the layout did not corrupt the dex file. ASSERT_TRUE(DexLayoutExec(&temp_dex, /*dex_filename=*/ nullptr, &profile_file, dexlayout_args)); ASSERT_TRUE(UnlinkFile(temp_dex.GetFilename() + ".new")); } TEST_F(DexLayoutTest, ClassFilter) { std::vector> dex_files; std::string error_msg; const ArtDexFileLoader dex_file_loader; const std::string input_jar = GetTestDexFileName("ManyMethods"); CHECK(dex_file_loader.Open(input_jar.c_str(), input_jar.c_str(), /*verify=*/ true, /*verify_checksum=*/ true, &error_msg, &dex_files)) << error_msg; ASSERT_EQ(dex_files.size(), 1u); for (const std::unique_ptr& dex_file : dex_files) { EXPECT_GT(dex_file->NumClassDefs(), 1u); for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { const dex::ClassDef& class_def = dex_file->GetClassDef(i); LOG(INFO) << dex_file->GetClassDescriptor(class_def); } Options options; // Filter out all the classes other than the one below based on class descriptor. options.class_filter_.insert("LManyMethods$Strings;"); DexLayout dexlayout(options, /*info=*/ nullptr, /*out_file=*/ nullptr, /*header=*/ nullptr); std::unique_ptr out; bool result = dexlayout.ProcessDexFile( dex_file->GetLocation().c_str(), dex_file.get(), /*dex_file_index=*/ 0, &out, &error_msg); ASSERT_TRUE(result) << "Failed to run dexlayout " << error_msg; std::unique_ptr output_dex_file( dex_file_loader.OpenWithDataSection( out->GetMainSection()->Begin(), out->GetMainSection()->Size(), out->GetDataSection()->Begin(), out->GetDataSection()->Size(), dex_file->GetLocation().c_str(), /* location_checksum= */ 0, /*oat_dex_file=*/ nullptr, /* verify= */ true, /*verify_checksum=*/ false, &error_msg)); ASSERT_TRUE(output_dex_file != nullptr); ASSERT_EQ(output_dex_file->NumClassDefs(), options.class_filter_.size()); for (uint32_t i = 0; i < output_dex_file->NumClassDefs(); ++i) { // Check that every class in the output dex file is in the filter. const dex::ClassDef& class_def = output_dex_file->GetClassDef(i); ASSERT_TRUE(options.class_filter_.find(output_dex_file->GetClassDescriptor(class_def)) != options.class_filter_.end()); } } } } // namespace art