Newer
Older
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(OptType),
OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
break;
}
} // end switch (OptType)
// always emit stack constant annotations, in case strata is
// instrumenting all instructions, or trying to verify speculative annotations.
this->AnnotateStackConstants(UseFP, AnnotFile);
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
// Likewise, we can tell mmStrata if a MemDest is an
// non-directly-accessed child object.
clc5q
committed
if (SDTInstrumentation || NoWarnFlag) {
if (strlen(this->DeadRegsString) > 0) {
// Optimize by informing mmStrata of dead registers. It can avoid saving
// and restoring dead state. This is particularly important for EFLAGS,
// as restoring the flags is a pipeline serializing instruction.
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR DEADREGS %s ZZ %s \n",
addr, this->SMPcmd.size, this->DeadRegsString, disasm);
}
#if SMP_CHILDACCESS_ALL_CODE
int ChildOffset, ChildSize;
if (MemDest && !OrphanCode
&& ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr,
ChildOffset, ChildSize)) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR CHILDACCESS %d %d ZZ %s \n",
addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm);
}
#endif
}
return;
} // end of SMPInstr::EmitAnnotations()
/**
* Emits Safe Returns
* Mark the type of the annotation as "-4". Currently the SDT is ignoring this
* annotation.
*/
void SMPInstr::EmitSafeReturn(FILE *AnnotFile)
{
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL SafeReturn %s\n",
this->address, -4, disasm);
// Emit all annotations for the instruction using RTL type inference.
void SMPInstr::EmitTypeAnnotations(bool UseFP, bool AllocSeen, bool NeedsFrame, FILE *AnnotFile, FILE *InfoAnnotFile) {
ea_t addr = this->address;
flags_t InstrFlags = getFlags(addr);
int TypeGroup = SMPTypeCategory[this->SMPcmd.itype];
bool NumericDEFs = this->AllDefsNumeric(); // all DEFs are NUMERIC or CODEPTR
bool ProfiledDEFs = this->AnyDefsProfiled(); // Some DEFs come from the profiler
bool UnusedMetadata = this->AllDefMetadataUnused();
bool MemDest = this->HasDestMemoryOperand();
bool MemSrc = this->HasSourceMemoryOperand();
bool SecondSrcOperandImmNum = this->IsSecondSrcOperandNumeric(InstrFlags); // assumes 2nd source is imm or not-numeric??
clc5q
committed
bool NoWarnFlag = false; // NOWARN annotation emitted?
bool CarryBorrow = ((this->SMPcmd.itype == NN_adc)
|| (this->SMPcmd.itype == NN_sbb));
// Do we have the special case in which a non-NUMERIC comes into
// an add with carry or subtract with borrow and the result
// has been inferred to be NUMERIC?
bool TypeChange = CarryBorrow && (!IsNumeric(this->AddSubUseType))
&& NumericDEFs;
SMPMetadataType DefMetadataType = this->GetDefMetadataType();
ProfilerInformation *ProfInfo;
ProfInfo = this->BasicBlock->GetFunc()->GetProg()->GetProfInfo();
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
++OptCount[this->OptType]; // keep count for debugging info
if (this->IsNop())
TypeGroup = 1; // no-op idioms need their category reset
// Emit appropriate optimization annotations.
bool SDTInstrumentation = false;
clc5q
committed
#if SMP_ANNOTATE_ALL_MEMORY_OPERANDS
// Emit informational annotations for memory operands.
if (MemSrc) {
op_t MemSrcOp = this->MDGetMemUseOp();
size_t SrcBitWidth = 8 * GetOpDataSize(MemSrcOp);
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6zu INSTR MEMSRC %zu", addr, this->SMPcmd.size, SrcBitWidth);
AnnotPrintOperand(MemSrcOp, InfoAnnotFile);
clc5q
committed
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
if (MemDest) {
op_t MemDestOp = this->MDGetMemDefOp();
size_t DestBitWidth = 8 * GetOpDataSize(MemDestOp);
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6zu INSTR MEMDEF %zu", addr, this->SMPcmd.size, DestBitWidth);
AnnotPrintOperand(MemDestOp, InfoAnnotFile);
clc5q
committed
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
clc5q
committed
#endif
// If the instruction is a CALL (or INDIR_CALL that has been resolved to
// a single target address), then we need to see if the call is to a
// function that has been forbidden by a security policy. If so, we
// need to output a security alert.
// In the near future, we will output SPRI instrumentation to prevent
// the system/library call from executing.
if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// We have a resolved call target address, either via direct or indirect call.
string FuncName = this->GetTrimmedCalledFunctionName();
ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName);
ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType);
if (ZST_DISALLOW == FuncCallPolicy) {
clc5q
committed
SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x in %s\n",
FuncName.c_str(), this->address, this->GetBlock()->GetFunc()->GetFuncName());
clc5q
committed
SMP_fprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]);
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR SECURITYCALL Disallow 1 1 %s \n", addr, this->SMPcmd.size, disasm);
}
}
// If the DEF metadata is all unused, mmStrata can skip the instruction.
// We omit this for groups 1 and 14, so that the metadata analysis
// does not get statistical credit for instructions that were already
// getting -1 annotations without analysis.
// We also cannot skip NN_adc and NN_sbb instructions that change the
// type of the incoming register.
if ((1 != TypeGroup) && (14 != TypeGroup) && (!this->MDIsInterruptCall())
&& !TypeChange) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataUnused %s \n",
++AnnotationCount[this->OptType];
return;
}
else if (DEF_METADATA_REDUNDANT == DefMetadataType) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataRedundant %s \n",
addr, -1, disasm);
++AnnotationCount[this->OptType];
return;
}
else if (DEF_METADATA_PROF_REDUNDANT == DefMetadataType) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataRedundant %s \n",
addr, -257, disasm);
++AnnotationCount[this->OptType];
clc5q
committed
// Profiler annotations could be backed off due to false
// positives, in which case we will need stack constant
// annotations.
this->AnnotateStackConstants(UseFP, AnnotFile);
switch (TypeGroup) {
case 0: // SDT will have to handle these
case 11: // PUSH/POP **!!** What if we push/pop NUMERIC type? Optimize?
// --jdh
// pop numeric's can be optimized with a numericdef annotation.
// numeric push's can't immediately be optimized, but if the stack location
// can be proven as dead metadata, then perhaps optimize.
// --jdh
// mmStrata wants to suppress warnings on the PUSH
// instructions that precede the LocalVarsAllocInstr
// (i.e. the PUSHes of callee-saved regs).
if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
addr, -3, disasm);
clc5q
committed
NoWarnFlag = true;
else if (this->MDIsPopInstr() && NumericDEFs) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType),
disasm);
++AnnotationCount[this->OptType];
}
else {
SDTInstrumentation = true;
}
break;
case 1: // nothing for SDT to do
case 14:
if (MemDest) {
clc5q
committed
SMP_msg("ERROR: MemDest in Type Category 1 or 14: %x %s\n", addr, disasm);
SDTInstrumentation = true;
break;
}
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
break;
case 4: // INC, DEC, etc.: no SDT work unless MemDest
if (MemDest || MemSrc) { // pretty conservative here?
// could be more aggressive if we know there's no overflow. -- jdh
SDTInstrumentation = true;
break; // treat as category 0
}
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL Always1stSrc %s \n",
addr, -1, disasm);
case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work.
if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
#endif
this->SetAddSubSourceType();
if (SecondSrcOperandImmNum
&& !this->MDIsFrameAllocInstr()
&& !TypeChange
#if SPECIAL_CASE_CARRY_BORROW
&& (!CarryBorrow)
#endif
) {
// treat as category 1
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
else if (IsEqType(NUMERIC, this->AddSubSourceType)
&& !this->MDIsFrameAllocInstr()
&& !TypeChange
#if SPECIAL_CASE_CARRY_BORROW
&& (!CarryBorrow)
#endif
) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL 2ndSrcNumeric %s \n",
addr, -1, disasm);
}
else if (NumericDEFs) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
#if SMP_OPTIMIZE_ADD_TO_NUMERIC
else if ((NN_add == this->SMPcmd.itype) && (!MemSrc)
&& IsNumeric(this->AddSubUseType)) {
// reg1 := reg1 + reg2, where reg1 comes in as NUMERIC,
// means that reg1 will get DEFed to the type of reg2,
// whatever it is. If reg2 were known to be NUMERIC,
// we would have hit one of the annotation cases above.
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s := %s ZZ AddToNumeric %s \n",
addr, -5, RegNames[this->AddSubUseOp.reg],
RegNames[this->AddSubSourceOp.reg], disasm);
++AnnotationCount[this->OptType];
}
#endif
else {
SDTInstrumentation = true;
}
break;
case 6: // Only OS code should include these; problem for SDT
SDTInstrumentation = true;
break; // treat as category 0
}
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL AlwaysPTR %s \n",
addr, -OptType, disasm);
break;
case 8: // Implicitly writes to EDX:EAX, always numeric.
if (this->OptType == 10) { // writes to ECX also
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n",
addr, -2, OptExplanation[this->OptType], disasm);
}
else {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ZZ %s %s \n",
addr, -2, OptExplanation[this->OptType], disasm);
}
SDTInstrumentation = true;
break;
case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0)
if (MemDest) {
SDTInstrumentation = true;
#if 0
if (NumericDEFs) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
}
#endif
}
else {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
break;
clc5q
committed
case 10: // AND, OR, etc.: If all DEFs have been inferred to be
// NUMERIC, then output optimizing annotation.
SDTInstrumentation = true;
if (MemDest) { // **!!** optimize with numeric annotation in future
break; // treat as category 0
}
else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
}
break;
case 12: // Exchange, exchange and add, conditional exchange: All NUMERIC
// sources ==> NUMERIC DEFs, so nothing for mmStrata to do.
if (MemDest) { // **!!** optimize with numeric annotation in future
SDTInstrumentation = true;
break; // treat as category 0
}
else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n", addr,
ProfiledDEFs ? -256-1 : -1, OptExplanation[TypeGroup], disasm);
}
else
SDTInstrumentation = true;
break;
case 15: // Floating point, NUMERIC, possible memory destination.
// If not memory destination, fpreg dest, so nothing for mmStrata to do.
if (MemDest) { // **!!** optimize with numeric annotation in future
SDTInstrumentation = true;
break; // treat as category 0
}
else { // NUMERIC floating register result; these regs are always NUMERIC
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n", addr,
-1, OptExplanation[TypeGroup], disasm);
}
break;
default: // 2,3,7: Optimization possibilities depend on operands
SDTInstrumentation = true;
if (MemDest) {
break; // treat as category 0
}
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(this->OptType),
OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
else if (NumericDEFs) { // NUMERIC move instruction
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
}
break;
} // end switch (OptType)
// always annotate stack constants for the profiler, etc.
this->AnnotateStackConstants(UseFP, AnnotFile);
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
// Likewise, we can tell mmStrata if a MemDest is an
// non-directly-accessed child object.
int ChildOffset, ChildSize;
clc5q
committed
if (SDTInstrumentation || NoWarnFlag) {
if (strlen(this->DeadRegsString) > 0) {
// Optimize by informing mmStrata of dead registers. It can avoid saving
// and restoring dead state. This is particularly important for EFLAGS,
// as restoring the flags is a pipeline serializing instruction.
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR DEADREGS %s ZZ %s \n",
addr, this->SMPcmd.size, this->DeadRegsString, disasm);
}
if (MemDest && ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr,
ChildOffset, ChildSize)) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR CHILDACCESS %d %d ZZ %s \n",
addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm);
}
#if SMP_IDENTIFY_POINTER_ADDRESS_REG
if (MemDest) {
set<DefOrUse, LessDefUse>::iterator PtrUse;
PtrUse = this->GetPointerAddressReg(this->DEFMemOp);
if (PtrUse != this->GetLastUse()) { // found POINTER addr reg USE
if (PtrUse->GetOp().type == o_reg) {
ushort PtrReg = PtrUse->GetOp().reg;
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR POINTER reg %s ZZ %s \n",
addr, this->SMPcmd.size, RegNames[PtrReg], disasm);
}
}
}
#endif
}
return;
} // end of SMPInstr::EmitTypeAnnotations()
// union of sign masks from 2 reg USEs for binary arithmetic
unsigned short SMPInstr::SignMaskUnionFromUseRegs(void) {
unsigned short UnionMask = 0;
unsigned short UseSignMask, DefSignMask;
set<DefOrUse, LessDefUse>::iterator UseIter;
op_t UseOp;
size_t RegOpCount = 0;
int UseHashValue;
struct FineGrainedInfo UseFGInfo, DefFGInfo;
clc5q
committed
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseOp = UseIter->GetOp();
clc5q
committed
if (MDIsGeneralPurposeReg(UseOp) && (!MDIsStackPtrReg(UseOp.reg, UseFP))) {
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
++RegOpCount;
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
bool LocalUseName = this->BasicBlock->IsLocalName(UseOp);
if (LocalUseName) {
// Local name, find in basic block maps.
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
UseSignMask = (FG_MASK_SIGNEDNESS_BITS & UseFGInfo.SignMiscInfo);
if (0 == UseSignMask) {
// Try to get signedness from DEF.
if (LocalUseName) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
}
DefSignMask = (FG_MASK_SIGNEDNESS_BITS & DefFGInfo.SignMiscInfo);
UnionMask |= DefSignMask;
}
else {
UnionMask |= UseSignMask;
}
}
}
if (2 > RegOpCount) { // only interested in binary arithmetic on two registers
UnionMask = 0;
}
return UnionMask;
} // SMPInstr::SignMaskUnionFromUseRegs()
// emit check annotations for signedness, overflow, truncation, etc.
void SMPInstr::EmitIntegerErrorAnnotations(FILE *InfoAnnotFile) {
set<DefOrUse, LessDefUse>::iterator UseIter, DefIter;
op_t UseOp, DefOp;
unsigned short UseWidthInfo, DefWidthInfo, SourceDefWidthInfo;
unsigned short UseSignInfo, DefSignInfo, SourceDefSignInfo;
unsigned short UseSignMask, DefSignMask, SourceDefSignMask;
struct FineGrainedInfo UseFGInfo, DefFGInfo, SourceDefFGInfo;
size_t UseBitWidth, DefBitWidth, UseMaxBitWidth, SourceDefBitWidth, DefMaxBitWidth;
ea_t DefAddr;
int UseHashValue, DefHashValue, SSANum, DefSSANum;
bool OverflowOpcode = this->MDIsOverflowingOpcode();
bool UnderflowOpcode = this->MDIsUnderflowingOpcode();
bool CheckForOverflow;
bool UseIsSigned, DefIsSigned, UseIsUnsigned, DefIsUnsigned, SourceDefIsSigned, SourceDefIsUnsigned;
bool UseSignMixed, SourceDefSignMixed; // inconsistent signedness
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
bool SignednessCheckEmitted = false;
bool SuppressSignednessCheck = false; // If we are not confident in check, set to true.
bool PartialStore; // Store is fewer bits than is defined for the target, e.g. overwriting last 8 bits
// of an int or a pointer. Cannot have signedness error in that case, as sign bit
// is not affected.
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
// If the instruction is the beginning of an infinite loop, we want no annotations other than
// the infinite loop annotation.
if (this->IsFirstInBlock()) {
if (this->GetBlock()->IsInfiniteSelfLoop()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR INFINITELOOP %s \n",
this->address, this->SMPcmd.size, disasm);
return;
}
}
// Case 1: Overflow on addition.
// Case 2: Underflow on subtraction.
if (OverflowOpcode || UnderflowOpcode) {
// If the flags register DEF is dead, we need a CHECK OVERFLOW/UNDERFLOW annotation.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = MD_FLAGS_REG;
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
if (this->BasicBlock->IsDefDead(this->address, DefOp)) {
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
clc5q
committed
SSANum = DefIter->GetSSANum();
bool IgnoreOverflow = this->IsBenignOverflow();
// Don't worry about stack space allocation instructions. The
// program will crash long before the stack pointer underflows
// below zero.
clc5q
committed
if (!(IgnoreOverflow || ((o_reg == DefOp.type) && DefOp.is_reg(MD_STACK_POINTER_REG)))) {
if (o_reg == DefOp.type) {
if (this->GetBlock()->IsBenignOverflowDEF(DefOp, SSANum, this->GetAddr())) {
IgnoreOverflow = true;
// e.g. if we overflow on ECX but the next USE is of CL as
// a shift counter, then program is intentionally tossing
// most of the ECX bits anyway.
}
else {
DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum);
if (this->BasicBlock->IsLocalName(DefOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
}
}
else if (MDIsStackAccessOpnd(DefOp, UseFP)) {
bool success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo);
assert(success);
}
else { // non-stack memory address; we know nothing about it.
DefFGInfo.SignMiscInfo = 0;
DefFGInfo.SizeInfo = 0;
}
if (!IgnoreOverflow) {
DefSignInfo = DefFGInfo.SignMiscInfo;
DefSignMask = DefSignInfo & FG_MASK_SIGNEDNESS_BITS;
DefWidthInfo = DefFGInfo.SizeInfo;
DefBitWidth = LargestBitWidthFromMask(DefWidthInfo);
if (0 == DefBitWidth) {
// Could happen for non-stack memory operands, for example.
DefBitWidth = MD_NORMAL_MACHINE_BITWIDTH;
}
if (DefSignMask == 0) {
// If no sign info from DEF, see if we can get it from the USE regs.
DefSignMask = this->SignMaskUnionFromUseRegs();
}
if (OverflowOpcode) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu ",
this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth);
}
else { // must be UnderflowOpcode
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK UNDERFLOW %s %zu ",
this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth);
}
AnnotPrintOperand(DefOp, InfoAnnotFile);
clc5q
committed
string SinkString("");
if (this->GetBlock()->GetFunc()->HasIntErrorCallSink(DefOp, SSANum, this->address, SinkString)) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm);
clc5q
committed
}
else {
clc5q
committed
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
clc5q
committed
}
} // end if (!IgnoreOverflow)
clc5q
committed
} // end if (!(IgnoreOverflow or DEF is stack ptr register))
} // end if flags reg is dead
} // end cases 1-2
// Case 3: Overflow on multiplication with upper bits discarded.
if (this->MDIsMultiply()) {
// There are four overflow sub-cases for x86: (A) the multiplication result
// can go into EDX:EAX for 32x32=>64 bit multiplication; (B) the result
// can go into DX:AX for 16x16=>32 bit; (C) the result can be in AX
// for 8x8=>16 bit; (D) see below. The latter case (C) will be detected most easily
// as a truncation in a later instruction, i.e. if only AL gets stored
// later, then we check the AH bits at that time for a truncation
// error. Because our SSA numbering lumps AL, AH, AX, and EAX into
// a single canonicalized register, we would have a hard time using
// SSA-based def-use chains to determine if AH is dead.
// For the other two sub-cases, the question is whether EDX becomes dead
// starting with the DEF of EDX in the multiply instruction.
// Case (D) is where the multiply instruction discards the upper bits
// of the multiply.
// Sub-cases A&B are detected by checking if EDX is dead, and if so, then
// emitting an annotation to check for the overflow flag. The x86 sets
// overflow and carry flags on multiplication instructions based on whether
// the result carries out of the lower half of the result to the upper half.
// Sub-case D is also detected using flags, but we don't need to check whether EDX
// is dead. We just need to detect that EDX is not in the DEF set in the
// first place. We have a private member flag for that case.
CheckForOverflow = false;
if (this->AreMultiplicationBitsDiscarded()) { // Sub-case D
CheckForOverflow = true;
assert(this->RTL.GetCount() > 0);
DefOp = this->RTL.GetRT(0)->GetLeftOperand();
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
}
else {
// If the instruction were EDX:=EDX*foo, then it would be
// the multiplication bits discarded case and would not
// reach this else clause. Therefore, if we find EDX in
// the DEF set, it is holding upper result bits of the
// multiplication and we have the potential for sub-cases A&B
// but not sub-case C. So, we check to see if the DEF of EDX
// is dead.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = R_dx;
DefIter = this->FindDef(DefOp);
if (DefIter != this->GetLastDef()) {
// We found DEF of EDX, so it is not AX:=AL*op8 sub-case C.
// Now, is DEF of EDX dead (i.e. no uses?)
CheckForOverflow = this->BasicBlock->IsDefDead(this->address, DefOp);
if (CheckForOverflow) {
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
}
}
} // end if sub-case D else if sub-case A or B
if (CheckForOverflow) { // need an annotation
if (this->BasicBlock->IsLocalName(DefOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
DefWidthInfo = DefFGInfo.SizeInfo;
DefBitWidth = LargestBitWidthFromMask(DefWidthInfo);
DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if (DefSignMask == 0) {
// If no sign info from DEF, see if we can get it from the USE regs.
DefSignMask = this->SignMaskUnionFromUseRegs();
}
// Next two statements exclude the inconsistent sign case and the no sign info known case.
DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND
DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND
if (this->MDIsUnsignedArithmetic() || DefIsUnsigned) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg], disasm);
}
else if (this->MDIsSignedArithmetic() || DefIsSigned) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg], disasm);
}
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW UNKNOWNSIGN %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg], disasm);
}
} // end of case 3
// Case 4: Signedness error on move.
// Case 5: Truncation error on move.
UseOp = this->GetMoveSource();
if ((3 == this->OptType) && (o_reg == UseOp.type) && (!(this->IsBenignTruncation()))) {
// Possibilities for a move: reg to reg, mem to reg, or reg to mem. If we load
// from memory into a register, we cannot track signedness in memory unless it
// is a stack location. In that case, we record the signedness in the stack
// map and transfer it to the reg DEF in SMPInstr.MDSetWidthSignInfo(). That
// determines the signedness of the reg DEF and it cannot be in conflict with
// the stack memory USE. The load from stack to reg also determines width
// of the stack operand and we cannot have a truncation. So, we can restrict
// our analysis of cases 4-5 to register-source move instructions, as we
// have done in the condition above.
//
// Similarly, we cannot detect a signedness conflict if the destination is a
// memory location that is not known to be a particular stack offset location.
//
// So, we only concern ourselves with signedness errors
// when the USE operand of the move is a register, and the destination is another
// register or a stack location.
//
// We can have a truncation error and a signedness error on a single instruction, so
// we group them into common code. For example, move the lower half of a 32-bit unsigned
// into a 16-bit signed destination. Upper bits set to 1 and discarded would be a
// truncation, and setting the sign bit of the 16-bit signed destination would be a
// signedness error.
//
// NOTE: Signedness errors are different from overflow and truncation errors. We
// can have incomplete knowledge about an instructions operands and still determine
// that truncation occurred. For example, if we do not know whether register EAX
// is signed or unsigned, we can still say that storing only AX is a truncation error
// if the upper half of EAX is a mixture of one and zero bits. If EAX is unsigned,
// we could be more specific and insist that the upper half be all zero bits; if EAX
// is signed, we could insist that the upper half of EAX be the sign-extension of AX.
// We can avoid false positives by only declaring a truncation error when the upper
// half of EAX is not all zero bits or all one bits. This approach allows a few
// potential false negatives. With signedness, if we don't know the signedness
// of one of the operands, we can only avoid false positives by doing no checks at
// all.
UseIter = this->FindUse(UseOp);
assert(UseIter != this->GetLastUse());
UseBitWidth = 8 * GetOpDataSize(UseOp);
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
// Now, the question is: Are we storing fewer bits than
// we were using in our computations in this DEF-USE chain?
// E.g. if we computed using 32 bits and then only store 16,
// we have potential truncation error. But if we computed
// using 16 bits all along, we have already checked for 16-bit
// overflows on arithmetic in the DU chain and there can be no
// truncation on this store.
op_t SearchOp = UseOp;
// Canonicalize sub-regs for searching DEFs and USEs.
SearchOp.reg = MDCanonicalizeSubReg(UseOp.reg);
SearchOp.dtyp = dt_dword;
UseHashValue = HashGlobalNameAndSSA(SearchOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(SearchOp)) {
// Local name, find in basic block maps.
SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
SourceDefWidthInfo = SourceDefFGInfo.SizeInfo;
UseWidthInfo = UseFGInfo.SizeInfo;
SourceDefBitWidth = LargestBitWidthFromMask(SourceDefWidthInfo);
UseMaxBitWidth = LargestBitWidthFromMask(UseWidthInfo);
UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
SourceDefSignInfo = SourceDefFGInfo.SignMiscInfo;
SourceDefSignMask = (SourceDefSignInfo & FG_MASK_SIGNEDNESS_BITS);
#if 1
// If we have no signedness info at all for the UseSignMask, but
// the SourceDefSignMask has info, then we want to use the
// SourceDefSignMask as our signedness info. This is because of
// simple instruction sequences in which no signedness can be inferred
// from the use, e.g.:
//
// imul eax,ebx ; eax := eax*ebx and eax&ebx are signed
// mov [ebp-8],al ; store al on stack
//
// If we don't know the signedness of stack location [ebp-8],
// then we will end up in the S->? case below and we will emit
// a CHECK TRUNCATION UNKNOWNSIGN annotation. This discards the
// knowledge we have that EAX is signed, from the IMUL opcode.
if (0 == UseSignMask) {
UseSignMask = SourceDefSignMask;
}
#endif
// Next six statements exclude the inconsistent sign case and the no sign info known case.
UseIsSigned = (FG_MASK_SIGNED == UseSignMask); // exact, not bit-mask-AND
UseIsUnsigned = (FG_MASK_UNSIGNED == UseSignMask); // exact, not bit-mask-AND
SourceDefIsSigned = (FG_MASK_SIGNED == SourceDefSignMask); // exact, not bit-mask-AND
SourceDefIsUnsigned = (FG_MASK_UNSIGNED == SourceDefSignMask); // exact, not bit-mask-AND
UseSignMixed = (FG_MASK_INCONSISTENT_SIGN == UseSignMask); // exclude uninit sign case
SourceDefSignMixed = (FG_MASK_INCONSISTENT_SIGN == SourceDefSignMask); // exclude uninit sign case
// Not only the CHECK SIGNEDNESS annotations depend on the signedness of the
// source and destination operands. The CHECK TRUNCATION annotations come
// in SIGNED, UNSIGNED, and UNKNOWNSIGN variants, so we need to get the
// signedness of the destination operand before we proceeed.
DefOp = this->RTL.GetRT(0)->GetLeftOperand(); // RTL must be dest := rhs
op_t DestSearchOp = DefOp;
bool StackDestination;
// Outargs locations are reused for different function calls, so no inference of their
// signedness is valid. We maintain a flag to suppress signedness checks on writes to
// outargs locations on the stack.
bool OutArgsWrite = false;
if (o_reg == DestSearchOp.type) {
StackDestination = false;
DestSearchOp.reg = MDCanonicalizeSubReg(DefOp.reg);
DestSearchOp.dtyp = dt_dword;
}
else if (!(MDIsStackAccessOpnd(DefOp, UseFP))) {
// If destination of move is not a register and is not
// a stack location, we cannot track its signedness and width.
return;
}
else {
StackDestination = true;
}
DefIter = this->FindDef(DestSearchOp);
DefSSANum = DefIter->GetSSANum();
if (StackDestination) {
// Fetch FG info from stack map.
bool success = this->GetBlock()->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo);
assert(success);
OutArgsWrite = this->GetBlock()->GetFunc()->IsInOutgoingArgsRegion(DefOp);
}
else {
// Fetch FG info from register FG info maps.
DefHashValue = HashGlobalNameAndSSA(DestSearchOp, DefSSANum);
if (this->BasicBlock->IsLocalName(DestSearchOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
// Next two statements exclude the inconsistent sign case and the no sign info known case.
DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND
DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND
// Get the Def bit width and maximum bit width for special cases.
DefWidthInfo = DefFGInfo.SizeInfo;
DefMaxBitWidth = LargestBitWidthFromMask(DefWidthInfo); // max width over all defs
DefBitWidth = 8 * GetOpDataSize(DefOp); // width of def in current instruction
PartialStore = (DefBitWidth < DefMaxBitWidth);
if (StackDestination) {
// We only do signedness checks in limited, safe analysis cases on stack writes.
SuppressSignednessCheck = this->SkipSignednessCheckOnStackWrite(DefSSANum);
}
else {
SuppressSignednessCheck = false;
}
// If we set the (source) DEF bit width to 0, it means we wanted to have the USEs determine
// the width. This happens on sign-extended and zero-extended loads. If we zero-extend
// a 16-bit value to 32 bits, then immediately store the lower 16 bits to a 16-bit location,
// then the upper bits cannot have any overflow info yet. But if we do 32-bit arithmetic
// on the zero-extended value, and then store the lower 16 bits, we need to check for
// truncation. So, the key is whether the value ever got used as a 32-bit value. If it
// did, check for truncation; if not, there is no need to check.
if ((SourceDefBitWidth > UseBitWidth)
|| ((SourceDefBitWidth == 0) && (UseMaxBitWidth > UseBitWidth))) {
// Original DEF (or subsequent USE) was wider than what we are storing now.
unsigned short SourceDefReg = SearchOp.reg;
unsigned short UseReg = UseOp.reg;
if (SourceDefBitWidth == 0) { // Convert for printing annotation.
SourceDefBitWidth = 8 * GetOpDataSize(SearchOp);
}
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
// OK, we need to check for possible truncation. But, how we check depends on the
// signedness combinations of the source and destination operands of the move.
// Each operand can be signed, unsigned, or of unknown sign (and we lump the
// inconsistent sign case into the unknown sign case). So, we have a set of 3x3=9
// possible combinations of signedness.
// Now we have the DefSignMask to compare to the UseSignMask. The nine possible
// combinations, and the annotations we want to emit for each, are shown below.
// S = SIGNED, U = UNSIGNED, and ? = unknown or inconsistent sign.
// S => U indicates a SIGNED being stored into an UNSIGNED, for example.
// Assume without loss of generality that register EAX is the source of
// all the move instructions, and that only subword register AX is being stored.
// We can perform all truncation and signedness checks on EAX just prior to
// the move instruction, which is cheaper than performing checks on the
// destination if the destination is in memory.
//
// U => U
// U => S
// S => U
// U => ?
// ? => U
//
// In these first five cases, EAX must be the zero-extension of AX else there is
// a truncation error. In the three cases in which the source (EAX/AX) is UNSIGNED,
// discarding upper bits that are not zero is obviously truncation. In the case
// of S => U, if the upper bits of EAX are not all zeroes, then we either have
// a large positive value of EAX that is being truncated, or EAX is negative and
// the lower bits will be misinterpreted in the unsigned destination. Finally,
// the ? => U case must be either U => U or S => U, and these two cases already
// share the demand that EAX be the zero-extension of AX. So, these five cases
// will receive the annotation: CHECK TRUNCATION UNSIGNED 32 EAX 16 AX which
// means that EAX is tested against AX to see if it is the 32-bit zero-extension
// of 16-bit reg AX.
// In the U => S case, we can have a signedness error as well as truncation. Even
// if the truncation check passes (all upper half bits of EAX are zero), the top
// bit of AX might be 1, and this will be misinterpreted as a sign bit in the
// destination. So, this case receives a second annotation: CHECK SIGNEDNESS SIGNED 16 AX.
// In the two cases that involve signedness uncertainty, there are possible signedness
// errors that we are not checking ar tun-time, because we do not have enough information
// to perform the checks without generating many more false positives than true positives.
// As a result, false negatives on signedness can occur.
//
// On to more of the 9 combinations:
//
// S => S
//
// In this case, EAX must be the sign-extension of AX. Because the destination is also
// signed, nothing is lost if the sign-extension bits (all zeroes or all ones) are dropped.
// We emit a CHECK TRUNCATION SIGNED 32 EAX 16 AX annotation to test EAX == sign-extended AX.
//
// S => ?
// ? => S
// ? => ?
//
// These final three cases all involve at least one operand of unknown signedness, and no
// operands that are known to be unsigned. In each case, there are two possibilities:
// either EAX must be the sign-extension of AX, or EAX must be the zero-extension of AX.
// Because of the uncertainty that is represented by the question marks, we are not sure
// which of these two cases we are dealing with. However, rather than just give up and
// perform no run-time checks (to avoid false positives), we can still perform a run-time
// check that will catch (perhaps most) true positives while causing no false positives.
// We can insist that EAX must be EITHER the sign-extension or the zero-extension of AX.
// To be neither type of extension of AX implies that some sort of truncation is happening.
// So, we emit a CHECK TRUNCATION UNKNOWNSIGN 32 EAX 16 AX annotation, and the Strata
// instrumentation will check for either EAX == sign-extended AX or EAX == zero-extended AX
// being true. If neither is true, we raise a true positive alert. False negatives on
// signedness errors are the result of the uncertainty, but all truncations are detected
// for all nine cases.
if (DefIsUnsigned || UseIsUnsigned) {
// First five cases above: any UNSIGNED operand leads to CHECK TRUNCATION UNSIGNED annotation.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNSIGNED %zu %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), disasm);
if (!SuppressSignednessCheck && UseIsUnsigned && DefIsSigned && !OutArgsWrite && !PartialStore) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if (DefIsSigned && UseIsSigned) {
// S => S case above. Emit CHECK TRUNCATION SIGNED annotation.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
// S => ?, ? => S, ? => ? cases above: CHECK TRUNCATION UNKNOWNSIGN annotation.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNKNOWNSIGN %zu %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), disasm);
}
#if 1
// Now check for signedness conflicts between the UseOp USEs and its DEF.
if (!SuppressSignednessCheck && !OutArgsWrite && !PartialStore) { // Inferred signedness of outargs location is invalid anyway
if (UseIsSigned && SourceDefIsUnsigned) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if (UseIsUnsigned && SourceDefIsSigned) {
// Currently same annotation, but might differ in the future for better forensics
// and more precise diagnostic messages.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if ((!SourceDefSignMixed) && UseSignMixed) {
// DEF has consistent and known signedness, USE is inconsistent.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, SignednessStrings[SourceDefSignMask],
UseBitWidth, MDGetRegName(UseOp), disasm);
}
}
#endif
} // end if truncation
else if (!SuppressSignednessCheck && !OutArgsWrite && !PartialStore) { // still need to check for signedness errors even if no truncation
if (UseIsSigned && DefIsUnsigned) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if (UseIsUnsigned && DefIsSigned) {
// Currently UNSIGNED and SIGNED annotations for signedness differ only in the
// arithmetic saturation value applied as a recovery action. UNSIGNED means
// left-hand-side is UNSIGNED so saturate a negative right-hand-side up to 0,
// while SIGNED means lhs is SIGNED so saturate large rhs to 0x7fff....
// For inconsistent sign cases we default to SIGNED.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if (UseIsSigned && SourceDefIsUnsigned) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if (UseIsUnsigned && SourceDefIsSigned) {
// Currently same annotation, but might differ in the future for better forensics
// and more precise diagnostic messages.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
clc5q
committed
#if 0
else if ((!SourceDefSignMixed) && UseSignMixed) {
clc5q
committed
// source DEF has consistent and known signedness, source USE is inconsistent.
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %u %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
#endif
} // end if truncation else check signedness
} // end of cases 4-5, (3 == OptType) checking for TRUNCATION and SIGNEDNESS errors
else if ((this->MDIsLoadEffectiveAddressInstr()) && (!(this->IsNop()))) {
// Case 6: Load Effective Address opcodes can do arithmetic that silently overflows.
this->MDEmitLeaOpcodeOverflowAnnotations(InfoAnnotFile);
} // end of case 6, LoadEffectiveAddress instruction
else if (this->MDDoublesWidth()) {
// case 7: half of register is sign-extended into the full register.
// Potential truncation error if entire register was DEFed first, because
// only the lower half is used. That also makes a potential signedness
// error, because the "sign bit" that is being extended is really a data bit,
// not a sign bit.
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
assert(o_reg == DefOp.type);