1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 /**
19 * @author Alex Beregszaszi
20 * @date 2018
21 * Tests for the assembler.
22 */
23
24 #include <libevmasm/Assembly.h>
25 #include <libsolutil/JSON.h>
26 #include <libyul/Exceptions.h>
27
28 #include <boost/test/unit_test.hpp>
29
30 #include <algorithm>
31 #include <memory>
32 #include <string>
33 #include <tuple>
34
35 using namespace std;
36 using namespace solidity::langutil;
37 using namespace solidity::evmasm;
38
39 namespace solidity::frontend::test
40 {
41
42 namespace
43 {
checkCompilation(evmasm::Assembly const & _assembly)44 void checkCompilation(evmasm::Assembly const& _assembly)
45 {
46 LinkerObject output = _assembly.assemble();
47 BOOST_CHECK(output.bytecode.size() > 0);
48 BOOST_CHECK(output.toHex().length() > 0);
49 }
50 }
51
52 BOOST_AUTO_TEST_SUITE(Assembler)
53
BOOST_AUTO_TEST_CASE(all_assembly_items)54 BOOST_AUTO_TEST_CASE(all_assembly_items)
55 {
56 map<string, unsigned> indices = {
57 { "root.asm", 0 },
58 { "sub.asm", 1 }
59 };
60 Assembly _assembly;
61 auto root_asm = make_shared<string>("root.asm");
62 _assembly.setSourceLocation({1, 3, root_asm});
63
64 Assembly _subAsm;
65 auto sub_asm = make_shared<string>("sub.asm");
66 _subAsm.setSourceLocation({6, 8, sub_asm});
67 // PushImmutable
68 _subAsm.appendImmutable("someImmutable");
69 _subAsm.append(Instruction::INVALID);
70 shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
71
72 // Tag
73 auto tag = _assembly.newTag();
74 _assembly.append(tag);
75 // Operation
76 _assembly.append(u256(1));
77 _assembly.append(u256(2));
78 // Push
79 _assembly.append(Instruction::KECCAK256);
80 // PushProgramSize
81 _assembly.appendProgramSize();
82 // PushLibraryAddress
83 _assembly.appendLibraryAddress("someLibrary");
84 // PushTag + Operation
85 _assembly.appendJump(tag);
86 // PushData
87 _assembly.append(bytes{0x1, 0x2, 0x3, 0x4});
88 // PushSubSize
89 auto sub = _assembly.appendSubroutine(_subAsmPtr);
90 // PushSub
91 _assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
92 // PushDeployTimeAddress
93 _assembly.append(PushDeployTimeAddress);
94 // AssignImmutable.
95 // Note that since there is no reference to "someOtherImmutable", this will just compile to two POPs in the hex output.
96 _assembly.appendImmutableAssignment("someOtherImmutable");
97 _assembly.append(u256(2));
98 _assembly.appendImmutableAssignment("someImmutable");
99 // Operation
100 _assembly.append(Instruction::STOP);
101 _assembly.appendToAuxiliaryData(bytes{0x42, 0x66});
102 _assembly.appendToAuxiliaryData(bytes{0xee, 0xaa});
103
104 checkCompilation(_assembly);
105
106 BOOST_CHECK_EQUAL(
107 _assembly.assemble().toHex(),
108 "5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__"
109 "6000566067602260457300000000000000000000000000000000000000005050"
110 "600260010152"
111 "00fe"
112 "7f0000000000000000000000000000000000000000000000000000000000000000"
113 "fe010203044266eeaa"
114 );
115 BOOST_CHECK_EQUAL(
116 _assembly.assemblyString(),
117 " /* \"root.asm\":1:3 */\n"
118 "tag_1:\n"
119 " keccak256(0x02, 0x01)\n"
120 " bytecodeSize\n"
121 " linkerSymbol(\"bf005014d9d0f534b8fcb268bd84c491a2380f4acd260d1ccfe9cd8201f7e994\")\n"
122 " jump(tag_1)\n"
123 " data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b\n"
124 " dataSize(sub_0)\n"
125 " dataOffset(sub_0)\n"
126 " deployTimeAddress()\n"
127 " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
128 " 0x02\n"
129 " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
130 " stop\n"
131 "stop\n"
132 "data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n"
133 "\n"
134 "sub_0: assembly {\n"
135 " /* \"sub.asm\":6:8 */\n"
136 " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
137 " invalid\n"
138 "}\n"
139 "\n"
140 "auxdata: 0x4266eeaa\n"
141 );
142 BOOST_CHECK_EQUAL(
143 util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
144 "{\".auxdata\":\"4266eeaa\",\".code\":["
145 "{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
146 "{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0},"
147 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"},"
148 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
149 "{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0},"
150 "{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0},"
151 "{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"},"
152 "{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},"
153 "{\"begin\":1,\"end\":3,\"name\":\"JUMP\",\"source\":0},"
154 "{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
155 "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
156 "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
157 "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0},"
158 "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"},"
159 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
160 "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"},"
161 "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}"
162 "],\".data\":{\"0\":{\".code\":["
163 "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
164 "{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}"
165 "]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}"
166 );
167 }
168
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)169 BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
170 {
171 // Tests for 1, 2, 3 number of immutables.
172 for (int numImmutables = 1; numImmutables <= 3; ++numImmutables)
173 {
174 BOOST_TEST_MESSAGE("NumImmutables: "s + to_string(numImmutables));
175 // Tests for the cases 1, 2, 3 occurrences of an immutable reference.
176 for (int numActualRefs = 1; numActualRefs <= 3; ++numActualRefs)
177 {
178 BOOST_TEST_MESSAGE("NumActualRefs: "s + to_string(numActualRefs));
179 auto const NumExpectedMappings =
180 (
181 2 + // PUSH <a> PUSH <b>
182 (numActualRefs - 1) * 5 + // DUP DUP PUSH <n> ADD MTOSRE
183 3 // PUSH <n> ADD MSTORkhbE
184 ) * numImmutables;
185
186 auto constexpr NumSubs = 1;
187 auto constexpr NumOpcodesWithoutMappings =
188 NumSubs + // PUSH <addr> for every sub assembly
189 1; // INVALID
190
191 auto assemblyName = make_shared<string>("root.asm");
192 auto subName = make_shared<string>("sub.asm");
193
194 map<string, unsigned> indices = {
195 { *assemblyName, 0 },
196 { *subName, 1 }
197 };
198
199 auto subAsm = make_shared<Assembly>();
200 for (char i = 0; i < numImmutables; ++i)
201 {
202 for (int r = 0; r < numActualRefs; ++r)
203 {
204 subAsm->setSourceLocation(SourceLocation{10*i, 10*i + 6 + r, subName});
205 subAsm->appendImmutable(string(1, char('a' + i))); // "a", "b", ...
206 }
207 }
208
209 Assembly assembly;
210 for (char i = 1; i <= numImmutables; ++i)
211 {
212 assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName});
213 assembly.append(u256(0x71)); // immutble value
214 assembly.append(u256(0)); // target... modules?
215 assembly.appendImmutableAssignment(string(1, char('a' + i - 1)));
216 }
217
218 assembly.appendSubroutine(subAsm);
219
220 checkCompilation(assembly);
221
222 string const sourceMappings = AssemblyItem::computeSourceMapping(assembly.items(), indices);
223 auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';');
224
225 LinkerObject const& obj = assembly.assemble();
226 string const disassembly = disassemble(obj.bytecode, "\n");
227 auto const numberOfOpcodes = std::count(disassembly.begin(), disassembly.end(), '\n');
228
229 #if 0 // {{{ debug prints
230 {
231 LinkerObject const& subObj = assembly.sub(0).assemble();
232 string const subDisassembly = disassemble(subObj.bytecode, "\n");
233 cout << '\n';
234 cout << "### immutables: " << numImmutables << ", refs: " << numActualRefs << '\n';
235 cout << " - srcmap: \"" << sourceMappings << "\"\n";
236 cout << " - src mappings: " << numberOfMappings << '\n';
237 cout << " - opcodes: " << numberOfOpcodes << '\n';
238 cout << " - subs: " << assembly.numSubs() << '\n';
239 cout << " - sub opcodes " << std::count(subDisassembly.begin(), subDisassembly.end(), '\n') << '\n';
240 cout << " - sub srcmaps " << AssemblyItem::computeSourceMapping(subAsm->items(), indices) << '\n';
241 cout << " - main bytecode:\n\t" << disassemble(obj.bytecode, "\n\t");
242 cout << "\r - sub bytecode:\n\t" << disassemble(subObj.bytecode, "\n\t");
243 }
244 #endif // }}}
245
246 BOOST_REQUIRE_EQUAL(NumExpectedMappings, numberOfMappings);
247 BOOST_REQUIRE_EQUAL(NumExpectedMappings, numberOfOpcodes - NumOpcodesWithoutMappings);
248 }
249 }
250 }
251
BOOST_AUTO_TEST_CASE(immutable)252 BOOST_AUTO_TEST_CASE(immutable)
253 {
254 map<string, unsigned> indices = {
255 { "root.asm", 0 },
256 { "sub.asm", 1 }
257 };
258 Assembly _assembly;
259 auto root_asm = make_shared<string>("root.asm");
260 _assembly.setSourceLocation({1, 3, root_asm});
261
262 Assembly _subAsm;
263 auto sub_asm = make_shared<string>("sub.asm");
264 _subAsm.setSourceLocation({6, 8, sub_asm});
265 _subAsm.appendImmutable("someImmutable");
266 _subAsm.appendImmutable("someOtherImmutable");
267 _subAsm.appendImmutable("someImmutable");
268 shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
269
270 _assembly.append(u256(42));
271 _assembly.append(u256(0));
272 _assembly.appendImmutableAssignment("someImmutable");
273 _assembly.append(u256(23));
274 _assembly.append(u256(0));
275 _assembly.appendImmutableAssignment("someOtherImmutable");
276
277 auto sub = _assembly.appendSubroutine(_subAsmPtr);
278 _assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
279
280 checkCompilation(_assembly);
281
282 BOOST_CHECK_EQUAL(
283 _assembly.assemble().toHex(),
284 // root.asm
285 // assign "someImmutable"
286 "602a" // PUSH1 42 - value for someImmutable
287 "6000" // PUSH1 0 - offset of code into which to insert the immutable
288 "8181" // DUP2 DUP2
289 "6001" // PUSH1 1 - offset of first someImmutable in sub_0
290 "01" // ADD - add offset of immutable to offset of code
291 "52" // MSTORE
292 "6043" // PUSH1 67 - offset of second someImmutable in sub_0
293 "01" // ADD - add offset of immutable to offset of code
294 "52" // MSTORE
295 // assign "someOtherImmutable"
296 "6017" // PUSH1 23 - value for someOtherImmutable
297 "6000" // PUSH1 0 - offset of code into which to insert the immutable
298 "6022" // PUSH1 34 - offset of someOtherImmutable in sub_0
299 "01" // ADD - add offset of immutable to offset of code
300 "52" // MSTORE
301 "6063" // PUSH1 0x63 - dataSize(sub_0)
302 "601b" // PUSH1 0x23 - dataOffset(sub_0)
303 "fe" // INVALID
304 // end of root.asm
305 // sub.asm
306 "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 1
307 "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someOtherImmutable - data at offset 34
308 "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 67
309 );
310 BOOST_CHECK_EQUAL(
311 _assembly.assemblyString(),
312 " /* \"root.asm\":1:3 */\n"
313 " 0x2a\n"
314 " 0x00\n"
315 " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
316 " 0x17\n"
317 " 0x00\n"
318 " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
319 " dataSize(sub_0)\n"
320 " dataOffset(sub_0)\n"
321 "stop\n"
322 "\n"
323 "sub_0: assembly {\n"
324 " /* \"sub.asm\":6:8 */\n"
325 " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
326 " immutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
327 " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
328 "}\n"
329 );
330 BOOST_CHECK_EQUAL(
331 util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
332 "{\".code\":["
333 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"},"
334 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
335 "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"},"
336 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"17\"},"
337 "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
338 "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"},"
339 "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
340 "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}"
341 "],\".data\":{\"0\":{\".code\":["
342 "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
343 "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"},"
344 "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}"
345 "]}}}"
346 );
347 }
348
BOOST_AUTO_TEST_CASE(subobject_encode_decode)349 BOOST_AUTO_TEST_CASE(subobject_encode_decode)
350 {
351 Assembly assembly;
352
353 shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>();
354 shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>();
355
356 assembly.appendSubroutine(subAsmPtr);
357 subAsmPtr->appendSubroutine(subSubAsmPtr);
358
359 BOOST_CHECK(assembly.encodeSubPath({0}) == 0);
360 BOOST_REQUIRE_THROW(assembly.encodeSubPath({1}), solidity::evmasm::AssemblyException);
361 BOOST_REQUIRE_THROW(assembly.decodeSubPath(1), solidity::evmasm::AssemblyException);
362
363 vector<size_t> subPath{0, 0};
364 BOOST_CHECK(assembly.decodeSubPath(assembly.encodeSubPath(subPath)) == subPath);
365 }
366
367 BOOST_AUTO_TEST_SUITE_END()
368
369 } // end namespaces
370