• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "dex_builder.h"
18 
19 #include "dex/art_dex_file_loader.h"
20 #include "dex/dex_file.h"
21 #include "gtest/gtest.h"
22 
23 using namespace startop::dex;
24 
25 // Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and
26 // returns whether the verification was successful.
EncodeAndVerify(DexBuilder * dex_file)27 bool EncodeAndVerify(DexBuilder* dex_file) {
28   slicer::MemView image{dex_file->CreateImage()};
29 
30   art::ArtDexFileLoader loader;
31   std::string error_msg;
32   std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(),
33                                                                   image.size(),
34                                                                   /*location=*/"",
35                                                                   /*location_checksum=*/0,
36                                                                   /*oat_dex_file=*/nullptr,
37                                                                   /*verify=*/true,
38                                                                   /*verify_checksum=*/false,
39                                                                   &error_msg)};
40   return loaded_dex_file != nullptr;
41 }
42 
43 // Write out and verify a DEX file that corresponds to:
44 //
45 // package dextest;
46 // public class DexTest {
47 //     public static void foo() {}
48 // }
TEST(DexBuilderTest,VerifyDexWithClassMethod)49 TEST(DexBuilderTest, VerifyDexWithClassMethod) {
50   DexBuilder dex_file;
51 
52   auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
53 
54   auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})};
55   method.BuildReturn();
56   method.Encode();
57 
58   EXPECT_TRUE(EncodeAndVerify(&dex_file));
59 }
60 
61 // Makes sure a bad DEX class fails to verify.
TEST(DexBuilderTest,VerifyBadDexWithClassMethod)62 TEST(DexBuilderTest, VerifyBadDexWithClassMethod) {
63   DexBuilder dex_file;
64 
65   auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
66 
67   // This method has the error, because methods cannot take Void() as a parameter.
68   auto method{
69       cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})};
70   method.BuildReturn();
71   method.Encode();
72 
73   EXPECT_FALSE(EncodeAndVerify(&dex_file));
74 }
75 
76 // Write out and verify a DEX file that corresponds to:
77 //
78 // package dextest;
79 // public class DexTest {
80 //     public static int foo() { return 5; }
81 // }
TEST(DexBuilderTest,VerifyDexReturn5)82 TEST(DexBuilderTest, VerifyDexReturn5) {
83   DexBuilder dex_file;
84 
85   auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
86 
87   auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
88   auto r = method.MakeRegister();
89   method.BuildConst4(r, 5);
90   method.BuildReturn(r);
91   method.Encode();
92 
93   EXPECT_TRUE(EncodeAndVerify(&dex_file));
94 }
95 
96 // Write out and verify a DEX file that corresponds to:
97 //
98 // package dextest;
99 // public class DexTest {
100 //     public static int foo(int x) { return x; }
101 // }
TEST(DexBuilderTest,VerifyDexReturnIntParam)102 TEST(DexBuilderTest, VerifyDexReturnIntParam) {
103   DexBuilder dex_file;
104 
105   auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
106 
107   auto method{
108       cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
109   method.BuildReturn(Value::Parameter(0));
110   method.Encode();
111 
112   EXPECT_TRUE(EncodeAndVerify(&dex_file));
113 }
114 
115 // Write out and verify a DEX file that corresponds to:
116 //
117 // package dextest;
118 // public class DexTest {
119 //     public static int foo(String s) { return s.length(); }
120 // }
TEST(DexBuilderTest,VerifyDexCallStringLength)121 TEST(DexBuilderTest, VerifyDexCallStringLength) {
122   DexBuilder dex_file;
123 
124   auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
125 
126   MethodBuilder method{cbuilder.CreateMethod(
127       "foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::FromClassname("java.lang.String")})};
128 
129   Value result = method.MakeRegister();
130 
131   MethodDeclData string_length =
132       dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
133                                   "length",
134                                   Prototype{TypeDescriptor::Int()});
135 
136   method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
137   method.BuildReturn(result);
138 
139   method.Encode();
140 
141   EXPECT_TRUE(EncodeAndVerify(&dex_file));
142 }
143 
144 // Write out and verify a DEX file that corresponds to:
145 //
146 // package dextest;
147 // public class DexTest {
148 //     public static int foo(String s) { return s.length(); }
149 // }
TEST(DexBuilderTest,VerifyDexCallManyRegisters)150 TEST(DexBuilderTest, VerifyDexCallManyRegisters) {
151   DexBuilder dex_file;
152 
153   auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
154 
155   MethodBuilder method{cbuilder.CreateMethod(
156       "foo", Prototype{TypeDescriptor::Int()})};
157 
158   Value result = method.MakeRegister();
159 
160   // Make a bunch of registers
161   for (size_t i = 0; i < 25; ++i) {
162     method.MakeRegister();
163   }
164 
165   // Now load a string literal into a register
166   Value string_val = method.MakeRegister();
167   method.BuildConstString(string_val, "foo");
168 
169   MethodDeclData string_length =
170       dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
171                                   "length",
172                                   Prototype{TypeDescriptor::Int()});
173 
174   method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, string_val));
175   method.BuildReturn(result);
176 
177   method.Encode();
178 
179   EXPECT_TRUE(EncodeAndVerify(&dex_file));
180 }
181