Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9683 | turbocat | 1 | /**************************** disasm2.cpp ******************************** |
2 | * Author: Agner Fog |
||
3 | * Date created: 2007-02-25 |
||
4 | * Last modified: 2016-11-27 |
||
5 | * Project: objconv |
||
6 | * Module: disasm2.cpp |
||
7 | * Description: |
||
8 | * Module for disassembler containing file output functions |
||
9 | * |
||
10 | * Changes that relate to assembly language syntax should be done in this file only. |
||
11 | * |
||
12 | * Copyright 2007-2016 GNU General Public License http://www.gnu.org/licenses |
||
13 | *****************************************************************************/ |
||
14 | #include "stdafx.h" |
||
15 | |||
16 | /********************** Warning and error texts *************************** |
||
17 | These texts are inserted in disassembled code in case of warnings or errors. |
||
18 | |||
19 | The occurrence of an error makes the disassembler mark the code block between |
||
20 | the nearest known code labels as dubious. This means that the byte sequence |
||
21 | might be data in the code segment or the disassembler might be out of phase |
||
22 | with instruction boundaries. Dubious code will be shown both as code and as |
||
23 | data. |
||
24 | |||
25 | A warning will be shown as 'Note:' before the instruction it applies to. |
||
26 | This might indicate suboptimal coding or a possible cause for concern. |
||
27 | |||
28 | The criteria for distinguishing between warnings and errors is not the |
||
29 | severity of consequences, but whether the condition is likely to be caused |
||
30 | by common programming errors or by data in the code segment. |
||
31 | |||
32 | Some of the warning messages are quite benign, e.g. an unnecessary prefix. |
||
33 | Other warning messages can have severe consequences, e.g. a function missing |
||
34 | a return statement. |
||
35 | |||
36 | Still other warnings are no case for concern, but a condition requiring |
||
37 | attention. For example the message: "Multi-byte NOP. Replace with ALIGN", |
||
38 | might actually indicate a well optimized code. But it requires attention |
||
39 | because the assembler cannot re-create the multi-byte NOP if the code |
||
40 | is assembled again. The programmer needs to decide what level of alignment |
||
41 | is optimal and replace the NOP with an align statement. |
||
42 | |||
43 | *****************************************************************************/ |
||
44 | |||
45 | // Define error texts. |
||
46 | SIntTxt AsmErrorTexts[] = { |
||
47 | {1, "Instruction longer than 15 bytes"}, |
||
48 | {2, "Lock prefix not allowed for this opcode"}, |
||
49 | {4, "Illegal opcode"}, |
||
50 | {8, "Illegal operands for this opcode"}, |
||
51 | {0x10, "Instruction extends beyond end of code block"}, |
||
52 | {0x20, "Prefix after REX prefix not allowed"}, |
||
53 | {0x40, "This instruction is not allowed in 64 bit mode"}, |
||
54 | {0x80, "Instruction out of phase with next label"}, |
||
55 | {0x100, "Attempt to use R13 as base register without displacement"}, |
||
56 | {0x200, "Register 8 - 15 only allowed in 64 bit mode (Ignored)."}, |
||
57 | {0x400, "REX prefix not allowed on instruction with DREX byte"}, |
||
58 | {0x800, "VEX has X bit but no SIB byte (Probably ignored)"}, |
||
59 | {0x1000, "Relocation source does not match address or operand field"}, |
||
60 | {0x2000, "Overlapping relocations"}, |
||
61 | {0x4000, "This is unlikely to be code"}, // Consecutive bytes of 0 found |
||
62 | {0x8000, "VEX.L bit not allowed here"}, |
||
63 | {0x10000, "VEX.mmmm bits out of range"}, |
||
64 | {0x80000, "Internal error in opcode table in opcodes.cpp"} |
||
65 | }; |
||
66 | |||
67 | // Warning texts 1: Warnings about conditions that could be intentional and suboptimal code |
||
68 | SIntTxt AsmWarningTexts1[] = { |
||
69 | {1, "Immediate operand could be made smaller by sign extension"}, |
||
70 | {2, "Immediate operand could be made smaller by zero extension"}, |
||
71 | {4, "Zero displacement could be omitted"}, |
||
72 | {8, "Displacement could be made smaller by sign extension"}, |
||
73 | {0x10, "SIB byte unnecessary here"}, |
||
74 | {0x20, "A shorter instruction exists for register operand"}, |
||
75 | {0x40, "Length-changing prefix causes delay on Intel processors"}, |
||
76 | {0x80, "Address size prefix should be avoided"}, |
||
77 | {0x100, "Same prefix occurs more than once"}, |
||
78 | {0x200, "Prefix valid but unnecessary"}, |
||
79 | {0x400, "Prefix bit or byte has no meaning in this context"}, |
||
80 | {0x800, "Contradicting prefixes"}, |
||
81 | {0x1000, "Required prefix missing"}, |
||
82 | {0x2000, "Address has scale factor but no index register"}, |
||
83 | {0x4000, "Address is not rip-relative"}, |
||
84 | {0x8000, "Absolute memory address without relocation"}, |
||
85 | {0x10000, "Unusual relocation type for this operand"}, |
||
86 | {0x20000, "Instruction pointer truncated by operand size prefix"}, |
||
87 | {0x40000, "Stack pointer truncated by address size prefix"}, |
||
88 | {0x80000, "Jump or call to data segment not allowed"}, |
||
89 | {0x100000, "Undocumented opcode"}, |
||
90 | {0x200000, "Unknown opcode reserved for future extensions"}, |
||
91 | {0x400000, "Memory operand is misaligned. Performance penalty"}, |
||
92 | {0x800000, "Alignment fault. Memory operand must be aligned"}, |
||
93 | {0x1000000, "Multi-byte NOP. Replace with ALIGN"}, |
||
94 | {0x2000000, "Bogus length-changing prefix causes delay on Intel processors here"}, |
||
95 | {0x4000000, "Non-default size for stack operation"}, |
||
96 | {0x8000000, "Function does not end with ret or jmp"}, |
||
97 | {0x10000000, "No jump seems to point here"}, |
||
98 | {0x20000000, "Full 64-bit address"}, |
||
99 | {0x40000000, "VEX prefix bits not allowed here"} |
||
100 | }; |
||
101 | |||
102 | // Warning texts 2: Warnings about possible misinterpretation; serious warnings |
||
103 | SIntTxt AsmWarningTexts2[] = { |
||
104 | {1, "Label out of phase with instruction. Possibly spurious"}, |
||
105 | {2, "Planned future instruction, according to preliminary specification"}, |
||
106 | {4, "This instruction has been planned but never implemented because plans were changed. Will not work"}, |
||
107 | {0x10, "EVEX prefix not allowed for this instruction"}, |
||
108 | {0x20, "MVEX prefix not allowed for this instruction"}, |
||
109 | {0x40, "EVEX prefix option bits not allowed here"}, |
||
110 | {0x80, "MVEX prefix option bits not allowed here"}, |
||
111 | {0x100, "Mask register must be nonzero"}, |
||
112 | {0x200, "Broadcasting to scalar not allowd"}, |
||
113 | }; |
||
114 | |||
115 | |||
116 | // Indication of relocation types in comments: |
||
117 | SIntTxt RelocationTypeNames[] = { |
||
118 | {0x001, "(d)" }, // Direct address in flat address space |
||
119 | {0x002, "(rel)" }, // Self-relative |
||
120 | {0x004, "(imgrel)" }, // Image-relative |
||
121 | {0x008, "(segrel)" }, // Segment-relative |
||
122 | {0x010, "(refpoint)" }, // Relative to arbitrary point (position-independent code in Mach-O) |
||
123 | {0x021, "(d)" }, // Direct (adjust by image base) |
||
124 | {0x041, "(d)" }, // Direct (make procecure linkage table entry) |
||
125 | {0x081, "(indirect)" }, // Gnu indirect function dispatcher (make procecure linkage table entry?) |
||
126 | {0x100, "(seg)" }, // Segment address or descriptor |
||
127 | {0x200, "(sseg)" }, // Segment of symbol |
||
128 | {0x400, "(far)" }, // Far segment:offset address |
||
129 | {0x1001, "(GOT)" }, // GOT entry |
||
130 | {0x1002, "(GOT r)" }, // self-relative to GOT entry |
||
131 | {0x2002, "(PLT r)" } // self-relative to PLT entry |
||
132 | }; |
||
133 | |||
134 | // Instruction set names |
||
135 | const char * InstructionSetNames[] = { |
||
136 | "8086", "80186", "80286", "80386", // 0 - 3 |
||
137 | "80486", "Pentium", "Pentium Pro", "MMX", // 4 - 7 |
||
138 | "Pentium II", "", "", "", // 8 - B |
||
139 | "", "", "", "", // C - F |
||
140 | "", "SSE", "SSE2", "SSE3", // 10 - 13 |
||
141 | "Supplementary SSE3", "SSE4.1", "SSE4.2", "AES", // 14 - 17 |
||
142 | "CLMUL", "AVX", "FMA3", "?", // 18 - 1B |
||
143 | "AVX2", "BMI etc.", "?", "?", // 1C - 1F |
||
144 | "AVX-512", "AVX512PF/ER/CD", "MPX,SHA,TBD", "AVX512IFMA/VBMI", // 20 - 23 |
||
145 | "AVX512_4FMAPS", "?", "?", "?", // 24 - 27 |
||
146 | "?", "?", "?", "?", // 28 - 2B |
||
147 | "?", "?", "?", "?", // 2C - 2F |
||
148 | "?", "?", "?", "?", // 30 - 33 |
||
149 | "?", "?", "?", "?", // 34 - 37 |
||
150 | "?", "?", "?", "?", // 38 - 3B |
||
151 | "?", "?", "?", "?", // 3C - 3F |
||
152 | "?", "?", "?", "?", // 40 - 43 |
||
153 | "?", "?", "?", "?", // 44 - 47 |
||
154 | "?", "?", "?", "?", // 48 - 4B |
||
155 | "?", "?", "?", "?", // 4C - 4F |
||
156 | "?", "?", "?", "?", // 50 - 53 |
||
157 | "?", "?", "?", "?", // 54 - 57 |
||
158 | "?", "?", "?", "?", // 58 - 5B |
||
159 | "?", "?", "?", "?", // 5C - 5F |
||
160 | "?", "?", "?", "?", // 60 - 63 |
||
161 | "?", "?", "?", "?", // 64 - 67 |
||
162 | "?", "?", "?", "?", // 68 - 6B |
||
163 | "?", "?", "?", "?", // 6C - 6F |
||
164 | "?", "?", "?", "?", // 70 - 73 |
||
165 | "?", "?", "?", "?", // 74 - 77 |
||
166 | "?", "?", "?", "?", // 78 - 7B |
||
167 | "?", "?", "?", "?", // 7C - 7F |
||
168 | "Knights Corner", "?", "?", "?", // 80 - 83 |
||
169 | "?", "?", "?", "?" // 84 - 87 |
||
170 | }; |
||
171 | |||
172 | const int InstructionSetNamesLen = TableSize(InstructionSetNames); |
||
173 | |||
174 | |||
175 | /************************** class CDisassembler ***************************** |
||
176 | Most member functions of CDisassembler are defined in disasm1.cpp |
||
177 | |||
178 | Only the functions that produce output are defined here: |
||
179 | ******************************************************************************/ |
||
180 | |||
181 | void CDisassembler::WriteShortRegOperand(uint32 Type) { |
||
182 | // Write register operand from lower 3 bits of opcode byte to OutFile |
||
183 | uint32 rnum = Get |
||
184 | // Check REX.B prefix |
||
185 | if (s.Prefixes[7] & 1) rnum |= 8; // Add 8 if REX.B prefix |
||
186 | // Write register name |
||
187 | WriteRegisterName(rnum, Type); |
||
188 | } |
||
189 | |||
190 | void CDisassembler::WriteRegOperand(uint32 Type) { |
||
191 | // Write register operand from reg bits |
||
192 | uint32 Num = s.Reg; // Register number |
||
193 | |||
194 | // Write register name |
||
195 | WriteRegisterName(Num, Type); |
||
196 | } |
||
197 | |||
198 | void CDisassembler::WriteRMOperand(uint32 Type) { |
||
199 | // Write memory or register operand from mod/rm bits of mod/reg/rm byte |
||
200 | // and possibly SIB byte or direct memory operand to OutFile. |
||
201 | // Also used for writing direct memory operand |
||
202 | |||
203 | if ((Type & 0xFF) == 0) { |
||
204 | // No explicit operand |
||
205 | return; |
||
206 | } |
||
207 | |||
208 | uint32 Components = 0; // Count number of addends inside [] |
||
209 | int64 Addend = 0; // Inline displacement or addend |
||
210 | int AddressingMode = 0; // 0: 16- or 32 bit addressing mode |
||
211 | // 1: 64-bit pointer |
||
212 | // 2: 32-bit absolute in 64-bit mode |
||
213 | // 4: 64-bit rip-relative |
||
214 | // 8: 64-bit absolute |
||
215 | // Check if register or memory |
||
216 | if (s.Mod == 3) { |
||
217 | // Register operand |
||
218 | WriteRegisterName(s.RM, Type); |
||
219 | return; |
||
220 | } |
||
221 | |||
222 | // Find addend, if any |
||
223 | switch (s.AddressFieldSize) { |
||
224 | case 1: // 1 byte displacement |
||
225 | Addend = Get |
||
226 | break; |
||
227 | case 2: // 2 bytes displacement |
||
228 | Addend = Get |
||
229 | break; |
||
230 | case 4: // 4 bytes displacement |
||
231 | Addend = Get |
||
232 | if ((s.MFlags & 0x100) && !s.AddressRelocation) { |
||
233 | // rip-relative |
||
234 | Addend += ImageBase + uint64(SectionAddress + IEnd); |
||
235 | } |
||
236 | break; |
||
237 | case 8: // 8 bytes address |
||
238 | Addend = Get |
||
239 | break; |
||
240 | } |
||
241 | // Get AddressingMode |
||
242 | if (s.AddressSize > 32) { |
||
243 | if (s.MFlags & 0x100) { |
||
244 | AddressingMode = 4; // 64-bit rip-relative |
||
245 | } |
||
246 | else if (s.AddressFieldSize == 8) { |
||
247 | AddressingMode = 8; // 64-bit absolute |
||
248 | } |
||
249 | else if (s.AddressRelocation || (s.BaseReg==0 && s.IndexReg==0)) { |
||
250 | AddressingMode = 2; // 32-bit absolute in 64-bit mode |
||
251 | } |
||
252 | else { |
||
253 | AddressingMode = 1; // 64-bit pointer |
||
254 | } |
||
255 | } |
||
256 | |||
257 | // Make exception for LEA with no type |
||
258 | if (Opcodei == 0x8D) { |
||
259 | Type = 0; |
||
260 | } |
||
261 | // Write type override |
||
262 | if ((s.OpcodeDef->InstructionFormat & 0x1F) == 0x1E) { |
||
263 | WriteOperandType(Type & 0xFF); // has vsib address: write element type rather than vector type |
||
264 | } |
||
265 | else if (!(s.OpcodeDef->Options & 0x800)) { |
||
266 | WriteOperandType(Type); // write operand type |
||
267 | } |
||
268 | |||
269 | if (Syntax != SUBTYPE_MASM) { |
||
270 | // Write "[" around memory operands, before segment |
||
271 | OutFile.Put("["); |
||
272 | } |
||
273 | |||
274 | // Write segment prefix, if any |
||
275 | if (s.Prefixes[0]) { |
||
276 | OutFile.Put(RegisterNamesSeg[GetSegmentRegisterFromPrefix()]); |
||
277 | OutFile.Put(":"); |
||
278 | } |
||
279 | else if (!s.BaseReg && !s.IndexReg && (!s.AddressRelocation || (s.Warnings1 & 0x10000)) && Syntax != SUBTYPE_YASM) { |
||
280 | // No pointer register and no memory reference or wrong type of memory reference. |
||
281 | // Write segment register to indicate that we have a memory operand |
||
282 | OutFile.Put("DS:"); |
||
283 | } |
||
284 | |||
285 | if (Syntax == SUBTYPE_MASM) { |
||
286 | // Write "[" around memory operands, after segment |
||
287 | OutFile.Put("["); |
||
288 | } |
||
289 | |||
290 | if (Syntax == SUBTYPE_YASM && (AddressingMode & 0x0E)) { |
||
291 | // Specify absolute or relative addressing mode |
||
292 | switch (AddressingMode) { |
||
293 | case 2: OutFile.Put("abs "); break; |
||
294 | case 4: OutFile.Put("rel "); break; |
||
295 | case 8: OutFile.Put("abs qword "); break; |
||
296 | } |
||
297 | } |
||
298 | |||
299 | // Write relocation target, if any |
||
300 | if (s.AddressRelocation) { |
||
301 | // Write cross reference |
||
302 | WriteRelocationTarget(s.AddressRelocation, 4 | (s.MFlags & 0x100), Addend); |
||
303 | // Addend has been written, don't write it again |
||
304 | Addend = 0; |
||
305 | // Remember that something has been written |
||
306 | Components++; |
||
307 | } |
||
308 | |||
309 | // Check address size for pointer registers |
||
310 | //const char * * PointerRegisterNames; |
||
311 | uint32 RegisterType = 0; |
||
312 | switch (s.AddressSize) { |
||
313 | case 16: |
||
314 | RegisterType = 2; break; |
||
315 | case 32: |
||
316 | RegisterType = 3; break; |
||
317 | case 64: |
||
318 | RegisterType = 4; break; |
||
319 | } |
||
320 | |||
321 | // Write base register, if any |
||
322 | if (s.BaseReg) { |
||
323 | if (Components++) OutFile.Put("+"); // Put "+" if anything before |
||
324 | WriteRegisterName(s.BaseReg - 1, RegisterType); |
||
325 | } |
||
326 | |||
327 | // Write index register, if any |
||
328 | if (s.IndexReg) { |
||
329 | if (Components++) OutFile.Put("+"); // Put "+" if anything before |
||
330 | if ((s.OpcodeDef->InstructionFormat & 0x1F) != 0x1E) { |
||
331 | // normal index register |
||
332 | WriteRegisterName(s.IndexReg - 1, RegisterType); |
||
333 | } |
||
334 | else { |
||
335 | // VSIB byte specifies vector index register |
||
336 | WriteRegisterName(s.IndexReg - 1, Type & 0xF00); |
||
337 | } |
||
338 | // Write scale factor, if any |
||
339 | if (s.Scale) { |
||
340 | OutFile.Put("*"); |
||
341 | OutFile.PutDecimal(1 << s.Scale); |
||
342 | } |
||
343 | } |
||
344 | |||
345 | // Write +/- before addend |
||
346 | if (Components && Addend) { |
||
347 | // Displacement comes after base/index registers |
||
348 | if (Addend >= 0 || s.AddressFieldSize == 8) { |
||
349 | // Positive. Write + |
||
350 | OutFile.Put("+"); |
||
351 | } |
||
352 | else { |
||
353 | // Negative. Write - |
||
354 | OutFile.Put("-"); |
||
355 | Addend = -Addend; |
||
356 | } |
||
357 | } |
||
358 | |||
359 | if (Addend || Components == 0) { |
||
360 | // Find minimum number of digits needed |
||
361 | uint32 AddendSize = s.AddressFieldSize; |
||
362 | if ((uint64)Addend < 0x100 && AddendSize > 1) AddendSize = 1; |
||
363 | else if ((uint64)Addend < 0x10000 && AddendSize > 2) AddendSize = 2; |
||
364 | |||
365 | // Write address or addend as hexadecimal |
||
366 | OutFile.PutHex((uint64)Addend, 2); |
||
367 | |||
368 | // Check if offset multiplier needed |
||
369 | if (s.OffsetMultiplier && s.AddressFieldSize == 1 && Addend) { |
||
370 | OutFile.Put("*"); |
||
371 | OutFile.PutHex(s.OffsetMultiplier, 2); |
||
372 | } |
||
373 | } |
||
374 | |||
375 | if (Syntax == SUBTYPE_GASM && (AddressingMode == 4)) { |
||
376 | // Need to specify rip-relative address |
||
377 | OutFile.Put("+rip"); |
||
378 | } |
||
379 | |||
380 | // End with "]" |
||
381 | OutFile.Put("]"); |
||
382 | } |
||
383 | |||
384 | |||
385 | void CDisassembler::WriteOperandType(uint32 type) { |
||
386 | switch (Syntax) { |
||
387 | case SUBTYPE_MASM: |
||
388 | WriteOperandTypeMASM(type); break; |
||
389 | case SUBTYPE_YASM: |
||
390 | WriteOperandTypeYASM(type); break; |
||
391 | case SUBTYPE_GASM: |
||
392 | WriteOperandTypeGASM(type); break; |
||
393 | } |
||
394 | } |
||
395 | |||
396 | void CDisassembler::WriteOperandTypeMASM(uint32 type) { |
||
397 | // Write type override before operand, e.g. "dword ", MASM syntax |
||
398 | if (type & 0xF00) { |
||
399 | type &= 0xF00; // Ignore element type for vectors |
||
400 | } |
||
401 | else { |
||
402 | type &= 0xFF; // Use operand type only |
||
403 | } |
||
404 | |||
405 | switch (type) { |
||
406 | case 1: // 8 bits |
||
407 | OutFile.Put("byte "); break; |
||
408 | case 2: // 16 bits |
||
409 | OutFile.Put("word "); break; |
||
410 | case 3: // 32 bits |
||
411 | OutFile.Put("dword "); break; |
||
412 | case 4: // 64 bits |
||
413 | OutFile.Put("qword "); break; |
||
414 | case 5: // 80 bits |
||
415 | if ((s.OpcodeDef->Destination & 0xFF) == 0xD) { |
||
416 | // 64+16 bit far pointer. Not supported by MASM |
||
417 | OutFile.Put("fword "); |
||
418 | s.OpComment = "64+16 bit. Need REX.W prefix"; |
||
419 | } |
||
420 | else { |
||
421 | OutFile.Put("tbyte ");} |
||
422 | break; |
||
423 | case 6: case 0x40: case 0x48: case 0: |
||
424 | // Other size. Write nothing |
||
425 | break; |
||
426 | case 7: case 0x0D: // 48 bits or far |
||
427 | OutFile.Put("fword "); |
||
428 | if ((s.OpcodeDef->Destination & 0xFF) == 0xD && WordSize == 64) { |
||
429 | // All assemblers I have tried forget the REX.W prefix here. Make a notice |
||
430 | s.OpComment = "32+16 bit. Possibly forgot REX.W prefix"; |
||
431 | } |
||
432 | break; |
||
433 | case 0x4A: // 16 bits float |
||
434 | OutFile.Put("word "); break; |
||
435 | case 0x43: // 32 bits float (x87) |
||
436 | case 0x4B: // 32 bits float (SSE2) |
||
437 | OutFile.Put("dword "); break; |
||
438 | case 0x44: // 64 bits float |
||
439 | case 0x4C: // 64 bits float (SSE2) |
||
440 | OutFile.Put("qword "); break; |
||
441 | case 0x45: // 80 bits float |
||
442 | OutFile.Put("tbyte "); break; |
||
443 | case 0x84: case 0x85: // far call |
||
444 | OutFile.Put("far "); break; |
||
445 | case 0x95: // 16 bits mask register |
||
446 | OutFile.Put("word "); break; |
||
447 | case 0x300: // MMX |
||
448 | OutFile.Put("qword "); break; |
||
449 | case 0x400: // XMM |
||
450 | OutFile.Put("xmmword "); break; |
||
451 | case 0x500: // YMM |
||
452 | OutFile.Put("ymmword "); break; |
||
453 | case 0x600: // ZMM |
||
454 | OutFile.Put("zmmword "); break; |
||
455 | case 0x700: // future 1024 bit |
||
456 | OutFile.Put("?mmword "); break; |
||
457 | } |
||
458 | OutFile.Put("ptr "); |
||
459 | } |
||
460 | |||
461 | void CDisassembler::WriteOperandTypeYASM(uint32 type) { |
||
462 | // Write type override before operand, e.g. "dword", NASM/YASM syntax |
||
463 | if (type & 0xF00) { |
||
464 | type &= 0xF00; // Ignore element type for vectors |
||
465 | } |
||
466 | else { |
||
467 | type &= 0xFF; // Use operand type only |
||
468 | } |
||
469 | uint32 Dest = s.OpcodeDef->Destination & 0xFF;// Destination operand |
||
470 | if (Dest >= 0xB && Dest < 0x10) { |
||
471 | // This is a pointer |
||
472 | if (Dest < 0x0D) { |
||
473 | OutFile.Put("near "); // Near indirect jump/call |
||
474 | } |
||
475 | else { |
||
476 | // Far pointer |
||
477 | if ((WordSize == 16 && type == 3) || (WordSize == 32 && type == 7)) { |
||
478 | OutFile.Put("far "); |
||
479 | } |
||
480 | else { |
||
481 | // Size currently not supported by YASM |
||
482 | switch (type) { |
||
483 | case 3: OutFile.Put("far "); |
||
484 | s.OpComment = "16+16 bit. Needs 66H prefix"; |
||
485 | break; |
||
486 | case 7: OutFile.Put("far "); |
||
487 | s.OpComment = "32+16 bit. Possibly forgot REX.W prefix"; |
||
488 | break; |
||
489 | case 5: OutFile.Put("far "); |
||
490 | s.OpComment = "64+16 bit. Needs REX.W prefix"; |
||
491 | break; |
||
492 | } |
||
493 | } |
||
494 | } |
||
495 | return; |
||
496 | } |
||
497 | switch (type) { |
||
498 | case 1: // 8 bits |
||
499 | OutFile.Put("byte "); break; |
||
500 | case 2: // 16 bits |
||
501 | OutFile.Put("word "); break; |
||
502 | case 3: // 32 bits |
||
503 | OutFile.Put("dword "); break; |
||
504 | case 4: // 64 bits |
||
505 | OutFile.Put("qword "); break; |
||
506 | case 5: // 80 bits |
||
507 | OutFile.Put("tbyte "); break; |
||
508 | case 7: // 48 bits |
||
509 | OutFile.Put("fword "); break; |
||
510 | case 0x4A: // 16 bits float |
||
511 | OutFile.Put("word "); break; |
||
512 | case 0x43: // 32 bits float (x87) |
||
513 | case 0x4B: // 32 bits float (SSE2) |
||
514 | OutFile.Put("dword "); break; |
||
515 | case 0x44: // 64 bits float |
||
516 | case 0x4C: // 64 bits float (SSE2) |
||
517 | OutFile.Put("qword "); break; |
||
518 | case 0x45: // 80 bits float |
||
519 | OutFile.Put("tbyte "); break; |
||
520 | case 0x84: case 0x85: // far call |
||
521 | OutFile.Put("far "); break; |
||
522 | case 0x95: // 16 bits mask register |
||
523 | OutFile.Put("word "); break; |
||
524 | case 0x300: // MMX |
||
525 | OutFile.Put("qword "); break; |
||
526 | case 0x400: // XMM |
||
527 | OutFile.Put("oword "); break; |
||
528 | case 0x500: // YMM |
||
529 | OutFile.Put("yword "); break; |
||
530 | case 0x600: // ZMM |
||
531 | OutFile.Put("zword "); break; |
||
532 | case 0x700: // Future 128 bytes |
||
533 | OutFile.Put("?word "); break; |
||
534 | default:; // Anything else: write nothing |
||
535 | } |
||
536 | } |
||
537 | |||
538 | void CDisassembler::WriteOperandTypeGASM(uint32 type) { |
||
539 | // Write type override before operand, e.g. "dword ", GAS syntax |
||
540 | if (type & 0xF00) { |
||
541 | type &= 0xF00; // Ignore element type for vectors |
||
542 | } |
||
543 | else { |
||
544 | type &= 0xFF; // Use operand type only |
||
545 | } |
||
546 | |||
547 | switch (type) { |
||
548 | case 1: // 8 bits |
||
549 | OutFile.Put("byte "); break; |
||
550 | case 2: // 16 bits |
||
551 | OutFile.Put("word "); break; |
||
552 | case 3: // 32 bits |
||
553 | OutFile.Put("dword "); break; |
||
554 | case 4: // 64 bits |
||
555 | OutFile.Put("qword "); break; |
||
556 | case 5: // 80 bits |
||
557 | if ((s.OpcodeDef->Destination & 0xFF) == 0xD) { |
||
558 | // 64+16 bit far pointer. Not supported by Gas |
||
559 | OutFile.Put("fword "); |
||
560 | s.OpComment = "64+16 bit. Needs REX.W prefix"; |
||
561 | } |
||
562 | else { |
||
563 | OutFile.Put("tbyte ");} |
||
564 | break; |
||
565 | case 6: case 0x40: case 0x48: case 0: |
||
566 | // Other size. Write nothing |
||
567 | break; |
||
568 | case 7: // 48 bits |
||
569 | OutFile.Put("fword "); |
||
570 | if ((s.OpcodeDef->Destination & 0xFF) == 0xD && WordSize == 64) { |
||
571 | // All assemblers I have tried forget the REX.W prefix here. Make a notice |
||
572 | s.OpComment = "32+16 bit. Possibly forgot REX.W prefix"; |
||
573 | } |
||
574 | break; |
||
575 | case 0x4A: // 16 bits float |
||
576 | OutFile.Put("word "); break; |
||
577 | case 0x43: // 32 bits float (x87) |
||
578 | case 0x4B: // 32 bits float (SSE2) |
||
579 | OutFile.Put("dword "); break; |
||
580 | case 0x44: // 64 bits float |
||
581 | case 0x4C: // 64 bits float (SSE2) |
||
582 | OutFile.Put("qword "); break; |
||
583 | case 0x45: // 80 bits float |
||
584 | OutFile.Put("tbyte "); break; |
||
585 | case 0x84: case 0x85: // far call |
||
586 | OutFile.Put("far "); break; |
||
587 | case 0x95: // 16 bits mask register |
||
588 | OutFile.Put("word "); break; |
||
589 | case 0x300: // MMX |
||
590 | OutFile.Put("qword "); break; |
||
591 | case 0x400: // XMM |
||
592 | OutFile.Put("xmmword "); break; |
||
593 | case 0x500: // YMM |
||
594 | OutFile.Put("ymmword "); break; |
||
595 | case 0x600: // ZMM |
||
596 | OutFile.Put("zmmword "); break; |
||
597 | case 0x700: // future 1024 bit |
||
598 | OutFile.Put("?mmword "); break; |
||
599 | } |
||
600 | } |
||
601 | |||
602 | |||
603 | void CDisassembler::WriteDREXOperand(uint32 Type) { |
||
604 | // Write register operand from dest bits of DREX byte (AMD only) |
||
605 | uint32 Num = s.Vreg >> 4; // Register number |
||
606 | // Write register name |
||
607 | WriteRegisterName(Num, Type); |
||
608 | } |
||
609 | |||
610 | void CDisassembler::WriteVEXOperand(uint32 Type, int i) { |
||
611 | // Write register operand from VEX.vvvv bits or immediate bits |
||
612 | uint32 Num; // Register number |
||
613 | switch (i) { |
||
614 | case 0: // Use VEX.vvvv bits |
||
615 | Num = s.Vreg & 0x1F; break; |
||
616 | case 1: // Use immediate bits 4-7 |
||
617 | Num = Get |
||
618 | case 2: // Use immediate bits 0-3 (Unused. For possible future use) |
||
619 | Num = Get |
||
620 | default: |
||
621 | Num = 0; |
||
622 | } |
||
623 | // Write register name |
||
624 | WriteRegisterName(Num, Type); |
||
625 | } |
||
626 | |||
627 | |||
628 | void CDisassembler::WriteOperandAttributeEVEX(int i, int isMem) { |
||
629 | // Write operand attributes and instruction attributes from EVEX z, LL, b and aaa bits |
||
630 | // i = operand number (0 = destination, 1 = first source, 2 = second source, |
||
631 | // 98 = after last SIMD operand, 99 = after last operand) |
||
632 | // isMem: true if memory operand, false if register operand |
||
633 | uint32 swiz = s.OpcodeDef->EVEX; // indicates meaning of EVEX attribute bits |
||
634 | |||
635 | if ((swiz & 0x30) && (i == 0 || (s.OpcodeDef->Destination == 0 && i == 1))) { // first operand |
||
636 | // write mask |
||
637 | if (s.Kreg || (swiz & 0xC0)) { |
||
638 | OutFile.Put(" {k"); |
||
639 | OutFile.PutDecimal(s.Kreg); |
||
640 | OutFile.Put("}"); |
||
641 | if ((swiz & 0x20) && (s.Esss & 8)) { |
||
642 | // zeroing |
||
643 | OutFile.Put("{z}"); |
||
644 | } |
||
645 | } |
||
646 | } |
||
647 | if (swiz & 0x07) { |
||
648 | // broadcast, rounding or sae allowed |
||
649 | if (isMem && i < 8) { |
||
650 | // memory operand |
||
651 | if ((swiz & 0x01) && (s.Esss & 1)) { |
||
652 | // write memory broadcast |
||
653 | // calculate broadcast factor |
||
654 | uint32 op = s.Operands[i]; // operand |
||
655 | uint32 elementsize = GetDataElementSize(op); // element size |
||
656 | uint32 opv = s.Operands[0]; // any vector operand |
||
657 | if (!(opv & 0xF00)) opv = s.Operands[1]; // first operand is not a vector, use next |
||
658 | uint32 vectorsize = GetDataItemSize(opv); // vector size |
||
659 | if (vectorsize > elementsize) { // avoid broadcasting to scalar |
||
660 | if (elementsize) { // avoid division by zero |
||
661 | OutFile.Put(" {1to"); |
||
662 | OutFile.PutDecimal(vectorsize/elementsize); |
||
663 | OutFile.Put("}"); |
||
664 | } |
||
665 | else { |
||
666 | OutFile.Put("{unknown broadcast}"); |
||
667 | } |
||
668 | } |
||
669 | } |
||
670 | } |
||
671 | if (i == 98 && s.Mod == 3) { // after last SIMD operand. no memory operand |
||
672 | // NASM has rounding mode and sae decoration after last SIMD operand with a comma. |
||
673 | // No spec. for other assemblers available yet (2014). |
||
674 | // use i == 99 if it should be placed after last operand. |
||
675 | // Perhaps the comma should be removed for other assemblers? |
||
676 | if ((swiz & 0x4) && (s.Esss & 1)) { |
||
677 | // write rounding mode |
||
678 | uint32 rounding = (s.Esss >> 1) & 3; |
||
679 | OutFile.Put(", {"); |
||
680 | OutFile.Put(EVEXRoundingNames[rounding]); |
||
681 | OutFile.Put("}"); |
||
682 | } |
||
683 | else if ((swiz & 0x2) && (s.Esss & 1)) { |
||
684 | // no rounding mode. write sae |
||
685 | OutFile.Put(", {"); |
||
686 | OutFile.Put(EVEXRoundingNames[4]); |
||
687 | OutFile.Put("}"); |
||
688 | } |
||
689 | } |
||
690 | } |
||
691 | } |
||
692 | |||
693 | |||
694 | void CDisassembler::WriteOperandAttributeMVEX(int i, int isMem) { |
||
695 | // Write operand attributes and instruction attributes from MVEX sss, e and kkk bits. |
||
696 | // i = operand number (0 = destination, 1 = first source, 2 = second source, 99 = after last operand) |
||
697 | // isMem: true if memory operand, false if register operand |
||
698 | uint32 swiz = s.OpcodeDef->MVEX; // indicates meaning of MVEX attribute bits |
||
699 | const int R_sae_syntax = 0; // syntax alternatives for rounding mode + sae |
||
700 | // 0: {rn-sae}, 1: {rn}{sae} |
||
701 | const char * text = 0; // temporary text pointer |
||
702 | |||
703 | if ((swiz & 0x1000) && (i == 0 || (s.OpcodeDef->Destination == 0 && i == 1))) { // first operand |
||
704 | // write mask |
||
705 | if (s.Kreg || (swiz & 0x2000)) { |
||
706 | OutFile.Put(" {k"); |
||
707 | OutFile.PutDecimal(s.Kreg); |
||
708 | OutFile.Put("}"); |
||
709 | } |
||
710 | } |
||
711 | if (swiz & 0x1F) { |
||
712 | // swizzle allowed |
||
713 | if (isMem && i < 90) { |
||
714 | // write memory broadcast/up/down conversion |
||
715 | text = s.SwizRecord->name; |
||
716 | if (text && *text) { |
||
717 | OutFile.Put(" {"); OutFile.Put(text); OutFile.Put("}"); |
||
718 | } |
||
719 | } |
||
720 | //if (i == 2 || ((s.OpcodeDef->Source2 & 0xF0F00) == 0 && i == 1)) { |
||
721 | if (i == 98) { // after last SIMD operand |
||
722 | // last register or memory operand |
||
723 | if (s.Mod == 3 && !((swiz & 0x700) && (s.Esss & 8))) { // skip alternative meaning of sss field for register operand when E=1 |
||
724 | // write register swizzle |
||
725 | text = s.SwizRecord->name; |
||
726 | if (text && *text) { |
||
727 | OutFile.Put(" {"); OutFile.Put(text); OutFile.Put("}"); |
||
728 | } |
||
729 | } |
||
730 | } |
||
731 | if (i == 99) { // after last operand |
||
732 | if (s.Mod == 3 && (swiz & 0x300) && (s.Esss & 8)) { |
||
733 | // alternative meaning of sss field for register operand when E=1 |
||
734 | switch (swiz & 0x300) { |
||
735 | case 0x100: // rounding mode and not sae |
||
736 | text = SwizRoundTables[0][0][s.Esss & 3].name; |
||
737 | break; |
||
738 | case 0x200: // suppress all exceptions |
||
739 | if ((s.Esss & 4) && !(swiz & 0x800)) text = "sae"; |
||
740 | break; |
||
741 | case 0x300: // rounding mode and sae |
||
742 | text = SwizRoundTables[0][R_sae_syntax][s.Esss & 7].name; |
||
743 | break; |
||
744 | } |
||
745 | } |
||
746 | if (text && *text) { |
||
747 | OutFile.Put(", {"); OutFile.Put(text); OutFile.Put("}"); |
||
748 | } |
||
749 | } |
||
750 | } |
||
751 | if (isMem && (s.Esss & 8) && !(swiz & 0x800)) { |
||
752 | // cache eviction hint after memory operand |
||
753 | OutFile.Put(" {eh}"); |
||
754 | } |
||
755 | } |
||
756 | |||
757 | void CDisassembler::WriteRegisterName(uint32 Value, uint32 Type) { |
||
758 | // Write name of register to OutFile |
||
759 | if (Type & 0xF00) { |
||
760 | // vector register |
||
761 | Type &= 0xF00; |
||
762 | } |
||
763 | else { |
||
764 | // Other register |
||
765 | Type &= 0xFF; // Remove irrelevant bits |
||
766 | } |
||
767 | |||
768 | // Check fixed registers (do not depend on Value) |
||
769 | switch (Type) { |
||
770 | case 0xA1: // al |
||
771 | Type = 1; Value = 0; |
||
772 | break; |
||
773 | |||
774 | case 0xA2: // ax |
||
775 | Type = 2; Value = 0; |
||
776 | break; |
||
777 | |||
778 | case 0xA3: // eax |
||
779 | Type = 3; Value = 0; |
||
780 | break; |
||
781 | |||
782 | case 0xA4: // rax |
||
783 | Type = 4; Value = 0; |
||
784 | break; |
||
785 | |||
786 | case 0xAE: // xmm0 |
||
787 | Type = 0x400; Value = 0; |
||
788 | break; |
||
789 | |||
790 | case 0xAF: // st(0) |
||
791 | Type = 0x40; Value = 0; |
||
792 | break; |
||
793 | |||
794 | case 0xB2: // dx |
||
795 | Type = 2; Value = 2; |
||
796 | break; |
||
797 | |||
798 | case 0xB3: // cl |
||
799 | Type = 1; Value = 1; |
||
800 | break; |
||
801 | } |
||
802 | |||
803 | // Get register number limit |
||
804 | uint32 RegNumLimit = 7; // largest register number |
||
805 | if (WordSize >= 64) { |
||
806 | RegNumLimit = 15; |
||
807 | if ((s.Prefixes[6] & 0x40) && (Type & 0xF40)) { |
||
808 | // EVEX or MVEX prefix and vector |
||
809 | RegNumLimit = 31; |
||
810 | } |
||
811 | } |
||
812 | |||
813 | switch (Type) { |
||
814 | case 0x91: // segment register |
||
815 | RegNumLimit = 5; |
||
816 | break; |
||
817 | case 0x300: // mmx |
||
818 | case 0x40: // st register |
||
819 | case 0x95: // k mask register |
||
820 | RegNumLimit = 7; |
||
821 | break; |
||
822 | case 0x98: // bounds register |
||
823 | RegNumLimit = 3; |
||
824 | break; |
||
825 | } |
||
826 | if (Value > RegNumLimit) { |
||
827 | // register number out of range |
||
828 | OutFile.Put("unknown register "); |
||
829 | switch (Type) { |
||
830 | case 1: |
||
831 | OutFile.Put("(8 bit) "); break; |
||
832 | case 2: |
||
833 | OutFile.Put("(16 bit) "); break; |
||
834 | case 3: |
||
835 | OutFile.Put("(32 bit) "); break; |
||
836 | case 4: |
||
837 | OutFile.Put("(64 bit) "); break; |
||
838 | case 0x40: // st register |
||
839 | OutFile.Put("st"); break; |
||
840 | case 0x91: // Segment register |
||
841 | OutFile.Put("seg"); break; |
||
842 | case 0x92: // Control register |
||
843 | OutFile.Put("cr"); break; |
||
844 | case 0x95: // k mask register |
||
845 | OutFile.Put("k"); break; |
||
846 | case 0x300: // mmx register |
||
847 | OutFile.Put("mm"); break; |
||
848 | case 0x400: // xmm register |
||
849 | OutFile.Put("xmm"); break; |
||
850 | case 0x500: // ymm register |
||
851 | OutFile.Put("ymm"); break; |
||
852 | case 0x600: // zmm register |
||
853 | OutFile.Put("zmm"); break; |
||
854 | case 0x700: // future 1024 bit register |
||
855 | OutFile.Put("?mm"); break; |
||
856 | } |
||
857 | OutFile.PutDecimal(Value); |
||
858 | } |
||
859 | else { |
||
860 | // Write register name depending on type |
||
861 | switch (Type) { |
||
862 | case 1: // 8 bit register. Depends on any REX prefix |
||
863 | OutFile.Put(s.Prefixes[7] ? RegisterNames8x[Value] : RegisterNames8[Value & 7]); |
||
864 | break; |
||
865 | |||
866 | case 2: // 16 bit register |
||
867 | OutFile.Put(RegisterNames16[Value]); |
||
868 | break; |
||
869 | |||
870 | case 3: // 32 bit register |
||
871 | OutFile.Put(RegisterNames32[Value]); |
||
872 | break; |
||
873 | |||
874 | case 4: // 64 bit register |
||
875 | OutFile.Put(RegisterNames64[Value]); |
||
876 | break; |
||
877 | |||
878 | case 0x300: // mmx register |
||
879 | OutFile.Put("mm"); |
||
880 | OutFile.PutDecimal(Value); |
||
881 | break; |
||
882 | |||
883 | case 0x400: // xmm register (packed integer or float) |
||
884 | case 0x48: case 0x4B: case 0x4C: // xmm register (scalar float) |
||
885 | OutFile.Put("xmm"); |
||
886 | OutFile.PutDecimal(Value); |
||
887 | break; |
||
888 | |||
889 | case 0x500: // ymm register (packed) |
||
890 | OutFile.Put("ymm"); |
||
891 | OutFile.PutDecimal(Value); |
||
892 | break; |
||
893 | |||
894 | case 0x600: // zmm register (packed) |
||
895 | OutFile.Put("zmm"); |
||
896 | OutFile.PutDecimal(Value); |
||
897 | break; |
||
898 | |||
899 | case 0x700: // future 1024 bit register |
||
900 | OutFile.Put("?mm"); |
||
901 | OutFile.PutDecimal(Value); |
||
902 | break; |
||
903 | |||
904 | case 0x40: // st register |
||
905 | if (Syntax == SUBTYPE_YASM) { |
||
906 | // NASM, YASM and GAS-AT&T use st0 |
||
907 | OutFile.Put("st"); |
||
908 | OutFile.PutDecimal(Value); |
||
909 | } |
||
910 | else { |
||
911 | // MASM and GAS-Intel use st(0), |
||
912 | OutFile.Put("st("); |
||
913 | OutFile.PutDecimal(Value); |
||
914 | OutFile.Put(")"); |
||
915 | } |
||
916 | break; |
||
917 | |||
918 | case 0x91: // Segment register |
||
919 | OutFile.Put(RegisterNamesSeg[Value & 7]); |
||
920 | break; |
||
921 | |||
922 | case 0x92: // Control register |
||
923 | OutFile.Put(RegisterNamesCR[Value]); |
||
924 | break; |
||
925 | |||
926 | case 0x93: // Debug register |
||
927 | OutFile.Put("dr"); |
||
928 | OutFile.PutDecimal(Value); |
||
929 | break; |
||
930 | |||
931 | case 0x94: // Test register (obsolete) |
||
932 | OutFile.Put("tr"); |
||
933 | OutFile.PutDecimal(Value); |
||
934 | break; |
||
935 | |||
936 | case 0x95: // k mask register |
||
937 | OutFile.Put("k"); |
||
938 | OutFile.PutDecimal(Value); |
||
939 | break; |
||
940 | |||
941 | case 0x98: // bounds register |
||
942 | OutFile.Put("bnd"); |
||
943 | OutFile.PutDecimal(Value); |
||
944 | break; |
||
945 | |||
946 | case 0xB1: // 1 |
||
947 | OutFile.Put("1"); |
||
948 | break; |
||
949 | |||
950 | default: // Unexpected |
||
951 | OutFile.Put("UNKNOWN REGISTER TYPE "); |
||
952 | OutFile.PutDecimal(Value); |
||
953 | break; |
||
954 | } |
||
955 | } |
||
956 | } |
||
957 | |||
958 | |||
959 | void CDisassembler::WriteImmediateOperand(uint32 Type) { |
||
960 | // Write immediate operand or direct jump/call address |
||
961 | int WriteFormat; // 0: unsigned, 1: signed, 2: hexadecimal |
||
962 | int Components = 0; // Number of components in immediate operand |
||
963 | uint32 OSize; // Operand size |
||
964 | uint32 FieldPointer; // Pointer to field containing value |
||
965 | uint32 FieldSize; // Size of field containing value |
||
966 | int64 Value = 0; // Value of immediate operand |
||
967 | |||
968 | // Check if far |
||
969 | if ((Type & 0xFE) == 0x84) { |
||
970 | // Write far |
||
971 | WriteOperandType(Type); |
||
972 | } |
||
973 | |||
974 | // Check if type override needed |
||
975 | if ((s.OpcodeDef->AllowedPrefixes & 2) && s.Prefixes[4] == 0x66 |
||
976 | && (Opcodei == 0x68 || Opcodei == 0x6A)) { |
||
977 | // Push immediate with non-default operand size needs type override |
||
978 | WriteOperandType(s.OperandSize == 16 ? 2 : 3); |
||
979 | } |
||
980 | |||
981 | FieldPointer = s.ImmediateField; |
||
982 | FieldSize = s.ImmediateFieldSize; |
||
983 | |||
984 | if (Syntax == SUBTYPE_YASM && (Type & 0x0F) == 4 && FieldSize == 8) { |
||
985 | // Write type override to make sure we get 8 bytes address in case there is a relocation here |
||
986 | WriteOperandType(4); |
||
987 | } |
||
988 | |||
989 | if (Type & 0x200000) { |
||
990 | if (FieldSize > 1) { |
||
991 | // Uses second part of field. Single byte only |
||
992 | FieldPointer += FieldSize-1; |
||
993 | FieldSize = 1; |
||
994 | } |
||
995 | else { |
||
996 | // Uses half a byte |
||
997 | FieldSize = 0; |
||
998 | } |
||
999 | } |
||
1000 | |||
1001 | // Get inline value |
||
1002 | switch (FieldSize) { |
||
1003 | case 0: // 4 bits |
||
1004 | Value = Get |
||
1005 | break; |
||
1006 | |||
1007 | case 1: // 8 bits |
||
1008 | Value = Get |
||
1009 | break; |
||
1010 | |||
1011 | case 2: // 16 bits |
||
1012 | Value = Get |
||
1013 | |||
1014 | case 6: // 48 bits |
||
1015 | Value = Get |
||
1016 | Value += (uint64)Get |
||
1017 | break; |
||
1018 | |||
1019 | case 4: // 32 bits |
||
1020 | Value = Get |
||
1021 | |||
1022 | case 8: // 64 bits |
||
1023 | Value = Get |
||
1024 | |||
1025 | case 3: // 16+8 bits ("Enter" instruction) |
||
1026 | if ((Type & 0xFF) == 0x12) { |
||
1027 | // First 16 bits |
||
1028 | FieldSize = 2; Value = Get |
||
1029 | } |
||
1030 | // else continue in default case to get error message |
||
1031 | |||
1032 | default: // Other sizes should not occur |
||
1033 | err.submit(3000); Value = -1; |
||
1034 | } |
||
1035 | |||
1036 | // Check if relocation |
||
1037 | if (s.ImmediateRelocation) { |
||
1038 | // Write relocation target name |
||
1039 | uint32 Context = 2; |
||
1040 | if ((Type & 0xFC) == 0x80) Context = 8; // Near jump/call destination |
||
1041 | if ((Type & 0xFC) == 0x84) Context = 0x10; // Far jump/call destination |
||
1042 | |||
1043 | // Write cross reference |
||
1044 | WriteRelocationTarget(s.ImmediateRelocation, Context, Value); |
||
1045 | |||
1046 | // Remember that Value has been written |
||
1047 | Value = 0; |
||
1048 | Components++; |
||
1049 | } |
||
1050 | // Check if AAM or AAD |
||
1051 | if (Value == 10 && (Opcodei & 0xFE) == 0xD4) { |
||
1052 | // Don't write operand for AAM or AAD if = 10 |
||
1053 | return; |
||
1054 | } |
||
1055 | |||
1056 | // Write as unsigned, signed or hexadecimal: |
||
1057 | if ((Type & 0xF0) == 0x30 || (Type & 0xF0) == 0x80) { |
||
1058 | // Hexadecimal |
||
1059 | WriteFormat = 2; |
||
1060 | } |
||
1061 | else if (s.ImmediateFieldSize == 8) { |
||
1062 | // 64 bit constant |
||
1063 | if (Value == (int32)Value) { |
||
1064 | // Signed |
||
1065 | WriteFormat = 1; |
||
1066 | } |
||
1067 | else { |
||
1068 | // Hexadecimal |
||
1069 | WriteFormat = 2; |
||
1070 | } |
||
1071 | } |
||
1072 | else if ((Type & 0xF0) == 0x20) { |
||
1073 | // Signed |
||
1074 | WriteFormat = 1; |
||
1075 | } |
||
1076 | else { |
||
1077 | // Unsigned |
||
1078 | WriteFormat = 0; |
||
1079 | } |
||
1080 | |||
1081 | if ((Type & 0xFC) == 0x80 && !s.ImmediateRelocation) { |
||
1082 | // Self-relative jump or call without relocation. Adjust immediate value |
||
1083 | Value += IEnd; // Get absolute address of target |
||
1084 | |||
1085 | // Look for symbol at target address |
||
1086 | uint32 ISymbol = Symbols.FindByAddress(Section, (uint32)Value); |
||
1087 | if (ISymbol && (Symbols[ISymbol].Name || CodeMode == 1)) { |
||
1088 | // Symbol found. Write its name |
||
1089 | OutFile.Put(Symbols.GetName(ISymbol)); |
||
1090 | // No offset to write |
||
1091 | return; |
||
1092 | } |
||
1093 | // Target address has no name |
||
1094 | Type |= 0x4000; // Write target as hexadecimal |
||
1095 | } |
||
1096 | |||
1097 | // Operand size |
||
1098 | if ((s.Operands[0] & 0xFFF) <= 0xA || (s.Operands[0] & 0xF0) == 0xA0) { |
||
1099 | // Destination is general purpose register |
||
1100 | OSize = s.OperandSize; |
||
1101 | } |
||
1102 | else { |
||
1103 | // Constant probably unrelated to destination size |
||
1104 | OSize = 8; |
||
1105 | } |
||
1106 | // Check if destination is 8 bit operand |
||
1107 | //if ((s.Operands[0] & 0xFF) == 1 || (s.Operands[0] & 0xFF) == 0xA1) OSize = 8; |
||
1108 | |||
1109 | // Check if sign extended |
||
1110 | if (OSize > s.ImmediateFieldSize * 8) { |
||
1111 | if (WriteFormat == 2 && Value >= 0) { |
||
1112 | // Hexadecimal sign extended, not negative: |
||
1113 | // Does not need full length |
||
1114 | OSize = s.ImmediateFieldSize * 8; |
||
1115 | } |
||
1116 | else if (WriteFormat == 0) { |
||
1117 | // Unsigned and sign extended, change to signed |
||
1118 | WriteFormat = 1; |
||
1119 | } |
||
1120 | } |
||
1121 | |||
1122 | if (Components) { |
||
1123 | // There was a relocated name |
||
1124 | if (Value) { |
||
1125 | // Addend to relocation is not zero |
||
1126 | if (Value > 0 || WriteFormat != 1) { |
||
1127 | OutFile.Put("+"); // Put "+" between name and addend |
||
1128 | } |
||
1129 | else { |
||
1130 | OutFile.Put("-"); // Put "-" between name and addend |
||
1131 | Value = - Value; // Change sign to avoid another "-" |
||
1132 | } |
||
1133 | } |
||
1134 | else { |
||
1135 | // No addend to relocated name |
||
1136 | return; |
||
1137 | } |
||
1138 | } |
||
1139 | // Write value |
||
1140 | if (WriteFormat == 2) { |
||
1141 | // Write with hexadecimal number appropriate size |
||
1142 | switch (OSize) { |
||
1143 | case 8: // 8 bits |
||
1144 | OutFile.PutHex((uint8)Value, 1); break; |
||
1145 | case 16: // 16 bits |
||
1146 | if ((Type & 0xFC) == 0x84) { |
||
1147 | // Segment of far call |
||
1148 | OutFile.PutHex((uint16)(Value >> 16), 1); |
||
1149 | OutFile.Put(':'); |
||
1150 | } |
||
1151 | OutFile.PutHex((uint16)Value, 2); break; |
||
1152 | case 32: // 32 bits |
||
1153 | default: // Should not occur |
||
1154 | if ((Type & 0xFC) == 0x84) { |
||
1155 | // Segment of far call |
||
1156 | OutFile.PutHex((uint16)(Value >> 32), 1); |
||
1157 | OutFile.Put(':'); |
||
1158 | } |
||
1159 | OutFile.PutHex((uint32)Value, 2); break; |
||
1160 | case 64: // 64 bits |
||
1161 | OutFile.PutHex((uint64)Value, 2); break; |
||
1162 | } |
||
1163 | } |
||
1164 | else { |
||
1165 | // Write as signed or unsigned decimal |
||
1166 | if (WriteFormat == 0) { // unsigned |
||
1167 | switch (OSize) { |
||
1168 | case 8: // 8 bits |
||
1169 | Value &= 0x00FF; break; |
||
1170 | case 16: // 16 bits |
||
1171 | Value &= 0xFFFF; break; |
||
1172 | } |
||
1173 | } |
||
1174 | OutFile.PutDecimal((int32)Value, WriteFormat); // Write value. Signed or usigned decimal |
||
1175 | } |
||
1176 | } |
||
1177 | |||
1178 | |||
1179 | void CDisassembler::WriteOtherOperand(uint32 Type) { |
||
1180 | // Write other type of operand |
||
1181 | const char * * OpRegisterNames; // Pointer to list of register names |
||
1182 | uint32 RegI = 0; // Index into list of register names |
||
1183 | |||
1184 | switch (Type & 0x8FF) { |
||
1185 | case 0xA1: // AL |
||
1186 | OpRegisterNames = RegisterNames8; |
||
1187 | break; |
||
1188 | case 0xA2: // AX |
||
1189 | OpRegisterNames = RegisterNames16; |
||
1190 | break; |
||
1191 | case 0xA3: // EAX |
||
1192 | OpRegisterNames = RegisterNames32; |
||
1193 | break; |
||
1194 | case 0xA4: // RAX |
||
1195 | OpRegisterNames = RegisterNames64; |
||
1196 | break; |
||
1197 | case 0xAE: // xmm0 |
||
1198 | OutFile.Put("xmm0"); |
||
1199 | return; |
||
1200 | case 0xAF: // ST(0) |
||
1201 | OutFile.Put("st(0)"); |
||
1202 | return; |
||
1203 | case 0xB1: // 1 |
||
1204 | OutFile.Put("1"); |
||
1205 | return; |
||
1206 | case 0xB2: // DX |
||
1207 | OpRegisterNames = RegisterNames16; |
||
1208 | RegI = 2; |
||
1209 | break; |
||
1210 | case 0xB3: // CL |
||
1211 | OpRegisterNames = RegisterNames8; |
||
1212 | RegI = 1; |
||
1213 | break; |
||
1214 | default: |
||
1215 | OutFile.Put("unknown operand"); |
||
1216 | err.submit(3000); |
||
1217 | return; |
||
1218 | } |
||
1219 | // Write register name |
||
1220 | OutFile.Put(OpRegisterNames[RegI]); |
||
1221 | } |
||
1222 | |||
1223 | |||
1224 | void CDisassembler::WriteErrorsAndWarnings() { |
||
1225 | // Write errors, warnings and comments, if any |
||
1226 | uint32 n; // Error bit |
||
1227 | if (s.Errors) { |
||
1228 | // There are errors |
||
1229 | // Loop through all bits in s.Errors |
||
1230 | for (n = 1; n; n <<= 1) { |
||
1231 | if (s.Errors & n) { |
||
1232 | if (OutFile.GetColumn()) OutFile.NewLine(); |
||
1233 | OutFile.Put(CommentSeparator); // Write "\n; " |
||
1234 | OutFile.Put("Error: "); // Write "Error: " |
||
1235 | OutFile.Put(Lookup(AsmErrorTexts,n));// Write error text |
||
1236 | OutFile.NewLine(); |
||
1237 | } |
||
1238 | } |
||
1239 | } |
||
1240 | |||
1241 | if (s.Warnings1) { |
||
1242 | // There are warnings 1 |
||
1243 | // Loop through all bits in s.Warnings1 |
||
1244 | for (n = 1; n; n <<= 1) { |
||
1245 | if (s.Warnings1 & n) { |
||
1246 | if (OutFile.GetColumn()) OutFile.NewLine(); |
||
1247 | OutFile.Put(CommentSeparator); // Write "; " |
||
1248 | OutFile.Put("Note: "); // Write "Note: " |
||
1249 | OutFile.Put(Lookup(AsmWarningTexts1, n));// Write warning text |
||
1250 | OutFile.NewLine(); |
||
1251 | } |
||
1252 | } |
||
1253 | } |
||
1254 | if (s.Warnings2) { |
||
1255 | // There are warnings 2 |
||
1256 | // Loop through all bits in s.Warnings2 |
||
1257 | for (n = 1; n; n <<= 1) { |
||
1258 | if (s.Warnings2 & n) { |
||
1259 | if (OutFile.GetColumn()) OutFile.NewLine(); |
||
1260 | OutFile.Put(CommentSeparator); // Write "; " |
||
1261 | OutFile.Put("Warning: "); // Write "Warning: " |
||
1262 | OutFile.Put(Lookup(AsmWarningTexts2, n)); // Write warning text |
||
1263 | OutFile.NewLine(); |
||
1264 | } |
||
1265 | } |
||
1266 | if (s.Warnings2 & 1) { |
||
1267 | // Write spurious label |
||
1268 | uint32 sym1 = Symbols.FindByAddress(Section, LabelEnd); |
||
1269 | if (sym1) { |
||
1270 | const char * name = Symbols.GetName(sym1); |
||
1271 | OutFile.Put(CommentSeparator); |
||
1272 | OutFile.Put(name); |
||
1273 | OutFile.Put("; Misplaced symbol at address "); |
||
1274 | OutFile.PutHex(Symbols[sym1].Offset); |
||
1275 | OutFile.NewLine(); |
||
1276 | } |
||
1277 | } |
||
1278 | } |
||
1279 | |||
1280 | if (s.OpcodeDef && (s.OpcodeDef->AllowedPrefixes & 8) && !s.Warnings1) { |
||
1281 | if (s.Prefixes[0]) { |
||
1282 | // Branch hint prefix. Write comment |
||
1283 | OutFile.Put(CommentSeparator); // Write "; " |
||
1284 | switch (s.Prefixes[0]) { |
||
1285 | case 0x2E: |
||
1286 | OutFile.Put("Branch hint prefix for Pentium 4: Predict no jump"); |
||
1287 | break; |
||
1288 | case 0x3E: |
||
1289 | OutFile.Put("Branch hint prefix for Pentium 4: Predict jump"); |
||
1290 | break; |
||
1291 | case 0x64: |
||
1292 | OutFile.Put("Branch hint prefix for Pentium 4: Predict alternate"); |
||
1293 | break; |
||
1294 | default: |
||
1295 | OutFile.Put("Note: Unrecognized branch hint prefix"); |
||
1296 | } |
||
1297 | OutFile.NewLine(); |
||
1298 | } |
||
1299 | } |
||
1300 | } |
||
1301 | |||
1302 | void CDisassembler::WriteSymbolName(uint32 symi) { |
||
1303 | // Write symbol name. symi = new symbol index |
||
1304 | OutFile.Put(Symbols.GetName(symi)); |
||
1305 | } |
||
1306 | |||
1307 | void CDisassembler::WriteSectionName(int32 SegIndex) { |
||
1308 | // Write name of section, segment or group from section index |
||
1309 | const char * Name = 0; |
||
1310 | // Check for special index values |
||
1311 | switch (SegIndex) { |
||
1312 | case ASM_SEGMENT_UNKNOWN: // Unknown segment. Typical for external symbols |
||
1313 | Name = "Unknown"; break; |
||
1314 | case ASM_SEGMENT_ABSOLUTE: // No segment. Used for absolute symbols |
||
1315 | Name = "Absolute"; break; |
||
1316 | case ASM_SEGMENT_FLAT: // Flat segment group |
||
1317 | Name = "flat"; break; |
||
1318 | case ASM_SEGMENT_NOTHING: // No segment |
||
1319 | Name = "Nothing"; break; |
||
1320 | case ASM_SEGMENT_ERROR: // Segment register assumed to error |
||
1321 | Name = "Error"; break; |
||
1322 | case ASM_SEGMENT_IMGREL: // Segment unknown. Offset relative to image base or file base |
||
1323 | Name = "ImageBased"; break; |
||
1324 | default: // > 0 means normal segment index |
||
1325 | if ((uint32)SegIndex >= Sections.GetNumEntries()) { |
||
1326 | // Out of range |
||
1327 | Name = "IndexOutOfRange"; |
||
1328 | } |
||
1329 | else { |
||
1330 | // Get index into NameBuffer |
||
1331 | uint32 NameIndex = Sections[SegIndex].Name; |
||
1332 | // Check if valid |
||
1333 | if (NameIndex == 0 || NameIndex >= NameBuffer.GetDataSize()) { |
||
1334 | Name = "ErrorNameMissing"; |
||
1335 | } |
||
1336 | else { |
||
1337 | // Normal valid name of segment, section or group |
||
1338 | Name = NameBuffer.Buf() + NameIndex; |
||
1339 | } |
||
1340 | } |
||
1341 | break; |
||
1342 | } |
||
1343 | if (Syntax == SUBTYPE_YASM && Name[0] == '_') { |
||
1344 | // Change leading underscore to dot |
||
1345 | OutFile.Put('.'); |
||
1346 | OutFile.Put(Name+1); // Write rest of name |
||
1347 | } |
||
1348 | else { |
||
1349 | // Write name |
||
1350 | OutFile.Put(Name); |
||
1351 | } |
||
1352 | } |
||
1353 | |||
1354 | void CDisassembler::WriteDataItems() { |
||
1355 | // Write data items to output file |
||
1356 | |||
1357 | int LineState; // 0: Start of new line, write label |
||
1358 | // 1: Label written if any, write data directive |
||
1359 | // 2: Data directive written, write data |
||
1360 | // 3: First data item written, write comma and more data |
||
1361 | // 4: Last data item written, write comment |
||
1362 | // 5: Comment written if any, start new line |
||
1363 | uint32 Pos = IBegin; // Current position |
||
1364 | uint32 LinePos = IBegin; // Position for beginning of output line |
||
1365 | uint32 BytesPerLine; // Number of bytes to write per line |
||
1366 | uint32 LineEnd; // Data position for end of line |
||
1367 | uint32 DataEnd; // End of data |
||
1368 | uint32 ElementSize, OldElementSize; // Size of each data element |
||
1369 | uint32 RelOffset; // Offset of relocation |
||
1370 | uint32 irel, Oldirel; // Relocation index |
||
1371 | int64 Value; // Inline value or addend |
||
1372 | const char * Symname; // Symbol name |
||
1373 | int SeparateLine; // Label is on separate line |
||
1374 | |||
1375 | SARelocation Rel; // Dummy relocation record |
||
1376 | |||
1377 | // Check if size is valid |
||
1378 | if (DataSize == 0) DataSize = 1; |
||
1379 | if (DataSize > 32) DataSize = 32; |
||
1380 | |||
1381 | // Expected end position |
||
1382 | if (CodeMode & 3) { |
||
1383 | // Writing data for dubious code. Make same length as code instruction |
||
1384 | DataEnd = IEnd; |
||
1385 | } |
||
1386 | else { |
||
1387 | // Regular data. End at next label |
||
1388 | DataEnd = LabelEnd; |
||
1389 | if (DataEnd > FunctionEnd) DataEnd = FunctionEnd; |
||
1390 | if (DataEnd <= Pos) DataEnd = Pos + DataSize; |
||
1391 | if (DataEnd > Sections[Section].InitSize && Pos < Sections[Section].InitSize) { |
||
1392 | DataEnd = Sections[Section].InitSize; |
||
1393 | } |
||
1394 | } |
||
1395 | |||
1396 | // Size of each data element |
||
1397 | ElementSize = DataSize; |
||
1398 | |||
1399 | // Check if packed type |
||
1400 | if (DataType & 0xF00) { |
||
1401 | // This is a packed vector type. Get element size |
||
1402 | ElementSize = GetDataElementSize(DataType); |
||
1403 | } |
||
1404 | |||
1405 | // Avoid sizes that are not powers of 2 |
||
1406 | if (ElementSize == 6 || ElementSize == 10) ElementSize = 2; |
||
1407 | |||
1408 | // Set maximum element size to 8 |
||
1409 | if (ElementSize > 8) ElementSize = 8; |
||
1410 | |||
1411 | // Set minimum element size to 1 |
||
1412 | if (ElementSize < 1) ElementSize = 1; |
||
1413 | |||
1414 | if (Pos + ElementSize > DataEnd) { |
||
1415 | // Make sure we end at DataEnd |
||
1416 | ElementSize = 1; BytesPerLine = 8; |
||
1417 | LineEnd = DataEnd; |
||
1418 | } |
||
1419 | |||
1420 | // Set number of bytes per line |
||
1421 | BytesPerLine = (DataSize == 10) ? 10 : 8; |
||
1422 | |||
1423 | if (!(CodeMode & 3)) { |
||
1424 | // Begin new line for each data item (except in code segment) |
||
1425 | OutFile.NewLine(); |
||
1426 | } |
||
1427 | LineState = 0; irel = 0; |
||
1428 | |||
1429 | // Check if alignment required |
||
1430 | if (DataSize >= 16 && (DataType & 0xC00) && (DataType & 0xFF) != 0x51 |
||
1431 | && (FlagPrevious & 0x100) < (DataSize << 4) && !(IBegin & (DataSize-1))) { |
||
1432 | // Write align directive |
||
1433 | WriteAlign(DataSize); |
||
1434 | // Remember that data is aligned |
||
1435 | FlagPrevious |= (DataSize << 4); |
||
1436 | } |
||
1437 | |||
1438 | // Get symbol name for label |
||
1439 | uint32 sym; // Current symbol index |
||
1440 | uint32 sym1, sym2 = 0; // First and last symbol at current address |
||
1441 | |||
1442 | sym1 = Symbols.FindByAddress(Section, Pos, &sym2); |
||
1443 | |||
1444 | // Loop for one or more symbols at this address |
||
1445 | for (sym = sym1; sym <= sym2; sym++) { |
||
1446 | |||
1447 | if (sym && Symbols[sym].Scope && !(Symbols[sym].Scope & 0x100) && !(Symbols[sym].Type & 0x80000000)) { |
||
1448 | |||
1449 | // Prepare for writing symbol label |
||
1450 | Symname = Symbols.GetName(sym); // Symbol name |
||
1451 | // Check if label needs a separate line |
||
1452 | SeparateLine = (ElementSize != DataSize |
||
1453 | || Symbols[sym].Size != DataSize |
||
1454 | || strlen(Symname) > AsmTab1 |
||
1455 | || sym < sym2 |
||
1456 | // || (Sections[Section].Type & 0xFF) == 3 |
||
1457 | || ((Symbols[sym].Type+1) & 0xFE) == 0x0C); |
||
1458 | |||
1459 | // Write symbol label |
||
1460 | switch (Syntax) { |
||
1461 | case SUBTYPE_MASM: |
||
1462 | WriteDataLabelMASM(Symname, sym, SeparateLine); break; |
||
1463 | case SUBTYPE_YASM: |
||
1464 | WriteDataLabelYASM(Symname, sym, SeparateLine); break; |
||
1465 | case SUBTYPE_GASM: |
||
1466 | WriteDataLabelGASM(Symname, sym, SeparateLine); break; |
||
1467 | } |
||
1468 | LineState = 1; // Label written |
||
1469 | if (SeparateLine) { |
||
1470 | LineState = 0; |
||
1471 | } |
||
1472 | } |
||
1473 | } |
||
1474 | |||
1475 | if ((Sections[Section].Type & 0xFF) == 3 || Pos >= Sections[Section].InitSize) { |
||
1476 | // This is an unitialized data (BSS) section |
||
1477 | // Data repeat count |
||
1478 | uint32 DataCount = (DataEnd - Pos) / ElementSize; |
||
1479 | if (DataCount) { |
||
1480 | OutFile.Tabulate(AsmTab1); |
||
1481 | // Write data directives |
||
1482 | switch (Syntax) { |
||
1483 | case SUBTYPE_MASM: |
||
1484 | WriteUninitDataItemsMASM(ElementSize, DataCount); break; |
||
1485 | case SUBTYPE_YASM: |
||
1486 | WriteUninitDataItemsYASM(ElementSize, DataCount); break; |
||
1487 | case SUBTYPE_GASM: |
||
1488 | WriteUninitDataItemsGASM(ElementSize, DataCount); break; |
||
1489 | } |
||
1490 | // Write comment |
||
1491 | WriteDataComment(ElementSize, Pos, Pos, 0); |
||
1492 | OutFile.NewLine(); |
||
1493 | LineState = 0; |
||
1494 | } |
||
1495 | // Update data position |
||
1496 | Pos += DataCount * ElementSize; |
||
1497 | |||
1498 | if (Pos < DataEnd) { |
||
1499 | // Some odd data remain. Write as bytes |
||
1500 | DataCount = DataEnd - Pos; |
||
1501 | ElementSize = 1; |
||
1502 | OutFile.Tabulate(AsmTab1); |
||
1503 | switch (Syntax) { |
||
1504 | case SUBTYPE_MASM: |
||
1505 | WriteUninitDataItemsMASM(ElementSize, DataCount); break; |
||
1506 | case SUBTYPE_YASM: |
||
1507 | WriteUninitDataItemsYASM(ElementSize, DataCount); break; |
||
1508 | case SUBTYPE_GASM: |
||
1509 | WriteUninitDataItemsGASM(ElementSize, DataCount); break; |
||
1510 | } |
||
1511 | // Write comment |
||
1512 | WriteDataComment(ElementSize, Pos, Pos, 0); |
||
1513 | OutFile.NewLine(); |
||
1514 | Pos = DataEnd; |
||
1515 | LineState = 0; |
||
1516 | } |
||
1517 | } |
||
1518 | else { |
||
1519 | // Not a BSS section |
||
1520 | // Label has been written, write data |
||
1521 | |||
1522 | // Loop for one or more elements |
||
1523 | LinePos = Pos; |
||
1524 | while (Pos < DataEnd) { |
||
1525 | |||
1526 | // Find end of line position |
||
1527 | LineEnd = LinePos + BytesPerLine; |
||
1528 | |||
1529 | // Remember element size and relocation |
||
1530 | OldElementSize = ElementSize; |
||
1531 | Oldirel = irel; |
||
1532 | |||
1533 | // Check if relocation |
||
1534 | Rel.Section = Section; |
||
1535 | Rel.Offset = Pos; |
||
1536 | uint32 irel = Relocations.FindFirst(Rel); |
||
1537 | if (irel >= Relocations.GetNumEntries() || Relocations[irel].Section != (int32)Section) { |
||
1538 | // No relevant relocation |
||
1539 | irel = 0; |
||
1540 | } |
||
1541 | if (irel) { |
||
1542 | // A relocation is found |
||
1543 | // Check relocation source |
||
1544 | RelOffset = Relocations[irel].Offset; |
||
1545 | if (RelOffset == Pos) { |
||
1546 | // Relocation source is here |
||
1547 | // Make sure the size fits and begin new line |
||
1548 | ElementSize = Relocations[irel].Size; BytesPerLine = 8; |
||
1549 | if (ElementSize < 1) ElementSize = WordSize / 8; |
||
1550 | if (ElementSize < 1) ElementSize = 4; |
||
1551 | LineEnd = Pos + ElementSize; |
||
1552 | if (LineState > 2) LineState = 4; // Make sure we begin at new line |
||
1553 | } |
||
1554 | else if (RelOffset < Pos + ElementSize) { |
||
1555 | // Relocation source begins before end of element with current ElementSize |
||
1556 | // Change ElementSize to make sure a new element begins at relocation source |
||
1557 | ElementSize = 1; BytesPerLine = 8; |
||
1558 | LineEnd = RelOffset; |
||
1559 | if (LineState > 2) LineState = 4; // Make sure we begin at new line |
||
1560 | irel = 0; |
||
1561 | } |
||
1562 | else { |
||
1563 | // Relocation is after this element |
||
1564 | irel = 0; |
||
1565 | } |
||
1566 | // Check for overlapping relocations |
||
1567 | if (irel && irel+1 < Relocations.GetNumEntries() |
||
1568 | && Relocations[irel+1].Section == (int32)Section |
||
1569 | && Relocations[irel+1].Offset < RelOffset + ElementSize) { |
||
1570 | // Overlapping relocations |
||
1571 | s.Errors |= 0x2000; |
||
1572 | WriteErrorsAndWarnings(); |
||
1573 | LineEnd = Relocations[irel+1].Offset; |
||
1574 | if (LineState > 2) LineState = 4; // Make sure we begin at new line |
||
1575 | } |
||
1576 | // Drop alignment |
||
1577 | FlagPrevious &= ~0xF00; |
||
1578 | } |
||
1579 | if (irel == 0) { |
||
1580 | // No relocation here |
||
1581 | // Check if DataEnd would be exceeded |
||
1582 | if (Pos + ElementSize > DataEnd) { |
||
1583 | // Make sure we end at DataEnd unless there is a relocation source here |
||
1584 | ElementSize = 1; BytesPerLine = 8; |
||
1585 | LineEnd = DataEnd; |
||
1586 | if (LineState > 2) LineState = 4; // Make sure we begin at new line |
||
1587 | FlagPrevious &= ~0xF00; // Drop alignment |
||
1588 | } |
||
1589 | } |
||
1590 | // Check if new line needed |
||
1591 | if (LineState == 4) { |
||
1592 | // Finish this line |
||
1593 | if (!(CodeMode & 3)) { |
||
1594 | WriteDataComment(OldElementSize, LinePos, Pos, Oldirel); |
||
1595 | } |
||
1596 | // Start new line |
||
1597 | OutFile.NewLine(); |
||
1598 | LineState = 0; |
||
1599 | LinePos = Pos; |
||
1600 | continue; |
||
1601 | } |
||
1602 | |||
1603 | // Tabulate |
||
1604 | OutFile.Tabulate(AsmTab1); |
||
1605 | |||
1606 | if (LineState < 2) { |
||
1607 | // Write data definition directive for appropriate size |
||
1608 | switch (Syntax) { |
||
1609 | case SUBTYPE_MASM: |
||
1610 | WriteDataDirectiveMASM(ElementSize); break; |
||
1611 | case SUBTYPE_YASM: |
||
1612 | WriteDataDirectiveYASM(ElementSize); break; |
||
1613 | case SUBTYPE_GASM: |
||
1614 | WriteDataDirectiveGASM(ElementSize); break; |
||
1615 | } |
||
1616 | LineState = 2; |
||
1617 | } |
||
1618 | else if (LineState == 3) { |
||
1619 | // Not the first element, write comma |
||
1620 | OutFile.Put(", "); |
||
1621 | } |
||
1622 | // Get inline value |
||
1623 | switch (ElementSize) { |
||
1624 | case 1: Value = Get |
||
1625 | case 2: Value = Get |
||
1626 | case 4: Value = Get |
||
1627 | case 6: Value = Get |
||
1628 | case 8: Value = Get |
||
1629 | case 10: Value = Get |
||
1630 | default: Value = 0; // should not occur |
||
1631 | } |
||
1632 | if (irel) { |
||
1633 | // There is a relocation here. Write the name etc. |
||
1634 | WriteRelocationTarget(irel, 1, Value); |
||
1635 | } |
||
1636 | else { |
||
1637 | // Write value |
||
1638 | switch (ElementSize) { |
||
1639 | case 1: |
||
1640 | OutFile.PutHex((uint8)Value, 1); |
||
1641 | break; |
||
1642 | case 2: |
||
1643 | OutFile.PutHex((uint16)Value, 1); |
||
1644 | break; |
||
1645 | case 4: |
||
1646 | OutFile.PutHex((uint32)Value, 1); |
||
1647 | break; |
||
1648 | case 6: |
||
1649 | OutFile.PutHex((uint16)(Value >> 32), 1); |
||
1650 | OutFile.Put(":"); |
||
1651 | OutFile.PutHex((uint32)Value, 1); |
||
1652 | break; |
||
1653 | case 8: |
||
1654 | OutFile.PutHex((uint64)Value, 1); |
||
1655 | break; |
||
1656 | case 10: |
||
1657 | OutFile.Put("??"); |
||
1658 | break; |
||
1659 | } |
||
1660 | } |
||
1661 | LineState = 3; |
||
1662 | // Increment position |
||
1663 | Pos += ElementSize; |
||
1664 | |||
1665 | // Check if end of line |
||
1666 | if (Pos >= LineEnd || Pos >= DataEnd) LineState = 4; |
||
1667 | |||
1668 | if (LineState == 4) { |
||
1669 | // End of line |
||
1670 | if (!(CodeMode & 3)) { |
||
1671 | // Write comment |
||
1672 | WriteDataComment(ElementSize, LinePos, Pos, irel); |
||
1673 | } |
||
1674 | OutFile.NewLine(); |
||
1675 | LinePos = Pos; |
||
1676 | LineState = 0; |
||
1677 | } |
||
1678 | } |
||
1679 | } |
||
1680 | |||
1681 | // Indicate end |
||
1682 | if (IEnd < Pos) IEnd = Pos; |
||
1683 | if (IEnd > LabelEnd) IEnd = LabelEnd; |
||
1684 | if (IEnd > FunctionEnd && FunctionEnd) IEnd = FunctionEnd; |
||
1685 | |||
1686 | // Reset FlagPrevious if not aligned |
||
1687 | if (DataSize < 16 || (DataType & 0xFF) == 0x28) FlagPrevious = 0; |
||
1688 | } |
||
1689 | |||
1690 | |||
1691 | void CDisassembler::WriteDataLabelMASM(const char * name, uint32 sym, int line) { |
||
1692 | // Write label before data item, MASM syntax |
||
1693 | // name = name of data item(s) |
||
1694 | // sym = symbol index |
||
1695 | // line = 1 if label is on separate line, 0 if data follows on same line |
||
1696 | // Write name |
||
1697 | OutFile.Put(name); |
||
1698 | // At least one space |
||
1699 | OutFile.Put(" "); |
||
1700 | // Tabulate |
||
1701 | OutFile.Tabulate(AsmTab1); |
||
1702 | |||
1703 | if (line) { |
||
1704 | // Write label and type on seperate line |
||
1705 | // Get size |
||
1706 | uint32 Symsize = Symbols[sym].Size; |
||
1707 | if (Symsize == 0) Symsize = DataSize; |
||
1708 | OutFile.Put("label "); |
||
1709 | // Write type |
||
1710 | switch(Symsize) { |
||
1711 | case 1: default: |
||
1712 | OutFile.Put("byte"); break; |
||
1713 | case 2: |
||
1714 | OutFile.Put("word"); break; |
||
1715 | case 4: |
||
1716 | OutFile.Put("dword"); break; |
||
1717 | case 6: |
||
1718 | OutFile.Put("fword"); break; |
||
1719 | case 8: |
||
1720 | OutFile.Put("qword"); break; |
||
1721 | case 10: |
||
1722 | OutFile.Put("tbyte"); break; |
||
1723 | case 16: |
||
1724 | OutFile.Put("xmmword"); break; |
||
1725 | case 32: |
||
1726 | OutFile.Put("ymmword"); break; |
||
1727 | } |
||
1728 | // Check if jump table or call table |
||
1729 | if (((Symbols[sym].Type+1) & 0xFE) == 0x0C) { |
||
1730 | OutFile.Tabulate(AsmTab3); |
||
1731 | OutFile.Put(CommentSeparator); |
||
1732 | if (Symbols[sym].DLLName) { |
||
1733 | // DLL import |
||
1734 | OutFile.Put("import from "); |
||
1735 | OutFile.Put(Symbols.GetDLLName(sym)); |
||
1736 | } |
||
1737 | else if (Symbols[sym].Type & 1) { |
||
1738 | OutFile.Put("switch/case jump table"); |
||
1739 | } |
||
1740 | else { |
||
1741 | OutFile.Put("virtual table or function pointer"); |
||
1742 | } |
||
1743 | } |
||
1744 | // New line |
||
1745 | OutFile.NewLine(); |
||
1746 | } |
||
1747 | } |
||
1748 | |||
1749 | void CDisassembler::WriteDataLabelYASM(const char * name, uint32 sym, int line) { |
||
1750 | // Write label before data item, YASM syntax |
||
1751 | // name = name of data item(s) |
||
1752 | // sym = symbol index |
||
1753 | // line = 1 if label is on separate line, 0 if data follows on same line |
||
1754 | // Write name and colon |
||
1755 | OutFile.Put(name); |
||
1756 | OutFile.Put(": "); |
||
1757 | // Tabulate |
||
1758 | OutFile.Tabulate(AsmTab1); |
||
1759 | |||
1760 | if (line) { |
||
1761 | // Write label on seperate line |
||
1762 | // Write comment |
||
1763 | OutFile.Tabulate(AsmTab3); |
||
1764 | OutFile.Put(CommentSeparator); |
||
1765 | // Check if jump table or call table |
||
1766 | if (((Symbols[sym].Type+1) & 0xFE) == 0x0C) { |
||
1767 | if (Symbols[sym].DLLName) { |
||
1768 | // DLL import |
||
1769 | OutFile.Put("import from "); |
||
1770 | OutFile.Put(Symbols.GetDLLName(sym)); |
||
1771 | } |
||
1772 | else if (Symbols[sym].Type & 1) { |
||
1773 | OutFile.Put("switch/case jump table"); |
||
1774 | } |
||
1775 | else { |
||
1776 | OutFile.Put("virtual table or function pointer"); |
||
1777 | } |
||
1778 | } |
||
1779 | else { |
||
1780 | // Write size |
||
1781 | uint32 Symsize = Symbols[sym].Size; |
||
1782 | if (Symsize == 0) Symsize = DataSize; |
||
1783 | switch(Symsize) { |
||
1784 | case 1: default: |
||
1785 | OutFile.Put("byte"); break; |
||
1786 | case 2: |
||
1787 | OutFile.Put("word"); break; |
||
1788 | case 4: |
||
1789 | OutFile.Put("dword"); break; |
||
1790 | case 6: |
||
1791 | OutFile.Put("fword"); break; |
||
1792 | case 8: |
||
1793 | OutFile.Put("qword"); break; |
||
1794 | case 10: |
||
1795 | OutFile.Put("tbyte"); break; |
||
1796 | case 16: |
||
1797 | OutFile.Put("oword"); break; |
||
1798 | case 32: |
||
1799 | OutFile.Put("yword"); break; |
||
1800 | case 64: |
||
1801 | OutFile.Put("zword"); break; |
||
1802 | } |
||
1803 | } |
||
1804 | // New line |
||
1805 | OutFile.NewLine(); |
||
1806 | } |
||
1807 | } |
||
1808 | |||
1809 | void CDisassembler::WriteDataLabelGASM(const char * name, uint32 sym, int line) { |
||
1810 | // Write label before data item, GAS syntax |
||
1811 | // name = name of data item(s) |
||
1812 | // sym = symbol index |
||
1813 | // line = 1 if label is on separate line, 0 if data follows on same line |
||
1814 | // Write name and colon |
||
1815 | OutFile.Put(name); |
||
1816 | OutFile.Put(": "); |
||
1817 | // Tabulate |
||
1818 | OutFile.Tabulate(AsmTab1); |
||
1819 | |||
1820 | if (line) { |
||
1821 | // Write label on seperate line |
||
1822 | // Write comment |
||
1823 | OutFile.Tabulate(AsmTab3); |
||
1824 | OutFile.Put(CommentSeparator); |
||
1825 | // Check if jump table or call table |
||
1826 | if (((Symbols[sym].Type+1) & 0xFE) == 0x0C) { |
||
1827 | if (Symbols[sym].DLLName) { |
||
1828 | // DLL import |
||
1829 | OutFile.Put("import from "); |
||
1830 | OutFile.Put(Symbols.GetDLLName(sym)); |
||
1831 | } |
||
1832 | else if (Symbols[sym].Type & 1) { |
||
1833 | OutFile.Put("switch/case jump table"); |
||
1834 | } |
||
1835 | else { |
||
1836 | OutFile.Put("virtual table or function pointer"); |
||
1837 | } |
||
1838 | } |
||
1839 | else { |
||
1840 | // Write size |
||
1841 | uint32 Symsize = Symbols[sym].Size; |
||
1842 | if (Symsize == 0) Symsize = DataSize; |
||
1843 | switch(Symsize) { |
||
1844 | case 1: default: |
||
1845 | OutFile.Put("byte"); break; |
||
1846 | case 2: |
||
1847 | OutFile.Put("word"); break; |
||
1848 | case 4: |
||
1849 | OutFile.Put("int"); break; |
||
1850 | case 6: |
||
1851 | OutFile.Put("farword"); break; |
||
1852 | case 8: |
||
1853 | OutFile.Put("qword"); break; |
||
1854 | case 10: |
||
1855 | OutFile.Put("tfloat"); break; |
||
1856 | case 16: |
||
1857 | OutFile.Put("xmmword"); break; |
||
1858 | case 32: |
||
1859 | OutFile.Put("ymmword"); break; |
||
1860 | } |
||
1861 | } |
||
1862 | // New line |
||
1863 | OutFile.NewLine(); |
||
1864 | } |
||
1865 | } |
||
1866 | |||
1867 | void CDisassembler::WriteUninitDataItemsMASM(uint32 size, uint32 count) { |
||
1868 | // Write uninitialized (BSS) data, MASM syntax |
||
1869 | // size = size of each data element |
||
1870 | // count = number of data elements on each line |
||
1871 | |||
1872 | // Write data definition directive for appropriate size |
||
1873 | switch (size) { |
||
1874 | case 1: |
||
1875 | OutFile.Put("db "); break; |
||
1876 | case 2: |
||
1877 | OutFile.Put("dw "); break; |
||
1878 | case 4: |
||
1879 | OutFile.Put("dd "); break; |
||
1880 | case 6: |
||
1881 | OutFile.Put("df "); break; |
||
1882 | case 8: |
||
1883 | OutFile.Put("dq "); break; |
||
1884 | case 10: |
||
1885 | OutFile.Put("dt "); break; |
||
1886 | } |
||
1887 | OutFile.Tabulate(AsmTab2); |
||
1888 | if (count > 1) { |
||
1889 | // Write duplication operator |
||
1890 | OutFile.PutDecimal(count); |
||
1891 | OutFile.Put(" dup (?)"); |
||
1892 | } |
||
1893 | else { |
||
1894 | // DataCount == 1 |
||
1895 | OutFile.Put("?"); |
||
1896 | } |
||
1897 | } |
||
1898 | |||
1899 | void CDisassembler::WriteUninitDataItemsYASM(uint32 size, uint32 count) { |
||
1900 | // Write uninitialized (BSS) data, YASM syntax |
||
1901 | // Write data definition directive for appropriate size |
||
1902 | switch (size) { |
||
1903 | case 1: |
||
1904 | OutFile.Put("resb "); break; |
||
1905 | case 2: |
||
1906 | OutFile.Put("resw "); break; |
||
1907 | case 4: |
||
1908 | OutFile.Put("resd "); break; |
||
1909 | case 6: |
||
1910 | OutFile.Put("resw "); count *= 3; break; |
||
1911 | case 8: |
||
1912 | OutFile.Put("resq "); break; |
||
1913 | case 10: |
||
1914 | OutFile.Put("rest "); break; |
||
1915 | } |
||
1916 | OutFile.Tabulate(AsmTab2); |
||
1917 | OutFile.PutDecimal(count); |
||
1918 | } |
||
1919 | |||
1920 | void CDisassembler::WriteUninitDataItemsGASM(uint32 size, uint32 count) { |
||
1921 | // Write uninitialized (BSS) data, GAS syntax |
||
1922 | OutFile.Put(".zero"); |
||
1923 | OutFile.Tabulate(AsmTab2); |
||
1924 | if (count != 1) { |
||
1925 | OutFile.PutDecimal(count); OutFile.Put(" * "); |
||
1926 | } |
||
1927 | OutFile.PutDecimal(size); |
||
1928 | } |
||
1929 | |||
1930 | void CDisassembler::WriteDataDirectiveMASM(uint32 size) { |
||
1931 | // Write DB, etc., MASM syntax |
||
1932 | // Write data definition directive for appropriate size |
||
1933 | switch (size) { |
||
1934 | case 1: OutFile.Put("db "); break; |
||
1935 | case 2: OutFile.Put("dw "); break; |
||
1936 | case 4: OutFile.Put("dd "); break; |
||
1937 | case 6: OutFile.Put("df "); break; |
||
1938 | case 8: OutFile.Put("dq "); break; |
||
1939 | case 10: OutFile.Put("dt "); break; |
||
1940 | case 16: OutFile.Put("xmmword "); break; |
||
1941 | case 32: OutFile.Put("ymmword "); break; |
||
1942 | default: OutFile.Put("Error "); break; |
||
1943 | } |
||
1944 | } |
||
1945 | |||
1946 | void CDisassembler::WriteDataDirectiveYASM(uint32 size) { |
||
1947 | // Write DB, etc., YASM syntax |
||
1948 | // Write data definition directive for appropriate size |
||
1949 | switch (size) { |
||
1950 | case 1: OutFile.Put("db "); break; |
||
1951 | case 2: OutFile.Put("dw "); break; |
||
1952 | case 4: OutFile.Put("dd "); break; |
||
1953 | case 6: OutFile.Put("df "); break; |
||
1954 | case 8: OutFile.Put("dq "); break; |
||
1955 | case 10: OutFile.Put("dt "); break; |
||
1956 | case 16: OutFile.Put("ddq "); break; |
||
1957 | default: OutFile.Put("Error "); break; |
||
1958 | } |
||
1959 | } |
||
1960 | |||
1961 | void CDisassembler::WriteDataDirectiveGASM(uint32 size) { |
||
1962 | // Write DB, etc., GAS syntax |
||
1963 | // Write data definition directive for appropriate size |
||
1964 | switch (size) { |
||
1965 | case 1: OutFile.Put(".byte "); break; |
||
1966 | case 2: OutFile.Put(".short "); break; |
||
1967 | case 4: OutFile.Put(".int "); break; |
||
1968 | case 8: OutFile.Put(".quad "); break; |
||
1969 | case 10: OutFile.Put(".tfloat "); break; |
||
1970 | default: OutFile.Put("Error "); break; |
||
1971 | } |
||
1972 | } |
||
1973 | |||
1974 | |||
1975 | void CDisassembler::WriteDataComment(uint32 ElementSize, uint32 LinePos, uint32 Pos, uint32 irel) { |
||
1976 | // Write comment after data item |
||
1977 | uint32 pos1; // Position of data for comment |
||
1978 | uint32 RelType = 0; // Relocation type |
||
1979 | char TextBuffer[64]; // Buffer for writing floating point number |
||
1980 | |||
1981 | OutFile.Tabulate(AsmTab3); // Tabulate to comment field |
||
1982 | OutFile.Put(CommentSeparator); // Start comment |
||
1983 | |||
1984 | // Write address |
||
1985 | if (SectionEnd + SectionAddress + (uint32)ImageBase > 0xFFFF) { |
||
1986 | // Write 32 bit address |
||
1987 | OutFile.PutHex(LinePos + SectionAddress + (uint32)ImageBase); |
||
1988 | } |
||
1989 | else { |
||
1990 | // Write 16 bit address |
||
1991 | OutFile.PutHex((uint16)(LinePos + SectionAddress)); |
||
1992 | } |
||
1993 | |||
1994 | if ((Sections[Section].Type & 0xFF) == 3 || Pos > Sections[Section].InitSize) { |
||
1995 | // Unitialized data. Write no data |
||
1996 | return; |
||
1997 | } |
||
1998 | |||
1999 | if (irel && irel < Relocations.GetNumEntries() && Relocations[irel].Offset == LinePos) { |
||
2000 | // Value is relocated, get relocation type |
||
2001 | RelType = Relocations[irel].Type; |
||
2002 | } |
||
2003 | |||
2004 | // Space after address |
||
2005 | OutFile.Put(" _ "); |
||
2006 | |||
2007 | // Comment type depends on ElementSize and DataType |
||
2008 | switch (ElementSize) { |
||
2009 | case 1: |
||
2010 | // Bytes. Write ASCII characters |
||
2011 | for (pos1 = LinePos; pos1 < Pos; pos1++) { |
||
2012 | // Get character |
||
2013 | int8 c = Get |
||
2014 | // Avoid non-printable characters |
||
2015 | if (c < ' ' || c == 0x7F) c = '.'; |
||
2016 | // Print ASCII character |
||
2017 | OutFile.Put(c); |
||
2018 | } |
||
2019 | break; |
||
2020 | case 2: |
||
2021 | // Words. Write as decimal |
||
2022 | for (pos1 = LinePos; pos1 < Pos; pos1 += 2) { |
||
2023 | if (RelType) { |
||
2024 | OutFile.PutHex(Get |
||
2025 | } |
||
2026 | else { |
||
2027 | OutFile.PutDecimal(Get |
||
2028 | } |
||
2029 | OutFile.Put(' '); |
||
2030 | } |
||
2031 | break; |
||
2032 | case 4: |
||
2033 | // Dwords |
||
2034 | for (pos1 = LinePos; pos1 < Pos; pos1 += 4) { |
||
2035 | if ((DataType & 0x47) == 0x43) { |
||
2036 | // Write as float |
||
2037 | sprintf(TextBuffer, "%.8G", Get |
||
2038 | OutFile.Put(TextBuffer); |
||
2039 | // Make sure the number has a . or E to indicate a floating point number |
||
2040 | if (!strchr(TextBuffer,'.') && !strchr(TextBuffer,'E')) OutFile.Put(".0"); |
||
2041 | } |
||
2042 | else if (((DataType + 1) & 0xFF) == 0x0C || RelType) { |
||
2043 | // jump/call address or offset. Write as hexadecimal |
||
2044 | OutFile.PutHex(Get |
||
2045 | } |
||
2046 | else { |
||
2047 | // Other. Write as decimal |
||
2048 | OutFile.PutDecimal(Get |
||
2049 | } |
||
2050 | OutFile.Put(' '); |
||
2051 | } |
||
2052 | break; |
||
2053 | case 8: |
||
2054 | // Qwords |
||
2055 | for (pos1 = LinePos; pos1 < Pos; pos1 += 8) { |
||
2056 | if ((DataType & 0x47) == 0x44) { |
||
2057 | // Write as double |
||
2058 | sprintf(TextBuffer, "%.16G", Get |
||
2059 | OutFile.Put(TextBuffer); |
||
2060 | // Make sure the number has a . or E to indicate a floating point number |
||
2061 | if (!strchr(TextBuffer,'.') && !strchr(TextBuffer,'E')) OutFile.Put(".0"); |
||
2062 | } |
||
2063 | else { |
||
2064 | // Write as hexadecimal |
||
2065 | OutFile.PutHex(Get |
||
2066 | } |
||
2067 | OutFile.Put(' '); |
||
2068 | } |
||
2069 | break; |
||
2070 | case 10: |
||
2071 | // tbyte. Many compilers do not support long doubles in sprintf. Write as bytes |
||
2072 | for (pos1 = LinePos; pos1 < Pos; pos1++) { |
||
2073 | OutFile.PutHex(Get |
||
2074 | } |
||
2075 | break; |
||
2076 | } |
||
2077 | if (RelType) { |
||
2078 | // Indicate relocation type |
||
2079 | OutFile.Put(Lookup(RelocationTypeNames, RelType)); |
||
2080 | } |
||
2081 | } |
||
2082 | |||
2083 | |||
2084 | void CDisassembler::WriteRelocationTarget(uint32 irel, uint32 Context, int64 Addend) { |
||
2085 | // Write cross reference, including addend, but not including segment override and [] |
||
2086 | // irel = index into Relocations |
||
2087 | // Context: |
||
2088 | // 1 = Data definition |
||
2089 | // 2 = Immediate data field in instruction |
||
2090 | // 4 = Data address in instruction |
||
2091 | // 8 = Near jump/call destination |
||
2092 | // 0x10 = Far jump/call destination |
||
2093 | // 0x100 = Self-relative address expected |
||
2094 | // Addend: inline addend |
||
2095 | // Implicit parameters: |
||
2096 | // IBegin: value of '$' operator |
||
2097 | // IEnd: reference point for self-relative addressing |
||
2098 | // BaseReg, IndexReg |
||
2099 | |||
2100 | uint32 RefFrame; // Target segment |
||
2101 | int32 Addend2 = 0; // Difference between '$' and reference point |
||
2102 | |||
2103 | // Get relocation type |
||
2104 | uint32 RelType = Relocations[irel].Type; |
||
2105 | |||
2106 | if (RelType & 0x60) { |
||
2107 | // Inline addend is already relocated. |
||
2108 | // Ignore addend and treat as direct relocation |
||
2109 | RelType = 1; |
||
2110 | Addend = 0; |
||
2111 | } |
||
2112 | |||
2113 | // Get relocation size |
||
2114 | uint32 RelSize = Relocations[irel].Size; |
||
2115 | |||
2116 | // Get relocation addend |
||
2117 | Addend += Relocations[irel].Addend; |
||
2118 | |||
2119 | // Get relocation target |
||
2120 | uint32 Target = Relocations[irel].TargetOldIndex; |
||
2121 | |||
2122 | // Is offset operand needed? |
||
2123 | if (Syntax != SUBTYPE_YASM && ( |
||
2124 | ((RelType & 0xB) && (Context & 2)) |
||
2125 | || ((RelType & 8) && (Context & 0x108)))) { |
||
2126 | // offset operator needed to convert memory operand to immediate address |
||
2127 | OutFile.Put("offset "); |
||
2128 | } |
||
2129 | |||
2130 | // Is seg operand needed? |
||
2131 | if (RelType & 0x200) { |
||
2132 | // seg operator needed to convert memory operand to its segment |
||
2133 | OutFile.Put("seg "); |
||
2134 | } |
||
2135 | |||
2136 | // Is explicit segment or frame needed? |
||
2137 | if ((RelType & 0x408) && (Context & 0x11B)) { |
||
2138 | // Write name of segment/group frame |
||
2139 | RefFrame = Relocations[irel].RefOldIndex; |
||
2140 | if (!RefFrame) { |
||
2141 | // No frame. Use segment of symbol |
||
2142 | RefFrame = Symbols[Symbols.Old2NewIndex(Target)].Section; |
||
2143 | } |
||
2144 | if (RefFrame && RefFrame < Sections.GetNumEntries()) { |
||
2145 | // Write segment or group name |
||
2146 | const char * SecName = NameBuffer.Buf()+Sections[RefFrame].Name; |
||
2147 | OutFile.Put(SecName); |
||
2148 | OutFile.Put(":"); |
||
2149 | } |
||
2150 | } |
||
2151 | |||
2152 | // Is imagerel operator needed? |
||
2153 | if (RelType & 4) { |
||
2154 | // imagerel operator needed to get image-relative address |
||
2155 | OutFile.Put("imagerel("); |
||
2156 | } |
||
2157 | |||
2158 | // Adjust addend |
||
2159 | // Adjust offset if self-relative relocation expected and found |
||
2160 | if ((RelType & 2) && (Context & 0x108)) { |
||
2161 | // Self-relative relocation expected and found |
||
2162 | // Adjust by size of address field and immediate field |
||
2163 | Addend += IEnd - Relocations[irel].Offset; |
||
2164 | } |
||
2165 | // Subtract self-reference if unexpected self-relative relocation |
||
2166 | if ((RelType & 2) && !(Context & 0x108)) { |
||
2167 | // Self-relative relocation found but not expected |
||
2168 | // Fix difference between '$' and reference point |
||
2169 | Addend2 = Relocations[irel].Offset - IBegin; |
||
2170 | Addend -= Addend2; |
||
2171 | } |
||
2172 | // Add self-reference if self-relative relocation expected but not found |
||
2173 | if (!(RelType & 2) && (Context & 0x108)) { |
||
2174 | // Self-relative relocation expected but not found |
||
2175 | // Fix difference between '$' and reference point |
||
2176 | Addend += IEnd - IBegin; |
||
2177 | } |
||
2178 | |||
2179 | if (RelType & 0x100) { |
||
2180 | // Target is a segment |
||
2181 | RefFrame = Symbols[Symbols.Old2NewIndex(Target)].Section; |
||
2182 | if (RefFrame && RefFrame < Sections.GetNumEntries()) { |
||
2183 | const char * SecName = NameBuffer.Buf()+Sections[RefFrame].Name; |
||
2184 | OutFile.Put(SecName); |
||
2185 | } |
||
2186 | else { |
||
2187 | OutFile.Put("undefined segment"); |
||
2188 | } |
||
2189 | } |
||
2190 | else { |
||
2191 | // Target is a symbol |
||
2192 | |||
2193 | // Find target symbol |
||
2194 | uint32 TargetSym = Symbols.Old2NewIndex(Target); |
||
2195 | |||
2196 | // Check if Target is appropriate |
||
2197 | if (((Symbols[TargetSym].Type & 0x80000000) || (int32)Addend) |
||
2198 | && !(CodeMode == 1 && s.BaseReg)) { |
||
2199 | // Symbol is a start-of-section entry in symbol table, or has an addend |
||
2200 | // Look for a more appropriate symbol, except if code with base register |
||
2201 | uint32 sym, sym1, sym2 = 0; |
||
2202 | sym1 = Symbols.FindByAddress(Symbols[TargetSym].Section, Symbols[TargetSym].Offset + (int32)Addend, &sym2); |
||
2203 | for (sym = sym1; sym && sym <= sym2; sym++) { |
||
2204 | if (Symbols[sym].Scope && !(Symbols[sym].Type & 0x80000000)) { |
||
2205 | // Found a better symbol name for target address |
||
2206 | TargetSym = sym; |
||
2207 | Addend = Addend2; |
||
2208 | } |
||
2209 | } |
||
2210 | } |
||
2211 | // Write name of target symbol |
||
2212 | OutFile.Put(Symbols.GetName(TargetSym)); |
||
2213 | |||
2214 | if (Syntax == SUBTYPE_GASM && ( |
||
2215 | RelType == 0x41 || RelType == 0x81 || RelType == 0x2002)) { |
||
2216 | // make PLT entry |
||
2217 | OutFile.Put("@PLT"); |
||
2218 | } |
||
2219 | } |
||
2220 | |||
2221 | // End parenthesis if we started one |
||
2222 | if (RelType & 4) { |
||
2223 | OutFile.Put(")"); |
||
2224 | } |
||
2225 | |||
2226 | // Subtract reference point, if any |
||
2227 | if (RelType & 0x10) { |
||
2228 | OutFile.Put("-"); |
||
2229 | // Write name of segment/group frame |
||
2230 | uint32 RefPoint = Relocations[irel].RefOldIndex; |
||
2231 | if (RefPoint) { |
||
2232 | // Reference point name valid |
||
2233 | OutFile.Put(Symbols.GetNameO(RefPoint)); |
||
2234 | } |
||
2235 | else { |
||
2236 | OutFile.Put("Reference_Point_Missing"); |
||
2237 | } |
||
2238 | } |
||
2239 | |||
2240 | // Subtract self-reference if unexpected self-relative relocation |
||
2241 | if ((RelType & 2) && !(Context & 0x108)) { |
||
2242 | // Self-relative relocation found but not expected |
||
2243 | OutFile.Put("-"); OutFile.Put(HereOperator); |
||
2244 | } |
||
2245 | |||
2246 | // Add self-reference if self-relative relocation expected but not found |
||
2247 | if (!(RelType & 2) && (Context & 0x108)) { |
||
2248 | // Self-relative relocation expected but not found |
||
2249 | OutFile.Put("+"); OutFile.Put(HereOperator); |
||
2250 | } |
||
2251 | |||
2252 | // Write addend, if not zero |
||
2253 | if (Addend) { |
||
2254 | if (Addend < 0) { |
||
2255 | // Negative, write "-" |
||
2256 | OutFile.Put("-"); |
||
2257 | Addend = -Addend; |
||
2258 | } |
||
2259 | else { |
||
2260 | // Positive, write "+" |
||
2261 | OutFile.Put("+"); |
||
2262 | } |
||
2263 | |||
2264 | // Write value as hexadecimal |
||
2265 | switch (RelSize) { |
||
2266 | case 1: |
||
2267 | OutFile.PutHex((uint8)Addend, 1); |
||
2268 | break; |
||
2269 | case 2: |
||
2270 | OutFile.PutHex((uint16)Addend, 2); |
||
2271 | break; |
||
2272 | case 4: |
||
2273 | OutFile.PutHex((uint32)Addend, 2); |
||
2274 | break; |
||
2275 | case 6: |
||
2276 | OutFile.PutHex((uint16)(Addend >> 32), 1); |
||
2277 | OutFile.Put(":"); |
||
2278 | OutFile.PutHex((uint32)Addend, 1); |
||
2279 | break; |
||
2280 | case 8: |
||
2281 | OutFile.PutHex((uint64)Addend, 2); |
||
2282 | break; |
||
2283 | default: |
||
2284 | OutFile.Put("??"); // Should not occur |
||
2285 | break; |
||
2286 | } |
||
2287 | } |
||
2288 | } |
||
2289 | |||
2290 | |||
2291 | int CDisassembler::WriteFillers() { |
||
2292 | // Check if code is a series of NOPs or other fillers. |
||
2293 | // If so then write it as filler and return 1. |
||
2294 | // If not, then return 0. |
||
2295 | |||
2296 | // Check if code is filler |
||
2297 | if (!(OpcodeOptions & 0x40)) { |
||
2298 | // This instruction can not be used as filler |
||
2299 | return 0; |
||
2300 | } |
||
2301 | uint32 FillerType; // Type of filler |
||
2302 | const char * FillerName = s.OpcodeDef->Name; // Name of filler |
||
2303 | uint32 IFillerBegin = IBegin; // Start of filling space |
||
2304 | uint32 IFillerEnd; // End of filling space |
||
2305 | |||
2306 | // check for CC = int 3 breakpoint, 3C00 = 90 NOP, 11F = multibyte NOP |
||
2307 | if (Opcodei == 0xCC || (Opcodei & 0xFFFE) == 0x3C00 || Opcodei == 0x11F) { |
||
2308 | // Instruction is a NOP or int 3 breakpoint |
||
2309 | FillerType = Opcodei; |
||
2310 | } |
||
2311 | else if (s.Warnings1 & 0x1000000) { |
||
2312 | // Instruction is a LEA, MOV, etc. with same source and destination |
||
2313 | // used as a multi-byte NOP |
||
2314 | FillerType = 0xFFFFFFFF; |
||
2315 | } |
||
2316 | else { |
||
2317 | // This instruction does something. Not a filler |
||
2318 | return 0; |
||
2319 | } |
||
2320 | // Save beginning position |
||
2321 | IFillerEnd = IEnd = IBegin; |
||
2322 | |||
2323 | // Loop through instructions to find all consecutive fillers |
||
2324 | while (NextInstruction2()) { |
||
2325 | |||
2326 | // Parse instruction |
||
2327 | ParseInstruction(); |
||
2328 | |||
2329 | // Check if code is filler |
||
2330 | if (!(OpcodeOptions & 0x40)) { |
||
2331 | // This instruction can not be a filler |
||
2332 | // Save position of this instruction |
||
2333 | IFillerEnd = IBegin; |
||
2334 | break; |
||
2335 | } |
||
2336 | if (Opcodei != 0xCC && (Opcodei & 0xFFFE) != 0x3C00 && Opcodei != 0x11F |
||
2337 | && !(s.Warnings1 & 0x1000000)) { |
||
2338 | // Not a filler |
||
2339 | // Save position of this instruction |
||
2340 | IFillerEnd = IBegin; |
||
2341 | break; |
||
2342 | } |
||
2343 | // If loop exits here then fillers end at end of this instruction |
||
2344 | IFillerEnd = IEnd; |
||
2345 | } |
||
2346 | // Safety check |
||
2347 | if (IFillerEnd <= IFillerBegin) return 0; |
||
2348 | |||
2349 | // Size of fillers |
||
2350 | uint32 FillerSize = IFillerEnd - IFillerBegin; |
||
2351 | |||
2352 | // Write size of filling space |
||
2353 | OutFile.Put(CommentSeparator); |
||
2354 | OutFile.Put("Filling space: "); |
||
2355 | OutFile.PutHex(FillerSize, 2); |
||
2356 | OutFile.NewLine(); |
||
2357 | // Write filler type |
||
2358 | OutFile.Put(CommentSeparator); |
||
2359 | OutFile.Put("Filler type: "); |
||
2360 | switch (FillerType) { |
||
2361 | case 0xCC: |
||
2362 | FillerName = "INT 3 Debug breakpoint"; break; |
||
2363 | case 0x3C00: |
||
2364 | FillerName = "NOP"; break; |
||
2365 | case 0x3C01: |
||
2366 | FillerName = "NOP with prefixes"; break; |
||
2367 | case 0x011F: |
||
2368 | FillerName = "Multi-byte NOP";break; |
||
2369 | } |
||
2370 | OutFile.Put(FillerName); |
||
2371 | if (FillerType == 0xFFFFFFFF) { |
||
2372 | OutFile.Put(" with same source and destination"); |
||
2373 | } |
||
2374 | |||
2375 | // Write as bytes |
||
2376 | uint32 Pos; |
||
2377 | for (Pos = IFillerBegin; Pos < IFillerEnd; Pos++) { |
||
2378 | if (((Pos - IFillerBegin) & 7) == 0) { |
||
2379 | // Start new line |
||
2380 | OutFile.NewLine(); |
||
2381 | OutFile.Put(CommentSeparator); |
||
2382 | OutFile.Tabulate(AsmTab1); |
||
2383 | OutFile.Put(Syntax == SUBTYPE_GASM ? ".byte " : "db "); |
||
2384 | } |
||
2385 | else { |
||
2386 | // Continue on same line |
||
2387 | OutFile.Put(", "); |
||
2388 | } |
||
2389 | // Write byte value |
||
2390 | OutFile.PutHex(Get |
||
2391 | } |
||
2392 | // Blank line |
||
2393 | OutFile.NewLine(); OutFile.NewLine(); |
||
2394 | |||
2395 | // Find alignment |
||
2396 | uint32 Alignment = 4; // Limit to 2^4 = 16 |
||
2397 | |||
2398 | // Check if first non-filler is aligned by this value |
||
2399 | while (Alignment && (IFillerEnd & ((1 << Alignment) - 1))) { |
||
2400 | // Not aligned by 2^Alignment |
||
2401 | Alignment--; |
||
2402 | } |
||
2403 | if (Alignment) { |
||
2404 | |||
2405 | // Check if smaller alignment would do |
||
2406 | if (Alignment > 3 && FillerSize < 1u << (Alignment-1)) { |
||
2407 | // End is aligned by 16, but there are less than 8 filler bytes. |
||
2408 | // Change to align 8 |
||
2409 | Alignment--; |
||
2410 | } |
||
2411 | // Write align directive |
||
2412 | WriteAlign(1 << Alignment); |
||
2413 | // Prevent writing ALIGN again |
||
2414 | FlagPrevious &= ~1; |
||
2415 | } |
||
2416 | |||
2417 | // Restore IBegin and IEnd to beginning of first non-filler instruction |
||
2418 | IBegin = IEnd = IFillerEnd; |
||
2419 | |||
2420 | if (LabelInaccessible == IFillerBegin && IFillerEnd < LabelEnd) { |
||
2421 | // Mark first instruction after filler as inaccessible |
||
2422 | LabelInaccessible = IFillerEnd; |
||
2423 | } |
||
2424 | |||
2425 | // Return success. Fillers have been written. Don't write as normal instructions |
||
2426 | return 1; |
||
2427 | } |
||
2428 | |||
2429 | void CDisassembler::WriteAlign(uint32 a) { |
||
2430 | // Write alignment directive |
||
2431 | OutFile.Put(Syntax == SUBTYPE_GASM ? ".ALIGN" : "ALIGN"); |
||
2432 | OutFile.Tabulate(AsmTab1); |
||
2433 | OutFile.PutDecimal(a); |
||
2434 | OutFile.NewLine(); |
||
2435 | } |
||
2436 | |||
2437 | void CDisassembler::WriteFileBegin() { |
||
2438 | // Write begin of file |
||
2439 | |||
2440 | OutFile.SetFileType(FILETYPE_ASM); |
||
2441 | |||
2442 | // Initial comment |
||
2443 | OutFile.Put(CommentSeparator); |
||
2444 | OutFile.Put("Disassembly of file: "); |
||
2445 | OutFile.Put(cmd.InputFile); |
||
2446 | OutFile.NewLine(); |
||
2447 | // Date and time. |
||
2448 | // Note: will fail after year 2038 on computers that use 32-bit time_t |
||
2449 | time_t time1 = time(0); |
||
2450 | char * timestring = ctime(&time1); |
||
2451 | if (timestring) { |
||
2452 | // Remove terminating '\n' in timestring |
||
2453 | for (char *c = timestring; *c; c++) { |
||
2454 | if (*c < ' ') *c = 0; |
||
2455 | } |
||
2456 | // Write date and time as comment |
||
2457 | OutFile.Put(CommentSeparator); |
||
2458 | OutFile.Put(timestring); |
||
2459 | OutFile.NewLine(); |
||
2460 | } |
||
2461 | |||
2462 | // Write mode |
||
2463 | OutFile.Put(CommentSeparator); |
||
2464 | OutFile.Put("Mode: "); |
||
2465 | OutFile.PutDecimal(WordSize); |
||
2466 | OutFile.Put(" bits"); |
||
2467 | OutFile.NewLine(); |
||
2468 | |||
2469 | // Write syntax dialect |
||
2470 | OutFile.Put(CommentSeparator); |
||
2471 | OutFile.Put("Syntax: "); |
||
2472 | switch (Syntax) { |
||
2473 | case SUBTYPE_MASM: |
||
2474 | OutFile.Put(WordSize < 64 ? "MASM/ML" : "MASM/ML64"); break; |
||
2475 | case SUBTYPE_YASM: |
||
2476 | OutFile.Put("YASM/NASM"); break; |
||
2477 | case SUBTYPE_GASM: |
||
2478 | OutFile.Put("GAS(Intel)"); break; |
||
2479 | } |
||
2480 | OutFile.NewLine(); |
||
2481 | |||
2482 | // Write instruction set as comment |
||
2483 | // Instruction set is at least .386 if 32 bit mode |
||
2484 | if (InstructionSetMax < 3 && (MasmOptions & 0x200)) InstructionSetMax = 3; |
||
2485 | |||
2486 | // Get name of basic instruction set |
||
2487 | const char * set0 = ""; |
||
2488 | if (InstructionSetMax < InstructionSetNamesLen) { |
||
2489 | set0 = InstructionSetNames[InstructionSetMax]; |
||
2490 | } |
||
2491 | |||
2492 | // Write as comment |
||
2493 | OutFile.Put(CommentSeparator); |
||
2494 | OutFile.Put("Instruction set: "); |
||
2495 | OutFile.Put(set0); |
||
2496 | |||
2497 | if (InstructionSetAMDMAX) { |
||
2498 | // Get name of any AMD-specific instruction set |
||
2499 | const char * setA = ""; |
||
2500 | switch (InstructionSetAMDMAX) { |
||
2501 | case 1: setA = "AMD 3DNow"; break; |
||
2502 | case 2: setA = "AMD 3DNowE"; break; |
||
2503 | case 4: setA = "AMD SSE4a"; break; |
||
2504 | case 5: setA = "AMD XOP"; break; |
||
2505 | case 6: setA = "AMD FMA4"; break; |
||
2506 | case 7: setA = "AMD TBM"; break; |
||
2507 | } |
||
2508 | if (*setA) { |
||
2509 | OutFile.Put(", "); |
||
2510 | OutFile.Put(setA); |
||
2511 | } |
||
2512 | } |
||
2513 | // VIA instruction set: |
||
2514 | if (InstructionSetOR & 0x2000) OutFile.Put(", VIA"); |
||
2515 | |||
2516 | // Additional instruction sets: |
||
2517 | if (WordSize > 32) OutFile.Put(", x64"); |
||
2518 | if (InstructionSetOR & 0x100) OutFile.Put(", 80x87"); |
||
2519 | if (InstructionSetOR & 0x800) OutFile.Put(", privileged instructions"); |
||
2520 | OutFile.NewLine(); |
||
2521 | |||
2522 | if (NamesChanged) { |
||
2523 | // Tell that symbol names have been changed |
||
2524 | OutFile.NewLine(); |
||
2525 | OutFile.Put(CommentSeparator); |
||
2526 | OutFile.Put("Error: symbol names contain illegal characters,"); |
||
2527 | OutFile.NewLine(); OutFile.Put(CommentSeparator); |
||
2528 | OutFile.PutDecimal(NamesChanged); |
||
2529 | #if ReplaceIllegalChars |
||
2530 | OutFile.Put(" Symbol names changed"); |
||
2531 | #else |
||
2532 | OutFile.Put(" Symbol names not changed"); |
||
2533 | #endif |
||
2534 | OutFile.NewLine(); |
||
2535 | } |
||
2536 | |||
2537 | // Write syntax-specific initializations |
||
2538 | switch (Syntax) { |
||
2539 | case SUBTYPE_MASM: |
||
2540 | WriteFileBeginMASM(); |
||
2541 | WritePublicsAndExternalsMASM(); |
||
2542 | break; |
||
2543 | case SUBTYPE_YASM: |
||
2544 | WriteFileBeginYASM(); |
||
2545 | WritePublicsAndExternalsYASMGASM(); |
||
2546 | break; |
||
2547 | case SUBTYPE_GASM: |
||
2548 | WriteFileBeginGASM(); |
||
2549 | WritePublicsAndExternalsYASMGASM(); |
||
2550 | break; |
||
2551 | } |
||
2552 | } |
||
2553 | |||
2554 | |||
2555 | void CDisassembler::WriteFileBeginMASM() { |
||
2556 | // Write MASM-specific file init |
||
2557 | if (WordSize < 64) { |
||
2558 | // Write instruction set directive, except for 64 bit assembler |
||
2559 | const char * set1 = ""; |
||
2560 | switch (InstructionSetMax) { |
||
2561 | case 0: set1 = ".8086"; break; |
||
2562 | case 1: set1 = ".186"; break; |
||
2563 | case 2: set1 = ".286"; break; |
||
2564 | case 3: set1 = ".386"; break; |
||
2565 | case 4: set1 = ".486"; break; |
||
2566 | case 5: set1 = ".586"; break; |
||
2567 | case 6: default: |
||
2568 | set1 = ".686"; break; |
||
2569 | } |
||
2570 | // Write basic instruction set |
||
2571 | OutFile.NewLine(); |
||
2572 | OutFile.Put(set1); |
||
2573 | if (InstructionSetOR & 0x800) { |
||
2574 | // Privileged. Add "p" |
||
2575 | OutFile.Put("p"); |
||
2576 | } |
||
2577 | OutFile.NewLine(); |
||
2578 | // Write extended instruction set |
||
2579 | if (InstructionSetOR & 0x100) { |
||
2580 | // Floating point |
||
2581 | if (InstructionSetMax < 3) { |
||
2582 | OutFile.Put(".8087"); OutFile.NewLine(); |
||
2583 | } |
||
2584 | else if (InstructionSetMax < 5) { |
||
2585 | OutFile.Put(".387"); OutFile.NewLine(); |
||
2586 | } |
||
2587 | } |
||
2588 | if (InstructionSetMax >= 0x11) { |
||
2589 | // .xmm directive. Not differentiated between SSE, SSE2, etc. |
||
2590 | OutFile.Put(".xmm"); OutFile.NewLine(); |
||
2591 | } |
||
2592 | else if (InstructionSetMax >= 7) { |
||
2593 | // .mmx directive |
||
2594 | OutFile.Put(".mmx"); OutFile.NewLine(); |
||
2595 | } |
||
2596 | } |
||
2597 | if (MasmOptions & 1) { |
||
2598 | // Need dotname option |
||
2599 | OutFile.Put("option dotname"); OutFile.NewLine(); |
||
2600 | } |
||
2601 | if (WordSize == 32) { |
||
2602 | // Write .model flat if 32 bit mode |
||
2603 | OutFile.Put(".model flat"); OutFile.NewLine(); |
||
2604 | } |
||
2605 | // Initialize Assumes for segment registers |
||
2606 | if (!(MasmOptions & 0x100)) { |
||
2607 | // No 16-bit segments. Assume CS=DS=ES=SS=flat |
||
2608 | Assumes[0]=Assumes[1]=Assumes[2]=Assumes[3] = ASM_SEGMENT_FLAT; |
||
2609 | } |
||
2610 | else { |
||
2611 | // 16-bit segmented model. Segment register values unknown |
||
2612 | Assumes[0]=Assumes[1]=Assumes[2]=Assumes[3] = ASM_SEGMENT_UNKNOWN; |
||
2613 | } |
||
2614 | // FS and GS assumed to ERROR |
||
2615 | Assumes[4] = Assumes[5] = ASM_SEGMENT_ERROR; |
||
2616 | |||
2617 | // Write assume if FS or GS used |
||
2618 | // This is superfluous because an assume directive will be written at first use of FS/GS |
||
2619 | if (MasmOptions & 2) { |
||
2620 | OutFile.Put("assume fs:nothing"); OutFile.NewLine(); |
||
2621 | } |
||
2622 | if (MasmOptions & 4) { |
||
2623 | OutFile.Put("assume gs:nothing"); OutFile.NewLine(); |
||
2624 | } |
||
2625 | OutFile.NewLine(); // Blank line |
||
2626 | } |
||
2627 | |||
2628 | void CDisassembler::WriteFileBeginYASM() { |
||
2629 | // Write YASM-specific file init |
||
2630 | OutFile.NewLine(); |
||
2631 | if (WordSize == 64) { |
||
2632 | OutFile.Put("default rel"); OutFile.NewLine(); |
||
2633 | } |
||
2634 | //if (InstructionSetMax >= 0x11) {OutFile.Put("%define xmmword oword"); OutFile.NewLine();} |
||
2635 | //if (InstructionSetMax >= 0x19) {OutFile.Put("%define ymmword"); OutFile.NewLine();} |
||
2636 | OutFile.NewLine(); |
||
2637 | } |
||
2638 | |||
2639 | void CDisassembler::WriteFileBeginGASM() { |
||
2640 | // Write GAS-specific file init |
||
2641 | OutFile.NewLine(); |
||
2642 | OutFile.Put(CommentSeparator); |
||
2643 | OutFile.Put("Note: Uses Intel syntax with destination operand first. Remember to"); |
||
2644 | OutFile.NewLine(); |
||
2645 | OutFile.Put(CommentSeparator); |
||
2646 | OutFile.Put("put syntax directives in the beginning and end of inline assembly:"); |
||
2647 | OutFile.NewLine(); |
||
2648 | OutFile.Put(".intel_syntax noprefix "); |
||
2649 | OutFile.NewLine(); OutFile.NewLine(); |
||
2650 | } |
||
2651 | |||
2652 | void CDisassembler::WritePublicsAndExternalsMASM() { |
||
2653 | // Write public and external symbol definitions |
||
2654 | uint32 i; // Loop counter |
||
2655 | uint32 LinesWritten = 0; // Count lines written |
||
2656 | const char * XName; // Name of external symbols |
||
2657 | |||
2658 | // Loop through public symbols |
||
2659 | for (i = 0; i < Symbols.GetNumEntries(); i++) { |
||
2660 | if (Symbols[i].Scope & 0x1C) { |
||
2661 | // Symbol is public |
||
2662 | OutFile.Put("public "); |
||
2663 | // Write name |
||
2664 | OutFile.Put(Symbols.GetName(i)); |
||
2665 | // Check if weak or communal |
||
2666 | if (Symbols[i].Scope & 0x18) { |
||
2667 | // Scope is weak or communal |
||
2668 | OutFile.Tabulate(AsmTab3); |
||
2669 | OutFile.Put(CommentSeparator); |
||
2670 | if (Symbols[i].Scope & 8) OutFile.Put("Note: Weak. Not supported by MASM "); |
||
2671 | if (Symbols[i].Scope & 0x10) OutFile.Put("Note: Communal. Not supported by MASM"); |
||
2672 | } |
||
2673 | OutFile.NewLine(); LinesWritten++; |
||
2674 | } |
||
2675 | } |
||
2676 | // Blank line if anything written |
||
2677 | if (LinesWritten) { |
||
2678 | OutFile.NewLine(); |
||
2679 | LinesWritten = 0; |
||
2680 | } |
||
2681 | // Loop through external symbols |
||
2682 | for (i = 0; i < Symbols.GetNumEntries(); i++) { |
||
2683 | |||
2684 | if (Symbols[i].Scope & 0x20) { |
||
2685 | // Symbol is external |
||
2686 | OutFile.Put("extern "); |
||
2687 | // Get name |
||
2688 | XName = Symbols.GetName(i); |
||
2689 | // Check for dynamic import |
||
2690 | if (Symbols[i].DLLName && strncmp(XName, Symbols.ImportTablePrefix, (uint32)strlen(Symbols.ImportTablePrefix)) == 0) { |
||
2691 | // Remove "_imp" prefix from name |
||
2692 | XName += (uint32)strlen(Symbols.ImportTablePrefix); |
||
2693 | } |
||
2694 | |||
2695 | // Write name |
||
2696 | OutFile.Put(XName); |
||
2697 | OutFile.Put(": "); |
||
2698 | |||
2699 | // Write type |
||
2700 | if ((Symbols[i].Type & 0xFE) == 0x84) { |
||
2701 | // Far |
||
2702 | OutFile.Put("far"); |
||
2703 | } |
||
2704 | else if ((Symbols[i].Type & 0xF0) == 0x80 || Symbols[i].DLLName) { |
||
2705 | // Near |
||
2706 | OutFile.Put("near"); |
||
2707 | } |
||
2708 | else { |
||
2709 | // Data. Write size |
||
2710 | switch (GetDataItemSize(Symbols[i].Type)) { |
||
2711 | case 1: default: OutFile.Put("byte"); break; |
||
2712 | case 2: OutFile.Put("word"); break; |
||
2713 | case 4: OutFile.Put("dword"); break; |
||
2714 | case 6: OutFile.Put("fword"); break; |
||
2715 | case 8: OutFile.Put("qword"); break; |
||
2716 | case 10: OutFile.Put("tbyte"); break; |
||
2717 | case 16: OutFile.Put("xmmword"); break; |
||
2718 | case 32: OutFile.Put("ymmword"); break; |
||
2719 | } |
||
2720 | } |
||
2721 | // Add comment if DLL import |
||
2722 | if (Symbols[i].DLLName) { |
||
2723 | OutFile.Tabulate(AsmTab3); |
||
2724 | OutFile.Put(CommentSeparator); |
||
2725 | OutFile.Put(Symbols.GetDLLName(i)); |
||
2726 | } |
||
2727 | // Finished line |
||
2728 | OutFile.NewLine(); LinesWritten++; |
||
2729 | } |
||
2730 | } |
||
2731 | // Blank line if anything written |
||
2732 | if (LinesWritten) { |
||
2733 | OutFile.NewLine(); |
||
2734 | LinesWritten = 0; |
||
2735 | } |
||
2736 | // Write the value of any constants |
||
2737 | // Loop through symbols |
||
2738 | for (i = 0; i < Symbols.GetNumEntries(); i++) { |
||
2739 | // Local symbols included because there might be a rip-relative address to a named constant = 0 |
||
2740 | if (Symbols[i].Section == ASM_SEGMENT_ABSOLUTE /*&& (Symbols[i].Scope & 0x1C)*/) { |
||
2741 | // Symbol is constant |
||
2742 | // Write name |
||
2743 | OutFile.Put(Symbols.GetName(i)); |
||
2744 | OutFile.Put(" equ "); |
||
2745 | // Write value as hexadecimal |
||
2746 | OutFile.PutHex(Symbols[i].Offset, 1); |
||
2747 | // Write decimal value as comment |
||
2748 | OutFile.Tabulate(AsmTab3); |
||
2749 | OutFile.Put(CommentSeparator); |
||
2750 | OutFile.PutDecimal(Symbols[i].Offset, 1); |
||
2751 | OutFile.NewLine(); LinesWritten++; |
||
2752 | } |
||
2753 | } |
||
2754 | // Blank line if anything written |
||
2755 | if (LinesWritten) { |
||
2756 | OutFile.NewLine(); |
||
2757 | LinesWritten = 0; |
||
2758 | } |
||
2759 | // Write any group definitions |
||
2760 | int32 GroupId, SegmentId; |
||
2761 | // Loop through sections to search for group definitions |
||
2762 | for (GroupId = 1; GroupId < (int32)Sections.GetNumEntries(); GroupId++) { |
||
2763 | |||
2764 | // Get section type |
||
2765 | uint32 SectionType = Sections[GroupId].Type; |
||
2766 | if (SectionType & 0x800) { |
||
2767 | // This is a segment group definition |
||
2768 | // Count number of members |
||
2769 | uint32 NumMembers = 0; |
||
2770 | // Write group name |
||
2771 | WriteSectionName(GroupId); |
||
2772 | // Write "group" |
||
2773 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); OutFile.Put("GROUP "); |
||
2774 | // Search for group members |
||
2775 | for (SegmentId = 1; SegmentId < (int32)Sections.GetNumEntries(); SegmentId++) { |
||
2776 | if (Sections[SegmentId].Group == GroupId && !(Sections[SegmentId].Type & 0x800)) { |
||
2777 | // is this first member? |
||
2778 | if (NumMembers++) { |
||
2779 | // Not first member. Write comma |
||
2780 | OutFile.Put(", "); |
||
2781 | } |
||
2782 | // Write group member |
||
2783 | WriteSectionName(SegmentId); |
||
2784 | } |
||
2785 | } |
||
2786 | // End line |
||
2787 | OutFile.NewLine(); LinesWritten++; |
||
2788 | } |
||
2789 | } |
||
2790 | // Blank line if anything written |
||
2791 | if (LinesWritten) { |
||
2792 | OutFile.NewLine(); |
||
2793 | LinesWritten = 0; |
||
2794 | } |
||
2795 | } |
||
2796 | |||
2797 | |||
2798 | void CDisassembler::WritePublicsAndExternalsYASMGASM() { |
||
2799 | // Write public and external symbol definitions, YASM and GAS syntax |
||
2800 | uint32 i; // Loop counter |
||
2801 | uint32 LinesWritten = 0; // Count lines written |
||
2802 | const char * XName; // Name of external symbols |
||
2803 | |||
2804 | // Loop through public symbols |
||
2805 | for (i = 0; i < Symbols.GetNumEntries(); i++) { |
||
2806 | if (Symbols[i].Scope & 0x1C) { |
||
2807 | // Symbol is public |
||
2808 | if (Syntax == SUBTYPE_GASM) OutFile.Put("."); |
||
2809 | OutFile.Put("global "); |
||
2810 | // Write name |
||
2811 | OutFile.Put(Symbols.GetName(i)); |
||
2812 | |||
2813 | // Write type |
||
2814 | if ((Symbols[i].Type & 0xF0) == 0x80) { |
||
2815 | // Symbol is a function |
||
2816 | if (Syntax == SUBTYPE_YASM) { |
||
2817 | OutFile.Put(": function"); |
||
2818 | } |
||
2819 | else if (Syntax == SUBTYPE_GASM) { |
||
2820 | OutFile.NewLine(); |
||
2821 | OutFile.Put(".type "); |
||
2822 | OutFile.Put(Symbols.GetName(i)); |
||
2823 | OutFile.Put(", @function"); |
||
2824 | } |
||
2825 | } |
||
2826 | |||
2827 | // Check if weak or communal |
||
2828 | if (Symbols[i].Scope & 0x18) { |
||
2829 | // Scope is weak or communal |
||
2830 | OutFile.Tabulate(AsmTab3); |
||
2831 | OutFile.Put(CommentSeparator); |
||
2832 | if (Symbols[i].Scope & 8) OutFile.Put("Note: Weak."); |
||
2833 | if (Symbols[i].Scope & 0x10) OutFile.Put("Note: Communal."); |
||
2834 | } |
||
2835 | OutFile.NewLine(); LinesWritten++; |
||
2836 | } |
||
2837 | } |
||
2838 | // Blank line if anything written |
||
2839 | if (LinesWritten) { |
||
2840 | OutFile.NewLine(); |
||
2841 | LinesWritten = 0; |
||
2842 | } |
||
2843 | // Loop through external symbols |
||
2844 | for (i = 0; i < Symbols.GetNumEntries(); i++) { |
||
2845 | |||
2846 | if (Symbols[i].Scope & 0x20) { |
||
2847 | // Symbol is external |
||
2848 | if (Syntax == SUBTYPE_GASM) OutFile.Put("."); |
||
2849 | OutFile.Put("extern "); |
||
2850 | // Get name |
||
2851 | XName = Symbols.GetName(i); |
||
2852 | // Check for dynamic import |
||
2853 | if (Symbols[i].DLLName && strncmp(XName, Symbols.ImportTablePrefix, (uint32)strlen(Symbols.ImportTablePrefix)) == 0) { |
||
2854 | // Remove "_imp" prefix from name |
||
2855 | XName += (uint32)strlen(Symbols.ImportTablePrefix); |
||
2856 | } |
||
2857 | // Write name |
||
2858 | OutFile.Put(XName); |
||
2859 | OutFile.Put(" "); |
||
2860 | OutFile.Tabulate(AsmTab3); |
||
2861 | OutFile.Put(CommentSeparator); |
||
2862 | |||
2863 | // Write type |
||
2864 | if ((Symbols[i].Type & 0xFE) == 0x84) { |
||
2865 | // Far |
||
2866 | OutFile.Put("far"); |
||
2867 | } |
||
2868 | else if ((Symbols[i].Type & 0xF0) == 0x80 || Symbols[i].DLLName) { |
||
2869 | // Near |
||
2870 | OutFile.Put("near"); |
||
2871 | } |
||
2872 | else { |
||
2873 | // Data. Write size |
||
2874 | switch (GetDataItemSize(Symbols[i].Type)) { |
||
2875 | case 1: default: OutFile.Put("byte"); break; |
||
2876 | case 2: OutFile.Put("word"); break; |
||
2877 | case 4: OutFile.Put("dword"); break; |
||
2878 | case 6: OutFile.Put("fword"); break; |
||
2879 | case 8: OutFile.Put("qword"); break; |
||
2880 | case 10: OutFile.Put("tbyte"); break; |
||
2881 | case 16: OutFile.Put("xmmword"); break; |
||
2882 | case 32: OutFile.Put("ymmword"); break; |
||
2883 | } |
||
2884 | } |
||
2885 | // Add comment if DLL import |
||
2886 | if (Symbols[i].DLLName) { |
||
2887 | OutFile.Tabulate(AsmTab3); |
||
2888 | OutFile.Put(CommentSeparator); |
||
2889 | OutFile.Put(Symbols.GetDLLName(i)); |
||
2890 | } |
||
2891 | // Finished line |
||
2892 | OutFile.NewLine(); LinesWritten++; |
||
2893 | } |
||
2894 | } |
||
2895 | // Blank line if anything written |
||
2896 | if (LinesWritten) { |
||
2897 | OutFile.NewLine(); LinesWritten = 0; |
||
2898 | } |
||
2899 | // Write the value of any constants |
||
2900 | // Loop through symbols |
||
2901 | for (i = 0; i < Symbols.GetNumEntries(); i++) { |
||
2902 | if (Symbols[i].Section == ASM_SEGMENT_ABSOLUTE /*&& (Symbols[i].Scope & 0x1C)*/) { |
||
2903 | // Symbol is constant |
||
2904 | if (Syntax == SUBTYPE_YASM) { |
||
2905 | // Write name equ value |
||
2906 | OutFile.Put(Symbols.GetName(i)); |
||
2907 | OutFile.Put(" equ "); |
||
2908 | } |
||
2909 | else { |
||
2910 | // Gas: write .equ name, value |
||
2911 | OutFile.Put(".equ "); |
||
2912 | OutFile.Tabulate(AsmTab1); |
||
2913 | OutFile.Put(Symbols.GetName(i)); |
||
2914 | OutFile.Put(", "); |
||
2915 | } |
||
2916 | // Write value as hexadecimal |
||
2917 | OutFile.PutHex(Symbols[i].Offset, 1); |
||
2918 | // Write decimal value as comment |
||
2919 | OutFile.Tabulate(AsmTab3); |
||
2920 | OutFile.Put(CommentSeparator); |
||
2921 | OutFile.PutDecimal(Symbols[i].Offset, 1); |
||
2922 | OutFile.NewLine(); LinesWritten++; |
||
2923 | } |
||
2924 | } |
||
2925 | // Blank line if anything written |
||
2926 | if (LinesWritten) { |
||
2927 | OutFile.NewLine(); |
||
2928 | LinesWritten = 0; |
||
2929 | } |
||
2930 | // Write any group definitions |
||
2931 | int32 GroupId, SegmentId; |
||
2932 | // Loop through sections to search for group definitions |
||
2933 | for (GroupId = 1; GroupId < (int32)Sections.GetNumEntries(); GroupId++) { |
||
2934 | // Get section type |
||
2935 | uint32 SectionType = Sections[GroupId].Type; |
||
2936 | if (SectionType & 0x800) { |
||
2937 | // This is a segment group definition |
||
2938 | // Count number of members |
||
2939 | uint32 NumMembers = 0; |
||
2940 | // Write group name |
||
2941 | WriteSectionName(GroupId); |
||
2942 | // Write "group" |
||
2943 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); OutFile.Put("GROUP "); |
||
2944 | // Search for group members |
||
2945 | for (SegmentId = 1; SegmentId < (int32)Sections.GetNumEntries(); SegmentId++) { |
||
2946 | if (Sections[SegmentId].Group == GroupId && !(Sections[SegmentId].Type & 0x800)) { |
||
2947 | // is this first member? |
||
2948 | if (NumMembers++) { |
||
2949 | // Not first member. Write comma |
||
2950 | OutFile.Put(", "); |
||
2951 | } |
||
2952 | // Write group member |
||
2953 | WriteSectionName(SegmentId); |
||
2954 | } |
||
2955 | } |
||
2956 | // End line |
||
2957 | OutFile.NewLine(); LinesWritten++; |
||
2958 | } |
||
2959 | } |
||
2960 | // Blank line if anything written |
||
2961 | if (LinesWritten) { |
||
2962 | OutFile.NewLine(); |
||
2963 | LinesWritten = 0; |
||
2964 | } |
||
2965 | } |
||
2966 | |||
2967 | |||
2968 | void CDisassembler::WriteFileEnd() { |
||
2969 | // Write end of file |
||
2970 | OutFile.NewLine(); |
||
2971 | switch(Syntax) { |
||
2972 | case SUBTYPE_MASM: |
||
2973 | OutFile.Put("END"); break; |
||
2974 | case SUBTYPE_GASM: |
||
2975 | OutFile.Put(CommentSeparator); |
||
2976 | OutFile.Put("Return to AT&T syntax with destination operand last:"); |
||
2977 | OutFile.NewLine(); |
||
2978 | OutFile.Put(".att_syntax prefix "); |
||
2979 | OutFile.NewLine(); |
||
2980 | break; |
||
2981 | case SUBTYPE_YASM: |
||
2982 | break; |
||
2983 | } |
||
2984 | } |
||
2985 | |||
2986 | |||
2987 | void CDisassembler::WriteSegmentBegin() { |
||
2988 | // Write begin of segment |
||
2989 | // Choose dialect |
||
2990 | switch (Syntax) { |
||
2991 | case SUBTYPE_MASM: |
||
2992 | WriteSegmentBeginMASM(); break; |
||
2993 | case SUBTYPE_YASM: |
||
2994 | WriteSegmentBeginYASM(); break; |
||
2995 | case SUBTYPE_GASM: |
||
2996 | WriteSegmentBeginGASM(); break; |
||
2997 | } |
||
2998 | } |
||
2999 | |||
3000 | |||
3001 | void CDisassembler::WriteSegmentBeginMASM() { |
||
3002 | // Write begin of segment |
||
3003 | OutFile.NewLine(); // Blank line |
||
3004 | |||
3005 | // Check if Section is valid |
||
3006 | if (Section == 0 || Section >= Sections.GetNumEntries()) { |
||
3007 | // Illegal segment entry |
||
3008 | OutFile.Put("UNKNOWN SEGMENT"); OutFile.NewLine(); |
||
3009 | return; |
||
3010 | } |
||
3011 | |||
3012 | // Write segment name |
||
3013 | WriteSectionName(Section); |
||
3014 | // Tabulate |
||
3015 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); |
||
3016 | // Write "segment" |
||
3017 | OutFile.Put("SEGMENT "); |
||
3018 | |||
3019 | // Write alignment |
||
3020 | switch (Sections[Section].Align) { |
||
3021 | case 0: // 1 |
||
3022 | OutFile.Put("BYTE "); break; |
||
3023 | case 1: // 2 |
||
3024 | OutFile.Put("WORD "); break; |
||
3025 | case 2: // 4 |
||
3026 | OutFile.Put("DWORD "); break; |
||
3027 | case 4: // 16 |
||
3028 | OutFile.Put("PARA "); break; |
||
3029 | //case 8: // 256 or 4096. Definition is ambiguous! |
||
3030 | // OutFile.Put("PAGE "); break; |
||
3031 | default: |
||
3032 | // Non-standard alignment |
||
3033 | OutFile.Put("ALIGN("); |
||
3034 | OutFile.PutDecimal(1 << Sections[Section].Align); |
||
3035 | OutFile.Put(") "); |
||
3036 | break; |
||
3037 | } |
||
3038 | if (WordSize != 64) { |
||
3039 | // "PUBLIC" not supported by ml64 assembler |
||
3040 | OutFile.Put("PUBLIC "); |
||
3041 | // Write segment word size if necessary |
||
3042 | if (MasmOptions & 0x100) { |
||
3043 | // There is at least one 16-bit segment. Write segment word size |
||
3044 | OutFile.Put("USE"); |
||
3045 | OutFile.PutDecimal(Sections[Section].WordSize); |
||
3046 | OutFile.Put(" "); |
||
3047 | } |
||
3048 | } |
||
3049 | // Write segment class |
||
3050 | switch (Sections[Section].Type & 0xFF) { |
||
3051 | case 1: |
||
3052 | OutFile.Put("'CODE'"); break; |
||
3053 | case 2: |
||
3054 | OutFile.Put("'DATA'"); break; |
||
3055 | case 3: |
||
3056 | OutFile.Put("'BSS'"); break; |
||
3057 | case 4: |
||
3058 | OutFile.Put("'CONST'"); break; |
||
3059 | default:; |
||
3060 | // Unknown class. Write nothing |
||
3061 | } |
||
3062 | |||
3063 | // Tabulate to comment |
||
3064 | OutFile.Put(" "); OutFile.Tabulate(AsmTab3); |
||
3065 | OutFile.Put(CommentSeparator); |
||
3066 | // Write section number |
||
3067 | OutFile.Put("section number "); |
||
3068 | OutFile.PutDecimal(Section); |
||
3069 | |||
3070 | // New line |
||
3071 | OutFile.NewLine(); |
||
3072 | |||
3073 | if (Sections[Section].Type & 0x1000) { |
||
3074 | // Communal |
||
3075 | OutFile.Put(CommentSeparator); |
||
3076 | OutFile.Put(" Communal section not supported by MASM"); |
||
3077 | OutFile.NewLine(); |
||
3078 | } |
||
3079 | |||
3080 | if (WordSize == 16 && Sections[Section].Type == 1) { |
||
3081 | // 16 bit code segment. Write ASSUME CS: SEGMENTNAME |
||
3082 | OutFile.Put("ASSUME "); |
||
3083 | OutFile.Tabulate(AsmTab1); |
||
3084 | OutFile.Put("CS:"); |
||
3085 | if (Sections[Section].Group) { |
||
3086 | // Group name takes precedence over segment name |
||
3087 | WriteSectionName(Sections[Section].Group); |
||
3088 | } |
||
3089 | else { |
||
3090 | WriteSectionName(Section); |
||
3091 | } |
||
3092 | OutFile.NewLine(); |
||
3093 | Assumes[1] = Section; |
||
3094 | } |
||
3095 | } |
||
3096 | |||
3097 | void CDisassembler::WriteSegmentBeginYASM() { |
||
3098 | // Write begin of segment |
||
3099 | OutFile.NewLine(); // Blank line |
||
3100 | |||
3101 | // Check if Section is valid |
||
3102 | if (Section == 0 || Section >= Sections.GetNumEntries()) { |
||
3103 | // Illegal segment entry |
||
3104 | OutFile.Put("UNKNOWN SEGMENT"); OutFile.NewLine(); |
||
3105 | return; |
||
3106 | } |
||
3107 | |||
3108 | // Write SECTION directive |
||
3109 | OutFile.Put("SECTION "); |
||
3110 | // Write segment name |
||
3111 | WriteSectionName(Section); |
||
3112 | // Tabulate |
||
3113 | OutFile.Put(" "); OutFile.Tabulate(AsmTab2); |
||
3114 | OutFile.Put("align="); |
||
3115 | OutFile.PutDecimal(1 << Sections[Section].Align); |
||
3116 | if (Sections[Section].WordSize != WordSize) { |
||
3117 | OutFile.Put(" use"); |
||
3118 | OutFile.PutDecimal(Sections[Section].WordSize); |
||
3119 | } |
||
3120 | if ((Sections[Section].Type & 0xFF) == 1) { |
||
3121 | OutFile.Put(" execute"); |
||
3122 | } |
||
3123 | else { |
||
3124 | OutFile.Put(" noexecute"); |
||
3125 | } |
||
3126 | |||
3127 | // Tabulate to comment |
||
3128 | OutFile.Put(" "); OutFile.Tabulate(AsmTab3); |
||
3129 | OutFile.Put(CommentSeparator); |
||
3130 | // Write section number |
||
3131 | OutFile.Put("section number "); |
||
3132 | OutFile.PutDecimal(Section); |
||
3133 | // Write type |
||
3134 | OutFile.Put(", "); |
||
3135 | switch (Sections[Section].Type & 0xFF) { |
||
3136 | case 1: OutFile.Put("code"); break; |
||
3137 | case 2: OutFile.Put("data"); break; |
||
3138 | case 3: OutFile.Put("bss"); break; |
||
3139 | case 4: OutFile.Put("const"); break; |
||
3140 | default: OutFile.Put("unknown type: "); |
||
3141 | OutFile.PutHex(Sections[Section].Type & 0xFF); |
||
3142 | break; |
||
3143 | } |
||
3144 | |||
3145 | // New line |
||
3146 | OutFile.NewLine(); |
||
3147 | |||
3148 | if (Sections[Section].Type & 0x1000) { |
||
3149 | // Communal |
||
3150 | OutFile.Put(CommentSeparator); |
||
3151 | OutFile.Put(" Communal section not supported by YASM"); |
||
3152 | OutFile.NewLine(); |
||
3153 | } |
||
3154 | } |
||
3155 | |||
3156 | void CDisassembler::WriteSegmentBeginGASM() { |
||
3157 | // Write begin of segment |
||
3158 | uint32 Type; // Section type |
||
3159 | |||
3160 | OutFile.NewLine(); // Blank line |
||
3161 | |||
3162 | // Check if Section is valid |
||
3163 | if (Section == 0 || Section >= Sections.GetNumEntries()) { |
||
3164 | // Illegal segment entry |
||
3165 | OutFile.Put("UNKNOWN SEGMENT"); OutFile.NewLine(); |
||
3166 | return; |
||
3167 | } |
||
3168 | |||
3169 | // Write SECTION directive |
||
3170 | OutFile.Put(".SECTION "); |
||
3171 | OutFile.Tabulate(AsmTab1); |
||
3172 | // Write segment name |
||
3173 | WriteSectionName(Section); |
||
3174 | // Tabulate |
||
3175 | OutFile.Put(" "); OutFile.Tabulate(AsmTab2); |
||
3176 | // Flags not supported by all versions of Gas. Put as comment: |
||
3177 | OutFile.Put(CommentSeparator); |
||
3178 | // Write flags |
||
3179 | OutFile.Put('"'); |
||
3180 | Type = Sections[Section].Type & 0xFF; |
||
3181 | if (Type) OutFile.Put('a'); // Allocatable |
||
3182 | if (Type != 1 && Type != 4) OutFile.Put('w'); // Writeable |
||
3183 | if (Type == 1) OutFile.Put('x'); // Executable |
||
3184 | OutFile.Put('"'); |
||
3185 | if (Type) OutFile.Put(", @progbits"); // Allocatable |
||
3186 | |||
3187 | // Tabulate to comment |
||
3188 | OutFile.Put(" "); OutFile.Tabulate(AsmTab3); |
||
3189 | OutFile.Put(CommentSeparator); |
||
3190 | // Write section number |
||
3191 | OutFile.Put("section number "); |
||
3192 | OutFile.PutDecimal(Section); |
||
3193 | // Write type |
||
3194 | OutFile.Put(", "); |
||
3195 | switch (Sections[Section].Type & 0xFF) { |
||
3196 | case 1: OutFile.Put("code"); break; |
||
3197 | case 2: OutFile.Put("data"); break; |
||
3198 | case 3: OutFile.Put("bss"); break; |
||
3199 | case 4: OutFile.Put("const"); break; |
||
3200 | default: OutFile.Put("unknown"); break; |
||
3201 | } |
||
3202 | OutFile.NewLine(); // Blank line |
||
3203 | if (Sections[Section].Type & 0x1000) { |
||
3204 | // Communal |
||
3205 | OutFile.Put(CommentSeparator); |
||
3206 | OutFile.Put(" Communal section "); |
||
3207 | OutFile.NewLine(); |
||
3208 | } |
||
3209 | |||
3210 | // Write alignment |
||
3211 | OutFile.Tabulate(AsmTab1); |
||
3212 | OutFile.Put(".ALIGN"); |
||
3213 | OutFile.Tabulate(AsmTab2); |
||
3214 | OutFile.PutDecimal(1 << Sections[Section].Align); |
||
3215 | |||
3216 | // New line |
||
3217 | OutFile.NewLine(); |
||
3218 | } |
||
3219 | |||
3220 | |||
3221 | void CDisassembler::WriteSegmentEnd() { |
||
3222 | // Write end of segment |
||
3223 | OutFile.NewLine(); |
||
3224 | |||
3225 | if (Syntax != SUBTYPE_MASM) { |
||
3226 | // Not MASM syntax, write only blank line |
||
3227 | return; |
||
3228 | } |
||
3229 | |||
3230 | // Check if Section is valid |
||
3231 | if (Section == 0 || Section >= Sections.GetNumEntries()) { |
||
3232 | // Illegal segment entry |
||
3233 | OutFile.Put("UNKNOWN ENDS"); OutFile.NewLine(); |
||
3234 | return; |
||
3235 | } |
||
3236 | |||
3237 | // Write segment name |
||
3238 | const char * segname = NameBuffer.Buf() + Sections[Section].Name; |
||
3239 | OutFile.Put(segname); |
||
3240 | |||
3241 | // Tabulate |
||
3242 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); |
||
3243 | // Write "segment" |
||
3244 | OutFile.Put("ENDS"); |
||
3245 | // New line |
||
3246 | OutFile.NewLine(); |
||
3247 | } |
||
3248 | |||
3249 | |||
3250 | |||
3251 | void CDisassembler::WriteFunctionBegin() { |
||
3252 | // Write begin of function IFunction |
||
3253 | |||
3254 | // Check if IFunction is valid |
||
3255 | if (IFunction == 0 || IFunction >= FunctionList.GetNumEntries()) { |
||
3256 | // Should not occur |
||
3257 | OutFile.Put(CommentSeparator); |
||
3258 | OutFile.Put("Internal error: undefined function begin"); |
||
3259 | return; |
||
3260 | } |
||
3261 | |||
3262 | // Get symbol old index |
||
3263 | uint32 symi = FunctionList[IFunction].OldSymbolIndex; |
||
3264 | |||
3265 | // Get symbol record |
||
3266 | uint32 SymI = Symbols.Old2NewIndex(symi); |
||
3267 | |||
3268 | OutFile.NewLine(); // Blank line |
||
3269 | |||
3270 | // Remember that symbol has been written |
||
3271 | Symbols[SymI].Scope |= 0x100; |
||
3272 | |||
3273 | // Check alignment if preceded by NOP |
||
3274 | if ((FlagPrevious & 1) && (IBegin & 0x0F) == 0 && Sections[Section].Align >= 4) { |
||
3275 | WriteAlign(16); |
||
3276 | } |
||
3277 | |||
3278 | if (Symbols[SymI].Name == 0) { |
||
3279 | // Has no name. Probably only NOP fillers |
||
3280 | return; |
||
3281 | } |
||
3282 | |||
3283 | // Write function name etc. |
||
3284 | switch (Syntax) { |
||
3285 | case SUBTYPE_MASM: |
||
3286 | WriteFunctionBeginMASM(SymI, Symbols[SymI].Scope); break; |
||
3287 | case SUBTYPE_YASM: |
||
3288 | WriteFunctionBeginYASM(SymI, Symbols[SymI].Scope); break; |
||
3289 | case SUBTYPE_GASM: |
||
3290 | WriteFunctionBeginGASM(SymI, Symbols[SymI].Scope); break; |
||
3291 | } |
||
3292 | } |
||
3293 | |||
3294 | void CDisassembler::WriteFunctionBeginMASM(uint32 symi, uint32 scope) { |
||
3295 | // Write begin of function, MASM syntax |
||
3296 | // Write name |
||
3297 | WriteSymbolName(symi); |
||
3298 | // Space |
||
3299 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); |
||
3300 | |||
3301 | if (scope & 0x1C) { |
||
3302 | // Scope is public |
||
3303 | // Write "PROC" |
||
3304 | OutFile.Put("PROC"); |
||
3305 | // Write "NEAR" unless 64 bit mode |
||
3306 | if (WordSize < 64) OutFile.Put(" NEAR"); |
||
3307 | // Check if weak |
||
3308 | if (scope & 8) { |
||
3309 | OutFile.NewLine(); |
||
3310 | OutFile.Put(CommentSeparator); |
||
3311 | OutFile.Put(" WEAK "); |
||
3312 | WriteSymbolName(symi); |
||
3313 | } |
||
3314 | // Check if communal |
||
3315 | if (scope & 0x10) { |
||
3316 | OutFile.NewLine(); |
||
3317 | OutFile.Put(CommentSeparator); |
||
3318 | OutFile.Put(" COMDEF "); |
||
3319 | WriteSymbolName(symi); |
||
3320 | } |
||
3321 | } |
||
3322 | else { |
||
3323 | // Scope is local |
||
3324 | OutFile.Put("LABEL NEAR"); |
||
3325 | } |
||
3326 | // Check if Gnu indirect |
||
3327 | if (Symbols[symi].Type & 0x40000000) { |
||
3328 | OutFile.Put(CommentSeparator); |
||
3329 | OutFile.Put("Gnu indirect function"); // Cannot be represented in Masm syntax |
||
3330 | } |
||
3331 | // End line |
||
3332 | OutFile.NewLine(); |
||
3333 | } |
||
3334 | |||
3335 | void CDisassembler::WriteFunctionBeginYASM(uint32 symi, uint32 scope) { |
||
3336 | // Write begin of function, YASM syntax |
||
3337 | // Write name |
||
3338 | WriteSymbolName(symi); |
||
3339 | // Colon |
||
3340 | OutFile.Put(":"); OutFile.Tabulate(AsmTab1); |
||
3341 | |||
3342 | if (scope & 0x1C) { |
||
3343 | // Scope is public |
||
3344 | // Write comment |
||
3345 | OutFile.Put(CommentSeparator); |
||
3346 | OutFile.Put("Function begin"); |
||
3347 | // Check if weak |
||
3348 | if (scope & 8) { |
||
3349 | OutFile.Put(", weak"); |
||
3350 | } |
||
3351 | // Check if communal |
||
3352 | if (scope & 0x10) { |
||
3353 | OutFile.Put(", communal"); |
||
3354 | } |
||
3355 | } |
||
3356 | else { |
||
3357 | // Scope is local. Write comment |
||
3358 | OutFile.Put(CommentSeparator); |
||
3359 | OutFile.Put("Local function"); |
||
3360 | } |
||
3361 | // Check if Gnu indirect |
||
3362 | if (Symbols[symi].Type & 0x40000000) { |
||
3363 | OutFile.Put(CommentSeparator); |
||
3364 | OutFile.Put("Gnu indirect function"); // Cannot be represented in NASM/YASM syntax |
||
3365 | } |
||
3366 | // End line |
||
3367 | OutFile.NewLine(); |
||
3368 | } |
||
3369 | |||
3370 | void CDisassembler::WriteFunctionBeginGASM(uint32 symi, uint32 scope) { |
||
3371 | // Write begin of function, GAS syntax |
||
3372 | WriteSymbolName(symi); // Write name |
||
3373 | OutFile.Put(":"); |
||
3374 | OutFile.Tabulate(AsmTab3); OutFile.Put(CommentSeparator); |
||
3375 | if (scope & 3) OutFile.Put("Local "); |
||
3376 | if (scope & 8) OutFile.Put("weak "); |
||
3377 | if (scope & 0x10) OutFile.Put("communal "); |
||
3378 | OutFile.Put("Function"); |
||
3379 | OutFile.NewLine(); |
||
3380 | OutFile.Tabulate(AsmTab1); |
||
3381 | OutFile.Put(".type "); |
||
3382 | OutFile.Tabulate(AsmTab2); |
||
3383 | WriteSymbolName(symi); // Write name |
||
3384 | if (Symbols[symi].Type & 0x40000000) { |
||
3385 | OutFile.Put(", @gnu_indirect_function"); |
||
3386 | } |
||
3387 | else { |
||
3388 | OutFile.Put(", @function"); |
||
3389 | } |
||
3390 | OutFile.NewLine(); |
||
3391 | } |
||
3392 | |||
3393 | |||
3394 | void CDisassembler::WriteFunctionEnd() { |
||
3395 | // Write end of function |
||
3396 | |||
3397 | // Check if IFunction is valid |
||
3398 | if (IFunction == 0 || IFunction >= FunctionList.GetNumEntries()) { |
||
3399 | // Should not occur |
||
3400 | OutFile.Put(CommentSeparator); |
||
3401 | OutFile.Put("Internal error: undefined function end"); |
||
3402 | return; |
||
3403 | } |
||
3404 | |||
3405 | // Get symbol index |
||
3406 | uint32 SymOldI = FunctionList[IFunction].OldSymbolIndex; |
||
3407 | uint32 SymNewI = Symbols.Old2NewIndex(SymOldI); |
||
3408 | |||
3409 | // check scope |
||
3410 | if (Symbols[SymNewI].Scope & 0x1C) { |
||
3411 | // Has public scope. Write end of function |
||
3412 | switch (Syntax) { |
||
3413 | case SUBTYPE_MASM: |
||
3414 | WriteFunctionEndMASM(SymNewI); break; |
||
3415 | case SUBTYPE_YASM: |
||
3416 | WriteFunctionEndYASM(SymNewI); break; |
||
3417 | case SUBTYPE_GASM: |
||
3418 | WriteFunctionEndGASM(SymNewI); break; |
||
3419 | } |
||
3420 | } |
||
3421 | } |
||
3422 | |||
3423 | void CDisassembler::WriteFunctionEndMASM(uint32 symi) { |
||
3424 | // Write end of function, MASM syntax |
||
3425 | // Write name |
||
3426 | WriteSymbolName(symi); |
||
3427 | |||
3428 | // Space |
||
3429 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); |
||
3430 | // Write "ENDP" |
||
3431 | OutFile.Put("ENDP"); |
||
3432 | OutFile.NewLine(); |
||
3433 | } |
||
3434 | |||
3435 | void CDisassembler::WriteFunctionEndYASM(uint32 symi) { |
||
3436 | // Write end of function, YASM syntax |
||
3437 | // Write comment |
||
3438 | OutFile.Put(CommentSeparator); |
||
3439 | // Write name |
||
3440 | WriteSymbolName(symi); |
||
3441 | OutFile.Put(" End of function"); |
||
3442 | OutFile.NewLine(); |
||
3443 | } |
||
3444 | |||
3445 | void CDisassembler::WriteFunctionEndGASM(uint32 symi){ |
||
3446 | // Write end of function, GAS syntax |
||
3447 | // Write .size directive |
||
3448 | OutFile.Tabulate(AsmTab1); |
||
3449 | OutFile.Put(".size "); |
||
3450 | OutFile.Tabulate(AsmTab2); |
||
3451 | WriteSymbolName(symi); // Name of function |
||
3452 | OutFile.Put(", . - "); |
||
3453 | WriteSymbolName(symi); // Name of function |
||
3454 | OutFile.Tabulate(AsmTab3); |
||
3455 | OutFile.Put(CommentSeparator); |
||
3456 | OutFile.Put("End of function is probably here"); |
||
3457 | OutFile.NewLine(); |
||
3458 | } |
||
3459 | |||
3460 | |||
3461 | void CDisassembler::WriteCodeLabel(uint32 symi) { |
||
3462 | // Write private or public code label. symi is new symbol index |
||
3463 | |||
3464 | // Get scope |
||
3465 | uint32 Scope = Symbols[symi].Scope; |
||
3466 | |||
3467 | // Check scope |
||
3468 | if (Scope & 0x100) return; // Has been written as function begin |
||
3469 | |||
3470 | if (Scope == 0) { |
||
3471 | // Inaccessible. No name. Make blank line |
||
3472 | OutFile.NewLine(); |
||
3473 | // Remember position for warning check |
||
3474 | LabelInaccessible = IBegin; |
||
3475 | return; |
||
3476 | } |
||
3477 | |||
3478 | // Begin on new line if preceded by another symbol |
||
3479 | if (OutFile.GetColumn()) OutFile.NewLine(); |
||
3480 | |||
3481 | // Check alignment if preceded by NOP |
||
3482 | if ((Scope & 0xFF) > 1 && (FlagPrevious & 1) && (IBegin & 0x0F) == 0 && Sections[Section].Align >= 4) { |
||
3483 | WriteAlign(16); |
||
3484 | } |
||
3485 | |||
3486 | switch (Syntax) { |
||
3487 | case SUBTYPE_MASM: |
||
3488 | WriteCodeLabelMASM(symi, Symbols[symi].Scope); break; |
||
3489 | case SUBTYPE_YASM: |
||
3490 | WriteCodeLabelYASM(symi, Symbols[symi].Scope); break; |
||
3491 | case SUBTYPE_GASM: |
||
3492 | WriteCodeLabelGASM(symi, Symbols[symi].Scope); break; |
||
3493 | } |
||
3494 | |||
3495 | // Remember this has been written |
||
3496 | Symbols[symi].Scope |= 0x100; |
||
3497 | } |
||
3498 | |||
3499 | |||
3500 | void CDisassembler::WriteCodeLabelMASM(uint32 symi, uint32 scope) { |
||
3501 | // Write private or public code label, MASM syntax |
||
3502 | if ((scope & 0xFF) > 1) { |
||
3503 | // Scope > function local. Write as label near |
||
3504 | // Check if extra linefeed needed |
||
3505 | // if (!(IFunction && FunctionList[IFunction].Start == IBegin)) |
||
3506 | // New line |
||
3507 | OutFile.NewLine(); |
||
3508 | |||
3509 | // Write name |
||
3510 | WriteSymbolName(symi); |
||
3511 | // Space |
||
3512 | OutFile.Put(" "); OutFile.Tabulate(AsmTab1); |
||
3513 | // Write "LABEL" |
||
3514 | OutFile.Put("LABEL"); |
||
3515 | // Write "NEAR" even 64 bit mode |
||
3516 | OutFile.Put(" NEAR"); |
||
3517 | // New line |
||
3518 | OutFile.NewLine(); |
||
3519 | |||
3520 | // Check if weak |
||
3521 | if (scope & 8) { |
||
3522 | OutFile.Put(CommentSeparator); |
||
3523 | OutFile.Put(" WEAK "); |
||
3524 | WriteSymbolName(symi); |
||
3525 | OutFile.NewLine(); |
||
3526 | } |
||
3527 | // Check if communal |
||
3528 | if (scope & 0x10) { |
||
3529 | OutFile.Put(CommentSeparator); |
||
3530 | OutFile.Put(" COMDEF "); |
||
3531 | WriteSymbolName(symi); |
||
3532 | OutFile.NewLine(); |
||
3533 | } |
||
3534 | } |
||
3535 | else { |
||
3536 | // Symbol is local to current function. Write name with colon |
||
3537 | if (FlagPrevious & 2) { |
||
3538 | // Insert blank line if previous instruction was unconditional jump or return |
||
3539 | OutFile.NewLine(); |
||
3540 | } |
||
3541 | // Write name |
||
3542 | WriteSymbolName(symi); |
||
3543 | // Write ":" |
||
3544 | OutFile.Put(":"); |
||
3545 | if (OutFile.GetColumn() > AsmTab1) { |
||
3546 | // Past tabstop. Go to next line |
||
3547 | OutFile.NewLine(); // New line |
||
3548 | } |
||
3549 | } |
||
3550 | } |
||
3551 | |||
3552 | void CDisassembler::WriteCodeLabelYASM(uint32 symi, uint32 scope) { |
||
3553 | // Write private or public code label, YASM syntax |
||
3554 | if ((scope & 0xFF) > 2) { |
||
3555 | // Scope is public |
||
3556 | OutFile.NewLine(); |
||
3557 | // Write name |
||
3558 | WriteSymbolName(symi); |
||
3559 | OutFile.Put(":"); |
||
3560 | |||
3561 | // Check if weak |
||
3562 | if (scope & 8) { |
||
3563 | OutFile.Put(CommentSeparator); |
||
3564 | OutFile.Put(" weak "); |
||
3565 | WriteSymbolName(symi); |
||
3566 | } |
||
3567 | // Check if communal |
||
3568 | if (scope & 0x10) { |
||
3569 | OutFile.Put(CommentSeparator); |
||
3570 | OutFile.Put(" communal "); |
||
3571 | WriteSymbolName(symi); |
||
3572 | } |
||
3573 | OutFile.NewLine(); |
||
3574 | } |
||
3575 | else { |
||
3576 | // Symbol is local to current function. Write name with colon |
||
3577 | if (FlagPrevious & 2) { |
||
3578 | // Insert blank line if previous instruction was unconditional jump or return |
||
3579 | OutFile.NewLine(); |
||
3580 | } |
||
3581 | // Write name |
||
3582 | WriteSymbolName(symi); |
||
3583 | // Write ":" |
||
3584 | OutFile.Put(":"); |
||
3585 | if (OutFile.GetColumn() > AsmTab1) { |
||
3586 | // Past tabstop. Go to next line |
||
3587 | OutFile.NewLine(); // New line |
||
3588 | } |
||
3589 | } |
||
3590 | } |
||
3591 | |||
3592 | void CDisassembler::WriteCodeLabelGASM(uint32 symi, uint32 scope) { |
||
3593 | // Write private or public code label, GAS syntax same as YASM syntax |
||
3594 | WriteCodeLabelYASM(symi, scope); |
||
3595 | } |
||
3596 | |||
3597 | void CDisassembler::WriteAssume() { |
||
3598 | // Write assume directive for segment register if MASM syntax |
||
3599 | if (Syntax != SUBTYPE_MASM) return; |
||
3600 | if (!s.AddressField) return; |
||
3601 | |||
3602 | int32 SegReg, PrefixSeg; // Segment register used |
||
3603 | uint32 symo; // Target symbol old index |
||
3604 | uint32 symi; // Target symbol new index |
||
3605 | int32 TargetSegment; // Target segment/section |
||
3606 | int32 TargetGroup; // Group containing target segment |
||
3607 | |||
3608 | // Find which segment register is used for addressing memory operand |
||
3609 | SegReg = 3; // DS is default |
||
3610 | if (s.BaseReg == 4+1 || s.BaseReg == 5+1) { |
||
3611 | // Base register is (E)BP or ESP |
||
3612 | SegReg = 2; // SS register used unless there is a prefix |
||
3613 | } |
||
3614 | if (s.Prefixes[0]) { |
||
3615 | // There is a segment prefix |
||
3616 | PrefixSeg = GetSegmentRegisterFromPrefix(); |
||
3617 | if (PrefixSeg >= 0 && PrefixSeg <= 5) { |
||
3618 | // Segment prefix is valid. Segment determined by segment prefix |
||
3619 | SegReg = PrefixSeg; |
||
3620 | } |
||
3621 | } |
||
3622 | // Default target segment is none |
||
3623 | TargetSegment = TargetGroup = 0; |
||
3624 | |||
3625 | // Find symbol referenced by next instruction |
||
3626 | if (s.AddressRelocation && s.AddressRelocation < Relocations.GetNumEntries()) { |
||
3627 | symo = Relocations[s.AddressRelocation].TargetOldIndex; // Target symbol old index |
||
3628 | if (symo) { |
||
3629 | symi = Symbols.Old2NewIndex(symo); // Target symbol new index |
||
3630 | if (symi) { |
||
3631 | TargetSegment = Symbols[symi].Section; // Target segment |
||
3632 | if (TargetSegment < 0 || TargetSegment >= (int32)Sections.GetNumEntries()) { |
||
3633 | TargetSegment = 0; |
||
3634 | } |
||
3635 | else { |
||
3636 | TargetGroup = Sections[TargetSegment].Group; // Group containing target segment |
||
3637 | if (TargetGroup <= ASM_SEGMENT_ERROR || TargetGroup >= (int32)Sections.GetNumEntries()) { |
||
3638 | TargetGroup = 0; |
||
3639 | } |
||
3640 | } |
||
3641 | } |
||
3642 | } |
||
3643 | } |
||
3644 | if (TargetSegment) { |
||
3645 | // Target has a segment. Check if it is different from currently assumed segment |
||
3646 | if (TargetSegment != Assumes[SegReg] && TargetGroup != Assumes[SegReg]) { |
||
3647 | // Assume directive needed |
||
3648 | // If segment belongs to a group then the group takes precedence |
||
3649 | if (TargetGroup) TargetSegment = TargetGroup; |
||
3650 | // Write assume directive |
||
3651 | OutFile.Put("ASSUME "); |
||
3652 | OutFile.Tabulate(AsmTab1); |
||
3653 | OutFile.Put(RegisterNamesSeg[SegReg]); // Name of segment register used |
||
3654 | OutFile.Put(":"); |
||
3655 | WriteSectionName(TargetSegment); // Name of segment or group referenced |
||
3656 | OutFile.NewLine(); |
||
3657 | Assumes[SegReg] = TargetSegment; |
||
3658 | } |
||
3659 | } |
||
3660 | else { |
||
3661 | // Target segment not specified. Assumed value may be anyting but 'error' |
||
3662 | if (Assumes[SegReg] <= ASM_SEGMENT_ERROR) { |
||
3663 | // Segment register is assumed to 'error'. Change assume to 'nothing' |
||
3664 | OutFile.Put("ASSUME "); |
||
3665 | OutFile.Tabulate(AsmTab1); |
||
3666 | OutFile.Put(RegisterNamesSeg[SegReg]); // Name of segment register used |
||
3667 | OutFile.Put(":NOTHING"); |
||
3668 | OutFile.NewLine(); |
||
3669 | Assumes[SegReg] = ASM_SEGMENT_NOTHING; |
||
3670 | } |
||
3671 | } |
||
3672 | } |
||
3673 | |||
3674 | |||
3675 | void CDisassembler::WriteInstruction() { |
||
3676 | // Write instruction and operands |
||
3677 | uint32 NumOperands = 0; // Number of operands written |
||
3678 | uint32 i; // Loop index |
||
3679 | const char * OpName; // Opcode name |
||
3680 | |||
3681 | if (s.AddressFieldSize && Syntax == SUBTYPE_MASM) { |
||
3682 | // There is a memory operand. Check if ASSUME directive needed |
||
3683 | WriteAssume(); |
||
3684 | } |
||
3685 | |||
3686 | if (CodeMode & 6) { |
||
3687 | // Code is dubious. Show as comment only |
||
3688 | OutFile.Put(CommentSeparator); // Start comment |
||
3689 | } |
||
3690 | else if ((s.OpcodeDef->Options & 0x20) && s.OpcodeStart1 > IBegin) { |
||
3691 | // Write prefixes explicitly. |
||
3692 | // This is used for rare cases where the assembler cannot generate the prefix |
||
3693 | OutFile.Tabulate(AsmTab1); // Tabulate |
||
3694 | OutFile.Put(Syntax == SUBTYPE_GASM ? ".byte " : "DB "); |
||
3695 | OutFile.Tabulate(AsmTab2); // Tabulate |
||
3696 | for (i = IBegin; i < s.OpcodeStart1; i++) { |
||
3697 | if (i > IBegin) OutFile.Put(", "); |
||
3698 | OutFile.PutHex(Get |
||
3699 | } |
||
3700 | OutFile.Tabulate(AsmTab3); // Tabulate |
||
3701 | OutFile.Put(CommentSeparator); |
||
3702 | if ((s.OpcodeDef->AllowedPrefixes & 8) && Get |
||
3703 | OutFile.Put("BND prefix coded explicitly"); // Comment |
||
3704 | } |
||
3705 | else { |
||
3706 | OutFile.Put("Prefix coded explicitly"); // Comment |
||
3707 | } |
||
3708 | OutFile.NewLine(); |
||
3709 | } |
||
3710 | |||
3711 | if ((s.Operands[0] & 0xF0) == 0xC0 || (s.Operands[1] & 0xF0) == 0xC0) { |
||
3712 | // String instruction or xlat instruction |
||
3713 | WriteStringInstruction(); |
||
3714 | return; |
||
3715 | } |
||
3716 | |||
3717 | OutFile.Tabulate(AsmTab1); // Tabulate |
||
3718 | |||
3719 | if ((s.OpcodeDef->AllowedPrefixes & 0xC40) == 0xC40) { |
||
3720 | switch (s.Prefixes[5]) { |
||
3721 | case 0xF2: |
||
3722 | OutFile.Put("xacquire "); break; // xacquire prefix |
||
3723 | case 0xF3: |
||
3724 | OutFile.Put("xrelease "); break; // xrelease prefix |
||
3725 | } |
||
3726 | } |
||
3727 | if (s.Prefixes[2]) { |
||
3728 | OutFile.Put("lock "); // Lock prefix |
||
3729 | } |
||
3730 | |||
3731 | // Get opcode name |
||
3732 | if (s.OpcodeDef->Name) { |
||
3733 | // Opcode name |
||
3734 | OpName = s.OpcodeDef->Name; |
||
3735 | // Search for opcode comment |
||
3736 | s.OpComment = strchr(OpName, ';'); |
||
3737 | if (s.OpComment) s.OpComment++; // Point to after ';' |
||
3738 | } |
||
3739 | else { |
||
3740 | OpName = "UNDEFINED"; // Undefined code with no name |
||
3741 | s.OpComment = 0; |
||
3742 | } |
||
3743 | |||
3744 | // Check prefix option |
||
3745 | if ((s.OpcodeDef->Options & 2) && (s.Prefixes[7] & 0x30)) { |
||
3746 | // Put prefix 'v' for VEX-prefixed instruction |
||
3747 | OutFile.Put('v'); |
||
3748 | } |
||
3749 | |||
3750 | // Write opcode name |
||
3751 | if (s.OpComment) { |
||
3752 | // OpName string contains opcode name and comment, separated by ';' |
||
3753 | while (*OpName != ';' && *OpName != 0) { // Write opcode name until comment |
||
3754 | OutFile.Put(*(OpName++)); |
||
3755 | } |
||
3756 | } |
||
3757 | else { |
||
3758 | OutFile.Put(OpName); // Write normal opcode name |
||
3759 | } |
||
3760 | |||
3761 | // Check suffix option |
||
3762 | if (s.OpcodeDef->Options & 1) { |
||
3763 | // Append suffix for operand size or type to name |
||
3764 | if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x1000) { |
||
3765 | // F.P. operand size defined by W prefix bit |
||
3766 | i = s.Prefixes[7] & 8; // W prefix bit |
||
3767 | OutFile.Put(i ? 'd' : 's'); |
||
3768 | } |
||
3769 | else if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x3000) { |
||
3770 | // Integer or f.p. operand size defined by W prefix bit |
||
3771 | bool f = false; |
||
3772 | // Find out if operands are integer or f.p. |
||
3773 | for (i = 0; i < s.MaxNumOperands; i++) { |
||
3774 | if ((s.Operands[i] & 0xF0) == 0x40) { |
||
3775 | f = true; break; |
||
3776 | } |
||
3777 | } |
||
3778 | i = s.Prefixes[7] & 8; // W prefix bit |
||
3779 | if (f) { |
||
3780 | OutFile.Put(i ? 'd' : 's'); // float precision suffix |
||
3781 | } |
||
3782 | else { |
||
3783 | OutFile.Put(i ? 'q' : 'd'); // integer size suffix |
||
3784 | } |
||
3785 | } |
||
3786 | else if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x4000) { |
||
3787 | // Integer operand size defined by W prefix bit |
||
3788 | i = s.Prefixes[7] & 8; // W prefix bit |
||
3789 | OutFile.Put(i ? 'w' : 'b'); |
||
3790 | } |
||
3791 | else if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x5000) { |
||
3792 | // mask register operand size defined by W prefix bit and 66 prefix |
||
3793 | i = (s.Prefixes[7] & 8) >> 2; // W prefix bit |
||
3794 | i |= s.Prefixes[5] != 0x66; // 66 prefix bit |
||
3795 | OutFile.Put("bwdq"[i]); |
||
3796 | } |
||
3797 | else if (s.OpcodeDef->AllowedPrefixes & 0xE00) { |
||
3798 | // F.P. operand type and size defined by prefixes |
||
3799 | switch (s.Prefixes[5]) { |
||
3800 | case 0: // No prefix = ps |
||
3801 | OutFile.Put("ps"); break; |
||
3802 | case 0x66: // 66 prefix = pd |
||
3803 | OutFile.Put("pd"); break; |
||
3804 | case 0xF3: // F3 prefix = ss |
||
3805 | OutFile.Put("ss"); break; |
||
3806 | case 0xF2: // F2 prefix = sd |
||
3807 | OutFile.Put("sd"); break; |
||
3808 | default: |
||
3809 | err.submit(9000); // Should not occur |
||
3810 | } |
||
3811 | } |
||
3812 | else if (s.OpcodeDef->AllowedPrefixes & 0x100){ |
||
3813 | // Integer operand size defined by prefixes |
||
3814 | // Suffix for operand size |
||
3815 | i = s.OperandSize / 8; |
||
3816 | if (i <= 8) { |
||
3817 | static const char SizeSuffixes[] = " bw d f q"; // Table of suffixes |
||
3818 | OutFile.Put(SizeSuffixes[i]); |
||
3819 | } |
||
3820 | } |
||
3821 | } |
||
3822 | // Alternative suffix option |
||
3823 | if (s.OpcodeDef->Options & 0x1000) { |
||
3824 | // Append alternative suffix for vector element size to name |
||
3825 | if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x3000) { |
||
3826 | // Integer operand size defined by W prefix bit |
||
3827 | i = ((s.Prefixes[7] & 8) + 8) * 4; // W prefix bit -> 8 / 16 |
||
3828 | OutFile.PutDecimal(i); |
||
3829 | } |
||
3830 | if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x4000) { // 32 / 64 |
||
3831 | i = (s.Prefixes[7] & 8) + 8; // W prefix bit -> 8 / 16 |
||
3832 | OutFile.PutDecimal(i); |
||
3833 | } |
||
3834 | } |
||
3835 | // More suffix option |
||
3836 | if ((s.OpcodeDef->Options & 0x400) && s.ImmediateFieldSize == 8) { |
||
3837 | // 64 bit immediate mov |
||
3838 | if (Syntax == SUBTYPE_GASM) OutFile.Put("abs"); |
||
3839 | } |
||
3840 | |||
3841 | // Space between opcode name and operands |
||
3842 | OutFile.Put(" "); OutFile.Tabulate(AsmTab2); // Tabulate. At least one space |
||
3843 | |||
3844 | // Loop for all operands to write |
||
3845 | for (i = 0; i < s.MaxNumOperands; i++) { |
||
3846 | if (s.Operands[i] & 0xFFFF) { |
||
3847 | |||
3848 | // Write operand i |
||
3849 | if (NumOperands++) { |
||
3850 | // At least one operand before this one. Separate by ", " |
||
3851 | OutFile.Put(", "); |
||
3852 | } |
||
3853 | |||
3854 | // Write constant and jump operands |
||
3855 | switch (s.Operands[i] & 0xF0) { |
||
3856 | case 0x10: case 0x20: case 0x30: case 0x80: |
||
3857 | WriteImmediateOperand(s.Operands[i]); |
||
3858 | continue; |
||
3859 | } |
||
3860 | |||
3861 | // Write register and memory operands |
||
3862 | uint32 optype = (s.Operands[i] >> 16) & 0x0F; |
||
3863 | switch (optype) { |
||
3864 | case 0: // Other type of operand |
||
3865 | WriteOtherOperand(s.Operands[i]); break; |
||
3866 | |||
3867 | case 0x1: // Direct memory operand |
||
3868 | WriteRMOperand(s.Operands[i]); break; |
||
3869 | |||
3870 | case 0x2: // Register operand indicated by last bits of opcode |
||
3871 | WriteShortRegOperand(s.Operands[i]); break; |
||
3872 | |||
3873 | case 0x3: // Register or memory operand indicated by mod/rm bits |
||
3874 | WriteRMOperand(s.Operands[i]); break; |
||
3875 | |||
3876 | case 0x4: // Register operand indicated by reg bits |
||
3877 | WriteRegOperand(s.Operands[i]); break; |
||
3878 | |||
3879 | case 0x5: // Register operand indicated by dest bits of DREX byte |
||
3880 | WriteDREXOperand(s.Operands[i]); break; |
||
3881 | |||
3882 | case 0x6: // Register operand indicated by VEX.vvvv bits |
||
3883 | WriteVEXOperand(s.Operands[i], 0); break; |
||
3884 | |||
3885 | case 0x7: // Register operand indicated by bits 4-7 of immediate operand |
||
3886 | WriteVEXOperand(s.Operands[i], 1); break; |
||
3887 | |||
3888 | case 0x8: // Register operand indicated by bits 0-3 of immediate operand |
||
3889 | WriteVEXOperand(s.Operands[i], 2); break; // Unused. For future use |
||
3890 | } |
||
3891 | int isMem = optype == 3 && s.Mod != 3; |
||
3892 | if (s.Prefixes[3] == 0x62) { // EVEX and MVEX prefix can have extra operand attributes |
||
3893 | if (s.Prefixes[6] & 0x20) { |
||
3894 | WriteOperandAttributeEVEX(i, isMem); |
||
3895 | } |
||
3896 | else { |
||
3897 | WriteOperandAttributeMVEX(i, isMem); |
||
3898 | } |
||
3899 | } |
||
3900 | if (s.Prefixes[3] == 0x62 && (i == s.MaxNumOperands - 1 || (s.Operands[i+1] & 0xFFF) < 0x40)) { |
||
3901 | // This is the last SIMD operand |
||
3902 | if (!(s.Operands[4] & 0x80000000)) { |
||
3903 | s.Operands[4] |= 0x80000000; // Make sure we don't write this twice |
||
3904 | if (s.Prefixes[6] & 0x20) { |
||
3905 | WriteOperandAttributeEVEX(98, isMem); |
||
3906 | } |
||
3907 | else { |
||
3908 | WriteOperandAttributeMVEX(98, isMem); |
||
3909 | } |
||
3910 | } |
||
3911 | } |
||
3912 | } |
||
3913 | } |
||
3914 | if (s.Prefixes[3] == 0x62) { // EVEX and MVEX prefix can have extra attributes after operands |
||
3915 | if (s.Prefixes[6] & 0x20) { |
||
3916 | WriteOperandAttributeEVEX(99, 0); |
||
3917 | } |
||
3918 | else { |
||
3919 | WriteOperandAttributeMVEX(99, 0); |
||
3920 | } |
||
3921 | } |
||
3922 | if (s.OpComment) { |
||
3923 | // Write opcode comment |
||
3924 | OutFile.Put(' '); |
||
3925 | OutFile.Put(CommentSeparator); |
||
3926 | OutFile.Put(s.OpComment); |
||
3927 | } |
||
3928 | } |
||
3929 | |||
3930 | |||
3931 | void CDisassembler::WriteStringInstruction() { |
||
3932 | // Write string instruction or xlat instruction |
||
3933 | uint32 NumOperands = 0; // Number of operands written |
||
3934 | uint32 i; // Loop index |
||
3935 | uint32 Segment; // Possible segment prefix |
||
3936 | |||
3937 | if (!(s.OpcodeDef->AllowedPrefixes & 0x1100)) { |
||
3938 | // Operand size is 8 if operand size prefixes not allowed |
||
3939 | s.OperandSize = 8; |
||
3940 | } |
||
3941 | |||
3942 | OutFile.Tabulate(AsmTab1); // Tabulate |
||
3943 | |||
3944 | if (Syntax != SUBTYPE_MASM && s.Prefixes[0] && (s.OpcodeDef->AllowedPrefixes & 4)) { |
||
3945 | // Get segment prefix |
||
3946 | Segment = GetSegmentRegisterFromPrefix(); // Interpret segment prefix |
||
3947 | // Write segment override |
||
3948 | OutFile.Put(RegisterNamesSeg[Segment]); |
||
3949 | OutFile.Put(" "); |
||
3950 | } |
||
3951 | |||
3952 | // Check repeat prefix |
||
3953 | if (s.OpcodeDef->AllowedPrefixes & 0x20) { |
||
3954 | if (s.Prefixes[3]) { |
||
3955 | // Repeat prefix |
||
3956 | OutFile.Put("rep "); |
||
3957 | } |
||
3958 | } |
||
3959 | else if (s.OpcodeDef->AllowedPrefixes & 0x40) { |
||
3960 | if (s.Prefixes[3] == 0xF2) { |
||
3961 | // repne prefix |
||
3962 | OutFile.Put("repne "); |
||
3963 | } |
||
3964 | else if (s.Prefixes[3] == 0xF3) { |
||
3965 | // repe prefix |
||
3966 | OutFile.Put("repe "); |
||
3967 | } |
||
3968 | } |
||
3969 | |||
3970 | // Write opcode name |
||
3971 | OutFile.Put(s.OpcodeDef->Name); // Opcode name |
||
3972 | |||
3973 | if (Syntax == SUBTYPE_MASM |
||
3974 | && (((s.OpcodeDef->AllowedPrefixes & 4) && s.Prefixes[0]) |
||
3975 | || ((s.OpcodeDef->AllowedPrefixes & 1) && s.Prefixes[1]))) { |
||
3976 | // Has segment or address size prefix. Must write operands explicitly |
||
3977 | OutFile.Put(" "); // Space before operands |
||
3978 | |||
3979 | // Check address size for pointer registers |
||
3980 | const char * * PointerRegisterNames; |
||
3981 | switch (s.AddressSize) { |
||
3982 | case 16: |
||
3983 | PointerRegisterNames = RegisterNames16; break; |
||
3984 | case 32: |
||
3985 | PointerRegisterNames = RegisterNames32; break; |
||
3986 | case 64: |
||
3987 | PointerRegisterNames = RegisterNames64; break; |
||
3988 | default: |
||
3989 | PointerRegisterNames = 0; // should not occur |
||
3990 | } |
||
3991 | |||
3992 | // Loop for possibly two operands |
||
3993 | for (i = 0; i < 2; i++) { |
||
3994 | if (s.Operands[i]) { |
||
3995 | // Operand i defined |
||
3996 | if (NumOperands++) { |
||
3997 | // An operand before this one. Separate by ", " |
||
3998 | OutFile.Put(", "); |
||
3999 | } |
||
4000 | if (NumOperands == 1) { |
||
4001 | // Write operand size for first operand |
||
4002 | switch (s.OperandSize) { |
||
4003 | case 8: |
||
4004 | OutFile.Put("byte "); break; |
||
4005 | case 16: |
||
4006 | OutFile.Put("word "); break; |
||
4007 | case 32: |
||
4008 | OutFile.Put("dword "); break; |
||
4009 | case 64: |
||
4010 | OutFile.Put("qword "); break; |
||
4011 | } |
||
4012 | } |
||
4013 | // Get segment |
||
4014 | Segment = 1; // Default segment is DS |
||
4015 | if (s.Prefixes[0]) { |
||
4016 | Segment = GetSegmentRegisterFromPrefix(); // Interpret segment prefix |
||
4017 | } |
||
4018 | if ((s.Operands[i] & 0xCF) == 0xC2) { |
||
4019 | Segment = 0; // Segment is ES regardless of prefix for [edi] operand |
||
4020 | } |
||
4021 | // Write segment override |
||
4022 | OutFile.Put(RegisterNamesSeg[Segment]); |
||
4023 | OutFile.Put(":"); |
||
4024 | // Opening "[" |
||
4025 | OutFile.Put("["); |
||
4026 | |||
4027 | // Write pointer register |
||
4028 | switch (s.Operands[i] & 0xCF) { |
||
4029 | case 0xC0: // [bx], [ebx] or [rbx] |
||
4030 | OutFile.Put(PointerRegisterNames[3]); |
||
4031 | break; |
||
4032 | case 0xC1: // [si], [esi] or [rsi] |
||
4033 | OutFile.Put(PointerRegisterNames[6]); |
||
4034 | break; |
||
4035 | case 0xC2: // [di], [edi] or [rdi] |
||
4036 | OutFile.Put(PointerRegisterNames[7]); |
||
4037 | break; |
||
4038 | } |
||
4039 | // Closing "]" |
||
4040 | OutFile.Put("]"); |
||
4041 | } |
||
4042 | } |
||
4043 | } |
||
4044 | else { |
||
4045 | // We don't have to write the operands |
||
4046 | // Append suffix for operand size, except for xlat |
||
4047 | if ((s.Operands[1] & 0xCF) != 0xC0) { |
||
4048 | |||
4049 | // Suffix for operand size |
||
4050 | uint32 i = s.OperandSize / 8; |
||
4051 | if (i <= 8) { |
||
4052 | static const char SizeSuffixes[] = " bw d q"; // Table of suffixes |
||
4053 | OutFile.Put(SizeSuffixes[i]); |
||
4054 | } |
||
4055 | } |
||
4056 | } |
||
4057 | } |
||
4058 | |||
4059 | |||
4060 | void CDisassembler::WriteCodeComment() { |
||
4061 | // Write hex listing of instruction as comment after instruction |
||
4062 | uint32 i; // Index to current byte |
||
4063 | uint32 FieldSize; // Number of bytes in field |
||
4064 | const char * Spacer; // Space between fields |
||
4065 | |||
4066 | OutFile.Tabulate(AsmTab3); // Tabulate to comment field |
||
4067 | OutFile.Put(CommentSeparator); // Start comment |
||
4068 | |||
4069 | // Write address |
||
4070 | if (SectionEnd + SectionAddress + (uint32)ImageBase > 0xFFFF) { |
||
4071 | // Write 32 bit address |
||
4072 | OutFile.PutHex(IBegin + SectionAddress + (uint32)ImageBase); |
||
4073 | } |
||
4074 | else { |
||
4075 | // Write 16 bit address |
||
4076 | OutFile.PutHex((uint16)(IBegin + SectionAddress)); |
||
4077 | } |
||
4078 | |||
4079 | // Space after address |
||
4080 | OutFile.Put(" _"); |
||
4081 | |||
4082 | // Start of instruction |
||
4083 | i = IBegin; |
||
4084 | |||
4085 | // Write bytes |
||
4086 | while (i < IEnd) { |
||
4087 | FieldSize = 1; // Size of field to write |
||
4088 | Spacer = " "; // Space between fields |
||
4089 | |||
4090 | // Spacer and FieldSize depends on fields |
||
4091 | if (i == s.OpcodeStart1 && i > IBegin) { |
||
4092 | Spacer = ": "; // Space between prefixes and opcode |
||
4093 | } |
||
4094 | if (i == s.OpcodeStart2 + 1) { |
||
4095 | Spacer = ". "; // Space between opcode and mod/reg/rm bytes |
||
4096 | } |
||
4097 | if (i == s.AddressField && s.AddressFieldSize) { |
||
4098 | Spacer = ", "; // Space before address field |
||
4099 | FieldSize = s.AddressFieldSize; |
||
4100 | } |
||
4101 | if (i == s.ImmediateField && s.ImmediateFieldSize) { |
||
4102 | Spacer = ", "; // Space before immediate operand field |
||
4103 | FieldSize = s.ImmediateFieldSize; |
||
4104 | } |
||
4105 | // Write space |
||
4106 | OutFile.Put(Spacer); |
||
4107 | |||
4108 | // Write byte or bytes |
||
4109 | switch (FieldSize) { |
||
4110 | case 1: // Write single byte |
||
4111 | OutFile.PutHex(Get |
||
4112 | break; |
||
4113 | case 2: // Write two bytes |
||
4114 | OutFile.PutHex(Get |
||
4115 | break; |
||
4116 | case 3: // Write three bytes (operands for "enter" instruction) |
||
4117 | OutFile.PutHex(Get |
||
4118 | OutFile.Put(", "); |
||
4119 | OutFile.PutHex(Get |
||
4120 | break; |
||
4121 | case 4: // Write four bytes |
||
4122 | if ((s.Operands[0] & 0xFE) == 0x84) { |
||
4123 | // Far jump/call address |
||
4124 | OutFile.PutHex(Get |
||
4125 | OutFile.Put(" "); |
||
4126 | OutFile.PutHex(Get |
||
4127 | } |
||
4128 | else { |
||
4129 | // Any other 32 bit operand |
||
4130 | OutFile.PutHex(Get |
||
4131 | } |
||
4132 | break; |
||
4133 | case 6: // Write six bytes (far jump address) |
||
4134 | OutFile.PutHex(Get |
||
4135 | OutFile.Put(" "); |
||
4136 | OutFile.PutHex(Get |
||
4137 | break; |
||
4138 | case 8: // Write eight bytes |
||
4139 | OutFile.PutHex(Get |
||
4140 | break; |
||
4141 | } |
||
4142 | // Search for relocation |
||
4143 | SARelocation rel1; // Make relocation records for searching |
||
4144 | rel1.Section = Section; |
||
4145 | rel1.Offset = i; // rel1 marks current field in instruction |
||
4146 | |||
4147 | // Is there a relocation source exactly here? |
||
4148 | int32 irel = Relocations.Exists(rel1); // Finds relocation with source = i |
||
4149 | |||
4150 | if (irel > 0) { |
||
4151 | // This field has a relocation. Indicate relocation type |
||
4152 | // 0 = unknown, 1 = direct, 2 = self-relative, 3 = image-relative, |
||
4153 | // 4 = segment relative, 5 = relative to arbitrary ref. point, 8 = segment address/descriptor |
||
4154 | uint32 RelType = Relocations[irel].Type; |
||
4155 | if (RelType) { |
||
4156 | OutFile.Put(Lookup(RelocationTypeNames, RelType)); |
||
4157 | } |
||
4158 | if (Relocations[irel].Size > FieldSize) { |
||
4159 | // Relocation has wrong size |
||
4160 | OutFile.Put(" Misplaced relocation."); |
||
4161 | } |
||
4162 | } |
||
4163 | |||
4164 | // Point to next byte |
||
4165 | i += FieldSize; |
||
4166 | } |
||
4167 | // New line |
||
4168 | OutFile.NewLine(); |
||
4169 | } |
||
4170 | |||
4171 | |||
4172 | void CDisassembler::CountInstructions() { |
||
4173 | // Count total number of instructions defined in opcodes.cpp |
||
4174 | // Two instructions are regarded as the same and counted as one if they |
||
4175 | // have the same name and differ only in the bits that define register |
||
4176 | // name, operand size, etc. |
||
4177 | |||
4178 | uint32 map; // Map number |
||
4179 | uint32 index; // Index into map |
||
4180 | uint32 n; // Number of instructions with same code |
||
4181 | uint32 iset; // Instruction set |
||
4182 | uint32 instructions = 0; // Total number of instructions |
||
4183 | uint32 mmxinstr = 0; // Number of MMX instructions |
||
4184 | uint32 sseinstr = 0; // Number of SSE instructions |
||
4185 | uint32 sse2instr = 0; // Number of SSE2 instructions |
||
4186 | uint32 sse3instr = 0; // Number of SSE3 instructions |
||
4187 | uint32 ssse3instr = 0; // Number of SSSE3 instructions |
||
4188 | uint32 sse41instr = 0; // Number of SSE4.1 instructions |
||
4189 | uint32 sse42instr = 0; // Number of SSE4.2 instructions |
||
4190 | uint32 AVXinstr = 0; // Number of AVX instructions |
||
4191 | uint32 FMAinstr = 0; // Number of FMA3 and later instructions |
||
4192 | uint32 AVX2instr = 0; // Number of AVX2 instructions |
||
4193 | uint32 BMIinstr = 0; // Number of BMI instructions and other small instruction sets |
||
4194 | uint32 AVX512instr = 0; // Number of AVX-512 instructions |
||
4195 | uint32 MICinstr = 0; // Number of MIC instructions |
||
4196 | uint32 AMDinstr = 0; // Number of AMD instructions |
||
4197 | uint32 VIAinstr = 0; // Number of AMD instructions |
||
4198 | uint32 privilinstr = 0; // Number of privileged instructions |
||
4199 | uint32 undocinstr = 0; // Number of undocumented instructions |
||
4200 | uint32 droppedinstr = 0; // Number of opcodes planned but never implemented |
||
4201 | uint32 VEXdouble = 0; // Number of instructions that have both VEX and non-VEX version |
||
4202 | SOpcodeDef const * opcode; // Pointer to map entry |
||
4203 | |||
4204 | // Loop through all maps |
||
4205 | for (map = 0; map < NumOpcodeTables1; map++) { |
||
4206 | // Loop through each map |
||
4207 | for (index = 0; index < OpcodeTableLength[map]; index++) { |
||
4208 | opcode = OpcodeTables[map] + index; |
||
4209 | if (opcode->InstructionFormat && opcode->Name |
||
4210 | && !opcode->TableLink && !(opcode->InstructionFormat & 0x8000)) { |
||
4211 | // instruction is defined |
||
4212 | if ((opcode->InstructionFormat & 0xFFF) == 3 |
||
4213 | && index > 0 && (opcode-1)->Name |
||
4214 | && strcmp(opcode->Name, (opcode-1)->Name) == 0) { |
||
4215 | // Same as previous instruction, just with another register |
||
4216 | continue; // Don't count this |
||
4217 | } |
||
4218 | n = 1; // Default = one instruction per map entry |
||
4219 | // Check if we have multiple instructions with different prefixes |
||
4220 | if (opcode->Options & 1) { |
||
4221 | if (opcode->AllowedPrefixes & 0x3000) { |
||
4222 | n++; // Extra instruction with W prefix bit |
||
4223 | } |
||
4224 | else if (opcode->AllowedPrefixes & 0xE00) { |
||
4225 | if (opcode->AllowedPrefixes & 0x200) n++; // Extra instruction with 66 prefix |
||
4226 | if (opcode->AllowedPrefixes & 0x400) n++; // Extra instruction with F3 prefix |
||
4227 | if (opcode->AllowedPrefixes & 0x800) n++; // Extra instruction with F2 prefix |
||
4228 | } |
||
4229 | else if (opcode->AllowedPrefixes & 0x100) { |
||
4230 | n++; // Extra instruction with 66 prefix |
||
4231 | if (opcode->AllowedPrefixes & 0x1000) n++;// Extra instruction with L prefix bit |
||
4232 | } |
||
4233 | } |
||
4234 | if (opcode->Options & 2) VEXdouble += n; // Instructions that have both VEX and non-VEX version |
||
4235 | instructions += n; // Count total instructions |
||
4236 | |||
4237 | iset = opcode->InstructionSet; // Instruction set |
||
4238 | if (iset & 0x20000) { |
||
4239 | droppedinstr += n; iset = 0; // Opcodes planned but never implemented |
||
4240 | } |
||
4241 | if (iset & 0x800) privilinstr += n; // Privileged instruction |
||
4242 | if (opcode->InstructionFormat & 0x4000) undocinstr += n; // Undocumented instruction |
||
4243 | |||
4244 | switch (iset & 0x37FF) { |
||
4245 | case 7: // MMX |
||
4246 | mmxinstr += n; break; |
||
4247 | case 0x11: // SSE |
||
4248 | sseinstr += n; break; |
||
4249 | case 0x12: // SSE2 |
||
4250 | sse2instr += n; break; |
||
4251 | case 0x13: // SSE3 |
||
4252 | sse3instr += n; break; |
||
4253 | case 0x14: // SSSE3 |
||
4254 | ssse3instr += n; break; |
||
4255 | case 0x15: // SSE4.1 |
||
4256 | sse41instr += n; break; |
||
4257 | case 0x16: // SSE4.2 |
||
4258 | sse42instr += n; break; |
||
4259 | case 0x17: case 0x18: case 0x19: // VEX etc. |
||
4260 | AVXinstr += n; break; |
||
4261 | case 0x1A: case 0x1B: // FMA and later instructions |
||
4262 | FMAinstr += n; break; |
||
4263 | case 0x1C: // AVX2 instructions |
||
4264 | AVX2instr += n; break; |
||
4265 | case 0x1D: case 0x1E: // BMI and other small instruction sets |
||
4266 | BMIinstr += n; break; |
||
4267 | case 0x20: // AVX-512 instructions |
||
4268 | AVX512instr += n; break; |
||
4269 | case 0x80: // MIC instructions |
||
4270 | MICinstr += n; break; |
||
4271 | case 0x1001: case 0x1002: case 0x1004: case 0x1005: case 0x1006: // AMD |
||
4272 | AMDinstr += n; break; |
||
4273 | case 0x2001: // VIA |
||
4274 | VIAinstr += n; break; |
||
4275 | } |
||
4276 | } |
||
4277 | } |
||
4278 | } |
||
4279 | |||
4280 | // output result |
||
4281 | printf("\n\nNumber of instruction opcodes supported by disassembler:\n%5i Total, including:", |
||
4282 | instructions); |
||
4283 | printf("\n%5i Privileged instructions", privilinstr); |
||
4284 | printf("\n%5i MMX instructions", mmxinstr); |
||
4285 | printf("\n%5i SSE instructions", sseinstr); |
||
4286 | printf("\n%5i SSE2 instructions", sse2instr); |
||
4287 | printf("\n%5i SSE3 instructions", sse3instr); |
||
4288 | printf("\n%5i SSSE3 instructions", ssse3instr); |
||
4289 | printf("\n%5i SSE4.1 instructions", sse41instr); |
||
4290 | printf("\n%5i SSE4.2 instructions", sse42instr); |
||
4291 | printf("\n%5i AVX instructions etc.", AVXinstr); |
||
4292 | printf("\n%5i AVX2 instructions", AVX2instr); |
||
4293 | printf("\n%5i FMA3 instructions", FMAinstr); |
||
4294 | printf("\n%5i BMI/micsellaneous instr.", BMIinstr); |
||
4295 | printf("\n%5i AVX-512 instructions", AVX512instr); |
||
4296 | printf("\n%5i MIC/Xeon Phi instructions", MICinstr); |
||
4297 | printf("\n%5i AMD instructions", AMDinstr); |
||
4298 | printf("\n%5i VIA instructions", VIAinstr); |
||
4299 | printf("\n%5i instructions planned but never implemented in any CPU", droppedinstr); |
||
4300 | printf("\n%5i undocumented or illegal instructions", undocinstr); |
||
4301 | printf("\n%5i instructions have both VEX and non-VEX versions", VEXdouble); |
||
4302 | printf("\n"); |
||
4303 | |||
4304 | #if 0 // temporary test code |
||
4305 | |||
4306 | // find entries with 0x2000 prefix code |
||
4307 | printf("\n\nInstructions with operand swap flag:\n"); |
||
4308 | // Loop through all maps |
||
4309 | for (map = 0; map < NumOpcodeTables1; map++) { |
||
4310 | // Loop through each map |
||
4311 | for (index = 0; index < OpcodeTableLength[map]; index++) { |
||
4312 | opcode = OpcodeTables[map] + index; |
||
4313 | if ((opcode->AllowedPrefixes & 0x2000) == 0x2000) { |
||
4314 | printf("\n%04X %02X %s", map, index, opcode->Name); |
||
4315 | } |
||
4316 | } |
||
4317 | } |
||
4318 | |||
4319 | /* |
||
4320 | printf("\n\nTables linked by type 0x0E:\n"); |
||
4321 | // Loop through all maps |
||
4322 | for (map = 0; map < NumOpcodeTables1; map++) { |
||
4323 | // Loop through each map |
||
4324 | for (index = 0; index < OpcodeTableLength[map]; index++) { |
||
4325 | opcode = OpcodeTables[map] + index; |
||
4326 | if (opcode->TableLink == 0x0E) { |
||
4327 | printf(" 0x%02X", opcode->InstructionSet); |
||
4328 | } |
||
4329 | } |
||
4330 | }*/ |
||
4331 | |||
4332 | printf("\n"); |
||
4333 | |||
4334 | #endif |
||
4335 | }>>>>>>>=>>>>=>>>=>=>>>=>>><>><>><>>>>>>>>>>>>>>>>>>>><>><>>><>>=>>=>>>>>>>>>>>>><>>>>>>>>>>=>><>><>>>>=>=><=>=><=>=><=>=>><>>>>>>>><> |