1 //: Operands can refer to bitfields smaller than a byte. This layer packs
  2 //: operands into their containing bytes in the right order.
  3 
  4 :(scenario pack_immediate_constants)
  5 == 0x1
  6 # instruction                     effective address                                                   operand     displacement    immediate
  7 # op          subop               mod             rm32          base        index         scale       r32
  8 # 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  9   bb                                                                                                                              0x2a/imm32        # copy 42 to EBX
 10 +transform: packing instruction 'bb 0x2a/imm32'
 11 +transform: instruction after packing: 'bb 2a 00 00 00'
 12 +run: copy imm32 0x0000002a to EBX
 13 
 14 :(scenario pack_disp8)
 15 == 0x1
 16 74 2/disp8  # jump 2 bytes away if ZF is set
 17 +transform: packing instruction '74 2/disp8'
 18 +transform: instruction after packing: '74 02'
 19 
 20 :(scenarios transform)
 21 :(scenario pack_disp8_negative)
 22 == 0x1
 23 # running this will cause an infinite loop
 24 74 -1/disp8  # jump 1 byte before if ZF is set
 25 +transform: packing instruction '74 -1/disp8'
 26 +transform: instruction after packing: '74 ff'
 27 :(scenarios run)
 28 
 29 :(scenario pack_modrm_imm32)
 30 == 0x1
 31 # instruction                     effective address                                                   operand     displacement    immediate
 32 # op          subop               mod             rm32          base        index         scale       r32
 33 # 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 34   81          0/add/subop         3/mod/direct    3/ebx/rm32                                                                      1/imm32           # add 1 to EBX
 35 +transform: packing instruction '81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32'
 36 +transform: instruction after packing: '81 c3 01 00 00 00'
 37 
 38 :(scenario pack_imm32_large)
 39 == 0x1
 40 b9 0x080490a7/imm32  # copy to ECX
 41 +transform: packing instruction 'b9 0x080490a7/imm32'
 42 +transform: instruction after packing: 'b9 a7 90 04 08'
 43 
 44 :(before "End Transforms")
 45 Transform.push_back(pack_operands);
 46 
 47 :(code)
 48 void pack_operands(program& p) {
 49   trace(99, "transform") << "-- pack operands" << end();
 50   if (p.segments.empty()) return;
 51   segment& code = p.segments.at(0);
 52   for (int i = 0;  i < SIZE(code.lines);  ++i) {
 53     line& inst = code.lines.at(i);
 54     if (all_hex_bytes(inst)) continue;
 55     trace(99, "transform") << "packing instruction '" << to_string(/*with metadata*/inst) << "'" << end();
 56     pack_operands(inst);
 57     trace(99, "transform") << "instruction after packing: '" << to_string(/*without metadata*/inst.words) << "'" << end();
 58   }
 59 }
 60 
 61 void pack_operands(line& inst) {
 62   line new_inst;
 63   add_opcodes(inst, new_inst);
 64   add_modrm_byte(inst, new_inst);
 65   add_sib_byte(inst, new_inst);
 66   add_disp_bytes(inst, new_inst);
 67   add_imm_bytes(inst, new_inst);
 68   inst.words.swap(new_inst.words);
 69 }
 70 
 71 void add_opcodes(const line& in, line& out) {
 72   out.words.push_back(in.words.at(0));
 73   if (in.words.at(0).data == "0f" || in.words.at(0).data == "f3")
 74     out.words.push_back(in.words.at(1));
 75   if (in.words.at(0).data == "f3" && in.words.at(1).data == "0f")
 76     out.words.push_back(in.words.at(2));
 77 }
 78 
 79 void add_modrm_byte(const line& in, line& out) {
 80   uint8_t mod=0, reg_subop=0, rm32=0;
 81   bool emit = false;
 82   for (int i = 0;  i < SIZE(in.words);  ++i) {
 83     const word& curr = in.words.at(i);
 84     if (has_metadata(curr, "mod")) {
 85       mod = hex_byte(curr.data);
 86       emit = true;
 87     }
 88     else if (has_metadata(curr, "rm32")) {
 89       rm32 = hex_byte(curr.data);
 90       emit = true;
 91     }
 92     else if (has_metadata(curr, "r32")) {
 93       reg_subop = hex_byte(curr.data);
 94       emit = true;
 95     }
 96     else if (has_metadata(curr, "subop")) {
 97       reg_subop = hex_byte(curr.data);
 98       emit = true;
 99     }
100   }
101   if (emit)
102     out.words.push_back(hex_byte_text((mod << 6) | (reg_subop << 3) | rm32));
103 }
104 
105 void add_sib_byte(const line& in, line& out) {
106   uint8_t scale=0, index=0, base=0;
107   bool emit = false;
108   for (int i = 0;  i < SIZE(in.words);  ++i) {
109     const word& curr = in.words.at(i);
110     if (has_metadata(curr, "scale")) {
111       scale = hex_byte(curr.data);
112       emit = true;
113     }
114     else if (has_metadata(curr, "index")) {
115       index = hex_byte(curr.data);
116       emit = true;
117     }
118     else if (has_metadata(curr, "base")) {
119       base = hex_byte(curr.data);
120       emit = true;
121     }
122   }
123   if (emit)
124     out.words.push_back(hex_byte_text((scale << 6) | (index << 3) | base));
125 }
126 
127 void add_disp_bytes(const line& in, line& out) {
128   for (int i = 0;  i < SIZE(in.words);  ++i) {
129     const word& curr = in.words.at(i);
130     if (has_metadata(curr, "disp8"))
131       emit_hex_bytes(out, curr, 1);
132     if (has_metadata(curr, "disp16"))
133       emit_hex_bytes(out, curr, 2);
134     else if (has_metadata(curr, "disp32"))
135       emit_hex_bytes(out, curr, 4);
136   }
137 }
138 
139 void add_imm_bytes(const line& in, line& out) {
140   for (int i = 0;  i < SIZE(in.words);  ++i) {
141     const word& curr = in.words.at(i);
142     if (has_metadata(curr, "imm8"))
143       emit_hex_bytes(out, curr, 1);
144     else if (has_metadata(curr, "imm32"))
145       emit_hex_bytes(out, curr, 4);
146   }
147 }
148 
149 void emit_hex_bytes(line& out, const word& w, int num) {
150   assert(num <= 4);
151   if (!is_hex_int(w.data)) {
152     out.words.push_back(w);
153     return;
154   }
155   emit_hex_bytes(out, static_cast<uint32_t>(parse_int(w.data)), num);
156 }
157 
158 void emit_hex_bytes(line& out, uint32_t val, int num) {
159   assert(num <= 4);
160   for (int i = 0;  i < num;  ++i) {
161     out.words.push_back(hex_byte_text(val & 0xff));
162     val = val >> 8;
163   }
164 }
165 
166 word hex_byte_text(uint8_t val) {
167   ostringstream out;
168   out << HEXBYTE << NUM(val);
169   word result;
170   result.data = out.str();
171   result.original = out.str()+"/auto";
172   return result;
173 }
174 
175 string to_string(const vector<word>& in) {
176   ostringstream out;
177   for (int i = 0;  i < SIZE(in);  ++i) {
178     if (i > 0) out << ' ';
179     out << in.at(i).data;
180   }
181   return out.str();
182 }
183 
184 :(scenario pack_immediate_constants_hex)
185 == 0x1
186 # instruction                     effective address                                                   operand     displacement    immediate
187 # op          subop               mod             rm32          base        index         scale       r32
188 # 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
189   bb                                                                                                                              0x2a/imm32        # copy 42 to EBX
190 +transform: packing instruction 'bb 0x2a/imm32'
191 +transform: instruction after packing: 'bb 2a 00 00 00'
192 +run: copy imm32 0x0000002a to EBX
193 
194 :(scenarios transform)
195 :(scenario pack_silently_ignores_non_hex)
196 == 0x1
197 # instruction                     effective address                                                   operand     displacement    immediate
198 # op          subop               mod             rm32          base        index         scale       r32
199 # 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
200   bb                                                                                                                              foo/imm32         # copy foo to EBX
201 +transform: packing instruction 'bb foo/imm32'
202 # no change (we're just not printing metadata to the trace)
203 +transform: instruction after packing: 'bb foo'
204 $error: 0
205 :(scenarios run)