void CheckAssertions (void) /* Check all assertions and evaluate the ones we can evaluate here. */ { unsigned I; /* Get the number of assertions */ unsigned Count = CollCount (&Assertions); /* Check the assertions */ for (I = 0; I < Count; ++I) { long Val; /* Get the next assertion */ Assertion* A = CollAtUnchecked (&Assertions, I); /* Ignore it, if it should only be evaluated by the linker */ if (!AssertAtAsmTime (A->Action)) { continue; } /* Can we evaluate the expression? */ if (IsConstExpr (A->Expr, &Val) && Val == 0) { /* Apply the action */ const char* Msg = GetString (A->Msg); switch (A->Action) { case ASSERT_ACT_WARN: LIWarning (&A->LI, 0, "%s", Msg); break; case ASSERT_ACT_ERROR: LIError (&A->LI, "%s", Msg); break; default: Internal ("Illegal assert action specifier"); break; } } } }
void ULabDone (void) /* Run through all unnamed labels, check for anomalies and errors and do * necessary cleanups. */ { /* Check if there are undefined labels */ unsigned I = ULabDefCount; while (I < CollCount (&ULabList)) { ULabel* L = CollAtUnchecked (&ULabList, I); LIError (&L->LineInfos, "Undefined label"); ++I; } /* Walk over all labels and emit a warning if any unreferenced ones * are found. Remove line infos because they're no longer needed. */ for (I = 0; I < CollCount (&ULabList); ++I) { ULabel* L = CollAtUnchecked (&ULabList, I); if (L->Ref == 0) { LIWarning (&L->LineInfos, 1, "No reference to unnamed label"); } ReleaseFullLineInfo (&L->LineInfos); } }
void SegDone (void) /* Check the segments for range and other errors. Do cleanup. */ { static const unsigned long U_Hi[4] = { 0x000000FFUL, 0x0000FFFFUL, 0x00FFFFFFUL, 0xFFFFFFFFUL }; static const long S_Hi[4] = { 0x0000007FL, 0x00007FFFL, 0x007FFFFFL, 0x7FFFFFFFL }; unsigned I; for (I = 0; I < CollCount (&SegmentList); ++I) { Segment* S = CollAtUnchecked (&SegmentList, I); Fragment* F = S->Root; while (F) { if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { /* We have an expression, study it */ ExprDesc ED; ED_Init (&ED); StudyExpr (F->V.Expr, &ED); /* Check if the expression is constant */ if (ED_IsConst (&ED)) { unsigned J; /* The expression is constant. Check for range errors. */ CHECK (F->Len <= 4); if (F->Type == FRAG_SEXPR) { long Hi = S_Hi[F->Len-1]; long Lo = ~Hi; if (ED.Val > Hi || ED.Val < Lo) { LIError (&F->LI, "Range error (%ld not in [%ld..%ld])", ED.Val, Lo, Hi); } } else { if (((unsigned long)ED.Val) > U_Hi[F->Len-1]) { LIError (&F->LI, "Range error (%lu not in [0..%lu])", (unsigned long)ED.Val, U_Hi[F->Len-1]); } } /* We don't need the expression tree any longer */ FreeExpr (F->V.Expr); /* Convert the fragment into a literal fragment */ for (J = 0; J < F->Len; ++J) { F->V.Data[J] = ED.Val & 0xFF; ED.Val >>= 8; } F->Type = FRAG_LITERAL; } else if (RelaxChecks == 0) { /* We cannot evaluate the expression now, leave the job for * the linker. However, we can check if the address size * matches the fragment size. Mismatches are errors in * most situations. */ if ((F->Len == 1 && ED.AddrSize > ADDR_SIZE_ZP) || (F->Len == 2 && ED.AddrSize > ADDR_SIZE_ABS) || (F->Len == 3 && ED.AddrSize > ADDR_SIZE_FAR)) { LIError (&F->LI, "Range error"); } } /* Release memory allocated for the expression decriptor */ ED_Done (&ED); }
void NextRawTok (void) /* Read the next raw token from the input stream */ { Macro* M; /* If we've a forced end of assembly, don't read further */ if (ForcedEnd) { CurTok.Tok = TOK_EOF; return; } Restart: /* Check if we have tokens from another input source */ if (InputFromStack ()) { if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) { /* This is a define style macro - expand it */ MacExpandStart (M); goto Restart; } return; } Again: /* Skip whitespace, remember if we had some */ if ((CurTok.WS = IsBlank (C)) != 0) { do { NextChar (); } while (IsBlank (C)); } /* Mark the file position of the next token */ Source->Func->MarkStart (Source); /* Clear the string attribute */ SB_Clear (&CurTok.SVal); /* Generate line info for the current token */ NewAsmLine (); /* Hex number or PC symbol? */ if (C == '$') { NextChar (); /* Hex digit must follow or DollarIsPC must be enabled */ if (!IsXDigit (C)) { if (DollarIsPC) { CurTok.Tok = TOK_PC; return; } else { Error ("Hexadecimal digit expected"); } } /* Read the number */ CurTok.IVal = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsXDigit (C)) { Error ("Number may not end with underline"); } } if (IsXDigit (C)) { if (CurTok.IVal & 0xF0000000) { Error ("Overflow in hexadecimal number"); CurTok.IVal = 0; } CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C); NextChar (); } else { break; } } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Binary number? */ if (C == '%') { NextChar (); /* 0 or 1 must follow */ if (!IsBDigit (C)) { Error ("Binary digit expected"); } /* Read the number */ CurTok.IVal = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsBDigit (C)) { Error ("Number may not end with underline"); } } if (IsBDigit (C)) { if (CurTok.IVal & 0x80000000) { Error ("Overflow in binary number"); CurTok.IVal = 0; } CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C); NextChar (); } else { break; } } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Number? */ if (IsDigit (C)) { char Buf[16]; unsigned Digits; unsigned Base; unsigned I; long Max; unsigned DVal; /* Ignore leading zeros */ while (C == '0') { NextChar (); } /* Read the number into Buf counting the digits */ Digits = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsXDigit (C)) { Error ("Number may not end with underline"); } } if (IsXDigit (C)) { /* Buf is big enough to allow any decimal and hex number to ** overflow, so ignore excess digits here, they will be detected ** when we convert the value. */ if (Digits < sizeof (Buf)) { Buf[Digits++] = C; } NextChar (); } else { break; } } /* Allow zilog/intel style hex numbers with a 'h' suffix */ if (C == 'h' || C == 'H') { NextChar (); Base = 16; Max = 0xFFFFFFFFUL / 16; } else { Base = 10; Max = 0xFFFFFFFFUL / 10; } /* Convert the number using the given base */ CurTok.IVal = 0; for (I = 0; I < Digits; ++I) { if (CurTok.IVal > Max) { Error ("Number out of range"); CurTok.IVal = 0; break; } DVal = DigitVal (Buf[I]); if (DVal >= Base) { Error ("Invalid digits in number"); CurTok.IVal = 0; break; } CurTok.IVal = (CurTok.IVal * Base) + DVal; } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Control command? */ if (C == '.') { /* Remember and skip the dot */ NextChar (); /* Check if it's just a dot */ if (!IsIdStart (C)) { /* Just a dot */ CurTok.Tok = TOK_DOT; } else { /* Read the remainder of the identifier */ SB_AppendChar (&CurTok.SVal, '.'); ReadIdent (); /* Dot keyword, search for it */ CurTok.Tok = FindDotKeyword (); if (CurTok.Tok == TOK_NONE) { /* Not found */ if (!LeadingDotInIdents) { /* Invalid pseudo instruction */ Error ("'%m%p' is not a recognized control command", &CurTok.SVal); goto Again; } /* An identifier with a dot. Check if it's a define style ** macro. */ if ((M = FindDefine (&CurTok.SVal)) != 0) { /* This is a define style macro - expand it */ MacExpandStart (M); goto Restart; } /* Just an identifier with a dot */ CurTok.Tok = TOK_IDENT; } } return; } /* Indirect op for sweet16 cpu. Must check this before checking for local ** symbols, because these may also use the '@' symbol. */ if (CPU == CPU_SWEET16 && C == '@') { NextChar (); CurTok.Tok = TOK_AT; return; } /* Local symbol? */ if (C == LocalStart) { /* Read the identifier. */ ReadIdent (); /* Start character alone is not enough */ if (SB_GetLen (&CurTok.SVal) == 1) { Error ("Invalid cheap local symbol"); goto Again; } /* A local identifier */ CurTok.Tok = TOK_LOCAL_IDENT; return; } /* Identifier or keyword? */ if (IsIdStart (C)) { /* Read the identifier */ ReadIdent (); /* Check for special names. Bail out if we have identified the type of ** the token. Go on if the token is an identifier. */ switch (SB_GetLen (&CurTok.SVal)) { case 1: switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) { case 'A': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_ABS; } else { CurTok.Tok = TOK_A; } return; case 'F': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_FAR; return; } break; case 'S': if ((CPU == CPU_4510) || (CPU == CPU_65816)) { CurTok.Tok = TOK_S; return; } break; case 'X': CurTok.Tok = TOK_X; return; case 'Y': CurTok.Tok = TOK_Y; return; case 'Z': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_ZP; return; } else { if (CPU == CPU_4510) { CurTok.Tok = TOK_Z; return; } } break; default: break; } break; case 2: if ((CPU == CPU_4510) && (toupper (SB_AtUnchecked (&CurTok.SVal, 0)) == 'S') && (toupper (SB_AtUnchecked (&CurTok.SVal, 1)) == 'P')) { CurTok.Tok = TOK_S; return; } /* FALL THROUGH */ default: if (CPU == CPU_SWEET16 && (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) { /* A sweet16 register number in sweet16 mode */ CurTok.Tok = TOK_REG; return; } } /* Check for define style macro */ if ((M = FindDefine (&CurTok.SVal)) != 0) { /* Macro - expand it */ MacExpandStart (M); goto Restart; } else { /* An identifier */ CurTok.Tok = TOK_IDENT; } return; } /* Ok, let's do the switch */ CharAgain: switch (C) { case '+': NextChar (); CurTok.Tok = TOK_PLUS; return; case '-': NextChar (); CurTok.Tok = TOK_MINUS; return; case '/': NextChar (); if (C != '*') { CurTok.Tok = TOK_DIV; } else if (CComments) { /* Remember the position, then skip the '*' */ Collection LineInfos = STATIC_COLLECTION_INITIALIZER; GetFullLineInfo (&LineInfos); NextChar (); do { while (C != '*') { if (C == EOF) { LIError (&LineInfos, "Unterminated comment"); ReleaseFullLineInfo (&LineInfos); DoneCollection (&LineInfos); goto CharAgain; } NextChar (); } NextChar (); } while (C != '/'); NextChar (); ReleaseFullLineInfo (&LineInfos); DoneCollection (&LineInfos); goto Again; } return; case '*': NextChar (); CurTok.Tok = TOK_MUL; return; case '^': NextChar (); CurTok.Tok = TOK_XOR; return; case '&': NextChar (); if (C == '&') { NextChar (); CurTok.Tok = TOK_BOOLAND; } else { CurTok.Tok = TOK_AND; } return; case '|': NextChar (); if (C == '|') { NextChar (); CurTok.Tok = TOK_BOOLOR; } else { CurTok.Tok = TOK_OR; } return; case ':': NextChar (); switch (C) { case ':': NextChar (); CurTok.Tok = TOK_NAMESPACE; break; case '-': CurTok.IVal = 0; do { --CurTok.IVal; NextChar (); } while (C == '-'); CurTok.Tok = TOK_ULABEL; break; case '+': CurTok.IVal = 0; do { ++CurTok.IVal; NextChar (); } while (C == '+'); CurTok.Tok = TOK_ULABEL; break; case '=': NextChar (); CurTok.Tok = TOK_ASSIGN; break; default: CurTok.Tok = TOK_COLON; break; } return; case ',': NextChar (); CurTok.Tok = TOK_COMMA; return; case ';': NextChar (); while (C != '\n' && C != EOF) { NextChar (); } goto CharAgain; case '#': NextChar (); CurTok.Tok = TOK_HASH; return; case '(': NextChar (); CurTok.Tok = TOK_LPAREN; return; case ')': NextChar (); CurTok.Tok = TOK_RPAREN; return; case '[': NextChar (); CurTok.Tok = TOK_LBRACK; return; case ']': NextChar (); CurTok.Tok = TOK_RBRACK; return; case '{': NextChar (); CurTok.Tok = TOK_LCURLY; return; case '}': NextChar (); CurTok.Tok = TOK_RCURLY; return; case '<': NextChar (); if (C == '=') { NextChar (); CurTok.Tok = TOK_LE; } else if (C == '<') { NextChar (); CurTok.Tok = TOK_SHL; } else if (C == '>') { NextChar (); CurTok.Tok = TOK_NE; } else { CurTok.Tok = TOK_LT; } return; case '=': NextChar (); CurTok.Tok = TOK_EQ; return; case '!': NextChar (); CurTok.Tok = TOK_BOOLNOT; return; case '>': NextChar (); if (C == '=') { NextChar (); CurTok.Tok = TOK_GE; } else if (C == '>') { NextChar (); CurTok.Tok = TOK_SHR; } else { CurTok.Tok = TOK_GT; } return; case '~': NextChar (); CurTok.Tok = TOK_NOT; return; case '\'': /* Hack: If we allow ' as terminating character for strings, read ** the following stuff as a string, and check for a one character ** string later. */ if (LooseStringTerm) { ReadStringConst ('\''); if (SB_GetLen (&CurTok.SVal) == 1) { CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0); CurTok.Tok = TOK_CHARCON; } else { CurTok.Tok = TOK_STRCON; } } else { /* Always a character constant */ NextChar (); if (C == EOF || IsControl (C)) { Error ("Illegal character constant"); goto CharAgain; } CurTok.IVal = C; CurTok.Tok = TOK_CHARCON; NextChar (); if (C != '\'') { if (!MissingCharTerm) { Error ("Illegal character constant"); } } else { NextChar (); } } return; case '\"': ReadStringConst ('\"'); CurTok.Tok = TOK_STRCON; return; case '\\': /* Line continuation? */ if (LineCont) { NextChar (); /* Next char should be a LF, if not, will result in an error later */ if (C == '\n') { /* Ignore the '\n' */ NextChar (); goto Again; } else { /* Make it clear what the problem is: */ Error ("EOL expected."); } } break; case '\n': NextChar (); CurTok.Tok = TOK_SEP; return; case EOF: CheckInputStack (); /* In case of the main file, do not close it, but return EOF. */ if (Source && Source->Next) { DoneCharSource (); goto Again; } else { CurTok.Tok = TOK_EOF; } return; } /* If we go here, we could not identify the current character. Skip it ** and try again. */ Error ("Invalid input character: 0x%02X", C & 0xFF); NextChar (); goto Again; }
static void SymCheckUndefined (SymEntry* S) /* Handle an undefined symbol */ { /* Undefined symbol. It may be... ** ** - An undefined symbol in a nested lexical level. If the symbol is not ** fixed to this level, search for the symbol in the higher levels and ** make the entry a trampoline entry if we find one. ** ** - If the symbol is not found, it is a real undefined symbol. If the ** AutoImport flag is set, make it an import. If the AutoImport flag is ** not set, it's an error. */ SymEntry* Sym = 0; if ((S->Flags & SF_FIXED) == 0) { SymTable* Tab = GetSymParentScope (S); while (Tab) { Sym = SymFind (Tab, GetStrBuf (S->Name), SYM_FIND_EXISTING | SYM_CHECK_ONLY); if (Sym && (Sym->Flags & (SF_DEFINED | SF_IMPORT)) != 0) { /* We've found a symbol in a higher level that is ** either defined in the source, or an import. */ break; } /* No matching symbol found in this level. Look further */ Tab = Tab->Parent; } } if (Sym) { /* We found the symbol in a higher level. Transfer the flags and ** address size from the local symbol to that in the higher level ** and check for problems. */ if (S->Flags & SF_EXPORT) { if (Sym->Flags & SF_IMPORT) { /* The symbol is already marked as import */ LIError (&S->RefLines, "Symbol `%s' is already an import", GetString (Sym->Name)); } if ((Sym->Flags & SF_EXPORT) == 0) { /* Mark the symbol as an export */ Sym->Flags |= SF_EXPORT; Sym->ExportSize = S->ExportSize; if (Sym->ExportSize == ADDR_SIZE_DEFAULT) { /* Use the actual size of the symbol */ Sym->ExportSize = Sym->AddrSize; } if (Sym->AddrSize > Sym->ExportSize) { /* We're exporting a symbol smaller than it actually is */ LIWarning (&Sym->DefLines, 1, "Symbol `%m%p' is %s but exported %s", GetSymName (Sym), AddrSizeToStr (Sym->AddrSize), AddrSizeToStr (Sym->ExportSize)); } } } if (S->Flags & SF_REFERENCED) { /* Mark as referenced and move the line info */ Sym->Flags |= SF_REFERENCED; CollTransfer (&Sym->RefLines, &S->RefLines); CollDeleteAll (&S->RefLines); } /* Transfer all expression references */ SymTransferExprRefs (S, Sym); /* Mark the symbol as unused removing all other flags */ S->Flags = SF_UNUSED; } else { /* The symbol is definitely undefined */ if (S->Flags & SF_EXPORT) { /* We will not auto-import an export */ LIError (&S->RefLines, "Exported symbol `%m%p' was never defined", GetSymName (S)); } else { if (AutoImport) { /* Mark as import, will be indexed later */ S->Flags |= SF_IMPORT; /* Use the address size for code */ S->AddrSize = CodeAddrSize; /* Mark point of import */ GetFullLineInfo (&S->DefLines); } else { /* Error */ LIError (&S->RefLines, "Symbol `%m%p' is undefined", GetSymName (S)); } } } }