1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "jit/Disassembler.h"
8 
9 #include "jit/x86-shared/Encoding-x86-shared.h"
10 
11 using namespace js;
12 using namespace js::jit;
13 using namespace js::jit::X86Encoding;
14 using namespace js::jit::Disassembler;
15 
REX_W(uint8_t rex)16 MOZ_COLD static bool REX_W(uint8_t rex) { return (rex >> 3) & 0x1; }
REX_R(uint8_t rex)17 MOZ_COLD static bool REX_R(uint8_t rex) { return (rex >> 2) & 0x1; }
REX_X(uint8_t rex)18 MOZ_COLD static bool REX_X(uint8_t rex) { return (rex >> 1) & 0x1; }
REX_B(uint8_t rex)19 MOZ_COLD static bool REX_B(uint8_t rex) { return (rex >> 0) & 0x1; }
20 
21 MOZ_COLD static uint8_t
MakeREXFlags(bool w,bool r,bool x,bool b)22 MakeREXFlags(bool w, bool r, bool x, bool b)
23 {
24     uint8_t rex = (w << 3) | (r << 2) | (x << 1) | (b << 0);
25     MOZ_RELEASE_ASSERT(REX_W(rex) == w);
26     MOZ_RELEASE_ASSERT(REX_R(rex) == r);
27     MOZ_RELEASE_ASSERT(REX_X(rex) == x);
28     MOZ_RELEASE_ASSERT(REX_B(rex) == b);
29     return rex;
30 }
31 
32 MOZ_COLD static ModRmMode
ModRM_Mode(uint8_t modrm)33 ModRM_Mode(uint8_t modrm)
34 {
35     return ModRmMode((modrm >> 6) & 0x3);
36 }
37 
38 MOZ_COLD static uint8_t
ModRM_Reg(uint8_t modrm)39 ModRM_Reg(uint8_t modrm)
40 {
41     return (modrm >> 3) & 0x7;
42 }
43 
44 MOZ_COLD static uint8_t
ModRM_RM(uint8_t modrm)45 ModRM_RM(uint8_t modrm)
46 {
47     return (modrm >> 0) & 0x7;
48 }
49 
50 MOZ_COLD static bool
ModRM_hasSIB(uint8_t modrm)51 ModRM_hasSIB(uint8_t modrm)
52 {
53     return ModRM_Mode(modrm) != ModRmRegister && ModRM_RM(modrm) == hasSib;
54 }
55 MOZ_COLD static bool
ModRM_hasDisp8(uint8_t modrm)56 ModRM_hasDisp8(uint8_t modrm)
57 {
58     return ModRM_Mode(modrm) == ModRmMemoryDisp8;
59 }
60 MOZ_COLD static bool
ModRM_hasRIP(uint8_t modrm)61 ModRM_hasRIP(uint8_t modrm)
62 {
63 #ifdef JS_CODEGEN_X64
64     return ModRM_Mode(modrm) == ModRmMemoryNoDisp && ModRM_RM(modrm) == noBase;
65 #else
66     return false;
67 #endif
68 }
69 MOZ_COLD static bool
ModRM_hasDisp32(uint8_t modrm)70 ModRM_hasDisp32(uint8_t modrm)
71 {
72     return ModRM_Mode(modrm) == ModRmMemoryDisp32 ||
73            ModRM_hasRIP(modrm);
74 }
75 
76 MOZ_COLD static uint8_t
SIB_SS(uint8_t sib)77 SIB_SS(uint8_t sib)
78 {
79     return (sib >> 6) & 0x3;
80 }
81 
82 MOZ_COLD static uint8_t
SIB_Index(uint8_t sib)83 SIB_Index(uint8_t sib)
84 {
85     return (sib >> 3) & 0x7;
86 }
87 
88 MOZ_COLD static uint8_t
SIB_Base(uint8_t sib)89 SIB_Base(uint8_t sib)
90 {
91     return (sib >> 0) & 0x7;
92 }
93 
94 MOZ_COLD static bool
SIB_hasRIP(uint8_t sib)95 SIB_hasRIP(uint8_t sib)
96 {
97     return SIB_Base(sib) == noBase && SIB_Index(sib) == noIndex;
98 }
99 
100 MOZ_COLD static bool
HasRIP(uint8_t modrm,uint8_t sib,uint8_t rex)101 HasRIP(uint8_t modrm, uint8_t sib, uint8_t rex)
102 {
103     return ModRM_hasRIP(modrm) && SIB_hasRIP(sib);
104 }
105 
106 MOZ_COLD static bool
HasDisp8(uint8_t modrm)107 HasDisp8(uint8_t modrm)
108 {
109     return ModRM_hasDisp8(modrm);
110 }
111 
112 MOZ_COLD static bool
HasDisp32(uint8_t modrm,uint8_t sib)113 HasDisp32(uint8_t modrm, uint8_t sib)
114 {
115     return ModRM_hasDisp32(modrm) ||
116            (SIB_Base(sib) == noBase &&
117             SIB_Index(sib) == noIndex &&
118             ModRM_Mode(modrm) == ModRmMemoryNoDisp);
119 }
120 
121 MOZ_COLD static uint32_t
Reg(uint8_t modrm,uint8_t sib,uint8_t rex)122 Reg(uint8_t modrm, uint8_t sib, uint8_t rex)
123 {
124     return ModRM_Reg(modrm) | (REX_R(rex) << 3);
125 }
126 
127 MOZ_COLD static bool
HasBase(uint8_t modrm,uint8_t sib)128 HasBase(uint8_t modrm, uint8_t sib)
129 {
130     return !ModRM_hasSIB(modrm) ||
131            SIB_Base(sib) != noBase ||
132            SIB_Index(sib) != noIndex ||
133            ModRM_Mode(modrm) != ModRmMemoryNoDisp;
134 }
135 
136 MOZ_COLD static RegisterID
DecodeBase(uint8_t modrm,uint8_t sib,uint8_t rex)137 DecodeBase(uint8_t modrm, uint8_t sib, uint8_t rex)
138 {
139     return HasBase(modrm, sib)
140            ? RegisterID((ModRM_hasSIB(modrm) ? SIB_Base(sib) : ModRM_RM(modrm)) | (REX_B(rex) << 3))
141            : invalid_reg;
142 }
143 
144 MOZ_COLD static RegisterID
DecodeIndex(uint8_t modrm,uint8_t sib,uint8_t rex)145 DecodeIndex(uint8_t modrm, uint8_t sib, uint8_t rex)
146 {
147     RegisterID index = RegisterID(SIB_Index(sib) | (REX_X(rex) << 3));
148     return ModRM_hasSIB(modrm) && index != noIndex ? index : invalid_reg;
149 }
150 
151 MOZ_COLD static uint32_t
DecodeScale(uint8_t modrm,uint8_t sib,uint8_t rex)152 DecodeScale(uint8_t modrm, uint8_t sib, uint8_t rex)
153 {
154     return ModRM_hasSIB(modrm) ? SIB_SS(sib) : 0;
155 }
156 
157 #define PackOpcode(op0, op1, op2) ((op0) | ((op1) << 8) | ((op2) << 16))
158 #define Pack2ByteOpcode(op1) PackOpcode(OP_2BYTE_ESCAPE, op1, 0)
159 #define Pack3ByteOpcode(op1, op2) PackOpcode(OP_2BYTE_ESCAPE, op1, op2)
160 
161 uint8_t*
DisassembleHeapAccess(uint8_t * ptr,HeapAccess * access)162 js::jit::Disassembler::DisassembleHeapAccess(uint8_t* ptr, HeapAccess* access)
163 {
164     VexOperandType type = VEX_PS;
165     uint32_t opcode = OP_HLT;
166     uint8_t modrm = 0;
167     uint8_t sib = 0;
168     uint8_t rex = 0;
169     int32_t disp = 0;
170     int32_t imm = 0;
171     bool haveImm = false;
172     int opsize = 4;
173 
174     // Legacy prefixes
175     switch (*ptr) {
176       case PRE_LOCK:
177       case PRE_PREDICT_BRANCH_NOT_TAKEN: // (obsolete), aka %cs
178       case 0x3E: // aka predict-branch-taken (obsolete)
179       case 0x36: // %ss
180       case 0x26: // %es
181       case 0x64: // %fs
182       case 0x65: // %gs
183       case 0x67: // address-size override
184         MOZ_CRASH("Unable to disassemble instruction");
185       case PRE_SSE_F2: // aka REPNZ/REPNE
186         type = VEX_SD;
187         ptr++;
188         break;
189       case PRE_SSE_F3: // aka REP/REPE/REPZ
190         type = VEX_SS;
191         ptr++;
192         break;
193       case PRE_SSE_66: // aka PRE_OPERAND_SIZE
194         type = VEX_PD;
195         opsize = 2;
196         ptr++;
197         break;
198       default:
199         break;
200     }
201 
202     // REX and VEX prefixes
203     {
204         int x = 0, b = 0, m = 1, w = 0;
205         int r, l, p;
206         switch (*ptr) {
207 #ifdef JS_CODEGEN_X64
208           case PRE_REX | 0x0: case PRE_REX | 0x1: case PRE_REX | 0x2: case PRE_REX | 0x3:
209           case PRE_REX | 0x4: case PRE_REX | 0x5: case PRE_REX | 0x6: case PRE_REX | 0x7:
210           case PRE_REX | 0x8: case PRE_REX | 0x9: case PRE_REX | 0xa: case PRE_REX | 0xb:
211           case PRE_REX | 0xc: case PRE_REX | 0xd: case PRE_REX | 0xe: case PRE_REX | 0xf:
212             rex = *ptr++ & 0xf;
213             goto rex_done;
214 #endif
215           case PRE_VEX_C4: {
216             if (type != VEX_PS)
217                 MOZ_CRASH("Unable to disassemble instruction");
218             ++ptr;
219             uint8_t c4a = *ptr++ ^ 0xe0;
220             uint8_t c4b = *ptr++ ^ 0x78;
221             r = (c4a >> 7) & 0x1;
222             x = (c4a >> 6) & 0x1;
223             b = (c4a >> 5) & 0x1;
224             m = (c4a >> 0) & 0x1f;
225             w = (c4b >> 7) & 0x1;
226             l = (c4b >> 2) & 0x1;
227             p = (c4b >> 0) & 0x3;
228             break;
229           }
230           case PRE_VEX_C5: {
231             if (type != VEX_PS)
232               MOZ_CRASH("Unable to disassemble instruction");
233             ++ptr;
234             uint8_t c5 = *ptr++ ^ 0xf8;
235             r = (c5 >> 7) & 0x1;
236             l = (c5 >> 2) & 0x1;
237             p = (c5 >> 0) & 0x3;
238             break;
239           }
240           default:
241             goto rex_done;
242         }
243         type = VexOperandType(p);
244         rex = MakeREXFlags(w, r, x, b);
245         switch (m) {
246           case 0x1:
247             opcode = Pack2ByteOpcode(*ptr++);
248             goto opcode_done;
249           case 0x2:
250             opcode = Pack3ByteOpcode(ESCAPE_38, *ptr++);
251             goto opcode_done;
252           case 0x3:
253             opcode = Pack3ByteOpcode(ESCAPE_3A, *ptr++);
254             goto opcode_done;
255           default:
256             MOZ_CRASH("Unable to disassemble instruction");
257         }
258         if (l != 0) // 256-bit SIMD
259             MOZ_CRASH("Unable to disassemble instruction");
260     }
261   rex_done:;
262     if (REX_W(rex))
263         opsize = 8;
264 
265     // Opcode.
266     opcode = *ptr++;
267     switch (opcode) {
268 #ifdef JS_CODEGEN_X64
269       case OP_PUSH_EAX + 0: case OP_PUSH_EAX + 1: case OP_PUSH_EAX + 2: case OP_PUSH_EAX + 3:
270       case OP_PUSH_EAX + 4: case OP_PUSH_EAX + 5: case OP_PUSH_EAX + 6: case OP_PUSH_EAX + 7:
271       case OP_POP_EAX + 0: case OP_POP_EAX + 1: case OP_POP_EAX + 2: case OP_POP_EAX + 3:
272       case OP_POP_EAX + 4: case OP_POP_EAX + 5: case OP_POP_EAX + 6: case OP_POP_EAX + 7:
273       case OP_PUSH_Iz:
274       case OP_PUSH_Ib:
275         opsize = 8;
276         break;
277 #endif
278       case OP_2BYTE_ESCAPE:
279         opcode |= *ptr << 8;
280         switch (*ptr++) {
281           case ESCAPE_38:
282           case ESCAPE_3A:
283             opcode |= *ptr++ << 16;
284             break;
285           default:
286             break;
287         }
288         break;
289       default:
290         break;
291     }
292   opcode_done:;
293 
294     // ModR/M
295     modrm = *ptr++;
296 
297     // SIB
298     if (ModRM_hasSIB(modrm))
299         sib = *ptr++;
300 
301     // Address Displacement
302     if (HasDisp8(modrm)) {
303         disp = int8_t(*ptr++);
304     } else if (HasDisp32(modrm, sib)) {
305         memcpy(&disp, ptr, sizeof(int32_t));
306         ptr += sizeof(int32_t);
307     }
308 
309     // Immediate operand
310     switch (opcode) {
311       case OP_PUSH_Ib:
312       case OP_IMUL_GvEvIb:
313       case OP_GROUP1_EbIb:
314       case OP_GROUP1_EvIb:
315       case OP_TEST_EAXIb:
316       case OP_GROUP2_EvIb:
317       case OP_GROUP11_EvIb:
318       case OP_GROUP3_EbIb:
319       case Pack2ByteOpcode(OP2_PSHUFD_VdqWdqIb):
320       case Pack2ByteOpcode(OP2_PSLLD_UdqIb): // aka OP2_PSRAD_UdqIb, aka OP2_PSRLD_UdqIb
321       case Pack2ByteOpcode(OP2_PEXTRW_GdUdIb):
322       case Pack2ByteOpcode(OP2_SHUFPS_VpsWpsIb):
323       case Pack3ByteOpcode(ESCAPE_3A, OP3_PEXTRD_EdVdqIb):
324       case Pack3ByteOpcode(ESCAPE_3A, OP3_BLENDPS_VpsWpsIb):
325       case Pack3ByteOpcode(ESCAPE_3A, OP3_PINSRD_VdqEdIb):
326         // 8-bit signed immediate
327         imm = int8_t(*ptr++);
328         haveImm = true;
329         break;
330       case OP_RET_Iz:
331         // 16-bit unsigned immediate
332         memcpy(&imm, ptr, sizeof(int16_t));
333         ptr += sizeof(int16_t);
334         haveImm = true;
335         break;
336       case OP_ADD_EAXIv:
337       case OP_OR_EAXIv:
338       case OP_AND_EAXIv:
339       case OP_SUB_EAXIv:
340       case OP_XOR_EAXIv:
341       case OP_CMP_EAXIv:
342       case OP_PUSH_Iz:
343       case OP_IMUL_GvEvIz:
344       case OP_GROUP1_EvIz:
345       case OP_TEST_EAXIv:
346       case OP_MOV_EAXIv:
347       case OP_GROUP3_EvIz:
348         // 32-bit signed immediate
349         memcpy(&imm, ptr, sizeof(int32_t));
350         ptr += sizeof(int32_t);
351         haveImm = true;
352         break;
353       case OP_GROUP11_EvIz:
354         // opsize-sized signed immediate
355         memcpy(&imm, ptr, opsize);
356         imm = (imm << (32 - opsize * 8)) >> (32 - opsize * 8);
357         ptr += opsize;
358         haveImm = true;
359         break;
360       default:
361         break;
362     }
363 
364     // Interpret the opcode.
365     if (HasRIP(modrm, sib, rex))
366         MOZ_CRASH("Unable to disassemble instruction");
367 
368     size_t memSize = 0;
369     OtherOperand otherOperand(imm);
370     HeapAccess::Kind kind = HeapAccess::Unknown;
371     RegisterID gpr(RegisterID(Reg(modrm, sib, rex)));
372     XMMRegisterID xmm(XMMRegisterID(Reg(modrm, sib, rex)));
373     ComplexAddress addr(disp,
374                         DecodeBase(modrm, sib, rex),
375                         DecodeIndex(modrm, sib, rex),
376                         DecodeScale(modrm, sib, rex));
377     switch (opcode) {
378       case OP_GROUP11_EvIb:
379         if (gpr != RegisterID(GROUP11_MOV))
380             MOZ_CRASH("Unable to disassemble instruction");
381         MOZ_RELEASE_ASSERT(haveImm);
382         memSize = 1;
383         kind = HeapAccess::Store;
384         break;
385       case OP_GROUP11_EvIz:
386         if (gpr != RegisterID(GROUP11_MOV))
387             MOZ_CRASH("Unable to disassemble instruction");
388         MOZ_RELEASE_ASSERT(haveImm);
389         memSize = opsize;
390         kind = HeapAccess::Store;
391         break;
392       case OP_MOV_GvEv:
393         MOZ_RELEASE_ASSERT(!haveImm);
394         otherOperand = OtherOperand(gpr);
395         memSize = opsize;
396         kind = HeapAccess::Load;
397         break;
398       case OP_MOV_GvEb:
399         MOZ_RELEASE_ASSERT(!haveImm);
400         otherOperand = OtherOperand(gpr);
401         memSize = 1;
402         kind = HeapAccess::Load;
403         break;
404       case OP_MOV_EvGv:
405         if (!haveImm)
406             otherOperand = OtherOperand(gpr);
407         memSize = opsize;
408         kind = HeapAccess::Store;
409         break;
410       case OP_MOV_EbGv:
411         if (!haveImm)
412             otherOperand = OtherOperand(gpr);
413         memSize = 1;
414         kind = HeapAccess::Store;
415         break;
416       case Pack2ByteOpcode(OP2_MOVZX_GvEb):
417         MOZ_RELEASE_ASSERT(!haveImm);
418         otherOperand = OtherOperand(gpr);
419         memSize = 1;
420         kind = HeapAccess::Load;
421         break;
422       case Pack2ByteOpcode(OP2_MOVZX_GvEw):
423         MOZ_RELEASE_ASSERT(!haveImm);
424         otherOperand = OtherOperand(gpr);
425         memSize = 2;
426         kind = HeapAccess::Load;
427         break;
428       case Pack2ByteOpcode(OP2_MOVSX_GvEb):
429         MOZ_RELEASE_ASSERT(!haveImm);
430         otherOperand = OtherOperand(gpr);
431         memSize = 1;
432         kind = opsize == 8 ? HeapAccess::LoadSext64 : HeapAccess::LoadSext32;
433         break;
434       case Pack2ByteOpcode(OP2_MOVSX_GvEw):
435         MOZ_RELEASE_ASSERT(!haveImm);
436         otherOperand = OtherOperand(gpr);
437         memSize = 2;
438         kind = opsize == 8 ? HeapAccess::LoadSext64 : HeapAccess::LoadSext32;
439         break;
440 #ifdef JS_CODEGEN_X64
441       case OP_MOVSXD_GvEv:
442         MOZ_RELEASE_ASSERT(!haveImm);
443         otherOperand = OtherOperand(gpr);
444         memSize = 4;
445         kind = HeapAccess::LoadSext64;
446         break;
447 #endif // JS_CODEGEN_X64
448       case Pack2ByteOpcode(OP2_MOVDQ_VdqWdq): // aka OP2_MOVDQ_VsdWsd
449       case Pack2ByteOpcode(OP2_MOVAPS_VsdWsd):
450         MOZ_RELEASE_ASSERT(!haveImm);
451         otherOperand = OtherOperand(xmm);
452         memSize = 16;
453         kind = HeapAccess::Load;
454         break;
455       case Pack2ByteOpcode(OP2_MOVSD_VsdWsd): // aka OP2_MOVPS_VpsWps
456         MOZ_RELEASE_ASSERT(!haveImm);
457         otherOperand = OtherOperand(xmm);
458         switch (type) {
459           case VEX_SS: memSize = 4; break;
460           case VEX_SD: memSize = 8; break;
461           case VEX_PS:
462           case VEX_PD: memSize = 16; break;
463           default: MOZ_CRASH("Unexpected VEX type");
464         }
465         kind = HeapAccess::Load;
466         break;
467       case Pack2ByteOpcode(OP2_MOVDQ_WdqVdq):
468         MOZ_RELEASE_ASSERT(!haveImm);
469         otherOperand = OtherOperand(xmm);
470         memSize = 16;
471         kind = HeapAccess::Store;
472         break;
473       case Pack2ByteOpcode(OP2_MOVSD_WsdVsd): // aka OP2_MOVPS_WpsVps
474         MOZ_RELEASE_ASSERT(!haveImm);
475         otherOperand = OtherOperand(xmm);
476         switch (type) {
477           case VEX_SS: memSize = 4; break;
478           case VEX_SD: memSize = 8; break;
479           case VEX_PS:
480           case VEX_PD: memSize = 16; break;
481           default: MOZ_CRASH("Unexpected VEX type");
482         }
483         kind = HeapAccess::Store;
484         break;
485       case Pack2ByteOpcode(OP2_MOVD_VdEd):
486         MOZ_RELEASE_ASSERT(!haveImm);
487         otherOperand = OtherOperand(xmm);
488         switch (type) {
489           case VEX_PD: memSize = 4; break;
490           default: MOZ_CRASH("Unexpected VEX type");
491         }
492         kind = HeapAccess::Load;
493         break;
494       case Pack2ByteOpcode(OP2_MOVQ_WdVd):
495         MOZ_RELEASE_ASSERT(!haveImm);
496         otherOperand = OtherOperand(xmm);
497         switch (type) {
498           case VEX_PD: memSize = 8; break;
499           default: MOZ_CRASH("Unexpected VEX type");
500         }
501         kind = HeapAccess::Store;
502         break;
503       case Pack2ByteOpcode(OP2_MOVD_EdVd): // aka OP2_MOVQ_VdWd
504         MOZ_RELEASE_ASSERT(!haveImm);
505         otherOperand = OtherOperand(xmm);
506         switch (type) {
507           case VEX_SS: memSize = 8; kind = HeapAccess::Load; break;
508           case VEX_PD: memSize = 4; kind = HeapAccess::Store; break;
509           default: MOZ_CRASH("Unexpected VEX type");
510         }
511         break;
512       default:
513         MOZ_CRASH("Unable to disassemble instruction");
514     }
515 
516     *access = HeapAccess(kind, memSize, addr, otherOperand);
517     return ptr;
518 }
519 
520 #ifdef DEBUG
521 void
DumpHeapAccess(const HeapAccess & access)522 js::jit::Disassembler::DumpHeapAccess(const HeapAccess& access)
523 {
524     switch (access.kind()) {
525       case HeapAccess::Store:      fprintf(stderr, "store"); break;
526       case HeapAccess::Load:       fprintf(stderr, "load"); break;
527       case HeapAccess::LoadSext32: fprintf(stderr, "loadSext32"); break;
528       case HeapAccess::LoadSext64: fprintf(stderr, "loadSext64"); break;
529       default:                     fprintf(stderr, "unknown"); break;
530     }
531     fprintf(stderr, "%u ", unsigned(access.size()));
532 
533     switch (access.otherOperand().kind()) {
534       case OtherOperand::Imm:
535         fprintf(stderr, "imm %d", access.otherOperand().imm());
536         break;
537       case OtherOperand::GPR:
538         fprintf(stderr, "gpr %s", X86Encoding::GPRegName(access.otherOperand().gpr()));
539         break;
540       case OtherOperand::FPR:
541         fprintf(stderr, "fpr %s", X86Encoding::XMMRegName(access.otherOperand().fpr()));
542         break;
543       default: fprintf(stderr, "unknown");
544     }
545 
546     fprintf(stderr, " @ ");
547 
548     if (access.address().isPCRelative()) {
549         fprintf(stderr, MEM_o32r " ", ADDR_o32r(access.address().disp()));
550     } else if (access.address().hasIndex()) {
551         if (access.address().hasBase()) {
552             fprintf(stderr, MEM_obs " ",
553                     ADDR_obs(access.address().disp(), access.address().base(),
554                              access.address().index(), access.address().scale()));
555         } else {
556             fprintf(stderr, MEM_os " ",
557                     ADDR_os(access.address().disp(),
558                             access.address().index(), access.address().scale()));
559         }
560     } else if (access.address().hasBase()) {
561         fprintf(stderr, MEM_ob " ", ADDR_ob(access.address().disp(), access.address().base()));
562     } else {
563         fprintf(stderr, MEM_o " ", ADDR_o(access.address().disp()));
564     }
565 
566     fprintf(stderr, "\n");
567 }
568 #endif
569