diff --git a/include/asm/symbol.h b/include/asm/symbol.h index 727f44c4..d0fc764d 100644 --- a/include/asm/symbol.h +++ b/include/asm/symbol.h @@ -71,5 +71,6 @@ ULONG sym_GetDefinedValue(char *s); ULONG sym_isDefined(char *tzName); void sym_Purge(char *tzName); ULONG sym_isConstDefined(char *tzName); +int sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2); #endif diff --git a/src/asm/asmy.y b/src/asm/asmy.y index 1757012b..62bbbdd0 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -1040,9 +1040,14 @@ const : T_ID { $$ = sym_GetConstantValue($1); } | const T_OP_LOGICNE const { $$ = $1 != $3; } | const T_OP_ADD const { $$ = $1 + $3; } | const T_OP_SUB const { $$ = $1 - $3; } - | T_ID T_OP_SUB T_ID { $$ = sym_GetDefinedValue($1) - sym_GetDefinedValue($3); } + | T_ID T_OP_SUB T_ID + { + if (sym_IsRelocDiffDefined($1, $3) == 0) + fatalerror("'%s - %s' not defined.", $1, $3); + $$ = sym_GetDefinedValue($1) - sym_GetDefinedValue($3); + } | const T_OP_XOR const { $$ = $1 ^ $3; } - | const T_OP_OR const { $$ = $1 | $3; } + | const T_OP_OR const { $$ = $1 | $3; } | const T_OP_AND const { $$ = $1 & $3; } | const T_OP_SHL const { $$ = $1 << $3; } | const T_OP_SHR const { $$ = $1 >> $3; } diff --git a/src/asm/symbol.c b/src/asm/symbol.c index 01251b3a..10a76f97 100644 --- a/src/asm/symbol.c +++ b/src/asm/symbol.c @@ -619,6 +619,49 @@ sym_AddReloc(char *tzSym) pScope = findsymbol(tzSym, NULL); } +/* + * Check if the subtraction of two symbols is defined. That is, either both + * symbols are defined and the result is a constant, or both symbols are + * relocatable and belong to the same section. + * + * It returns 1 if the difference is defined, 0 if not. + */ +int +sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2) +{ + /* Do nothing the first pass. */ + if (nPass != 2) + return 1; + + struct sSymbol *nsym1, *nsym2; + + /* Do the symbols exist? */ + if ((nsym1 = sym_FindSymbol(tzSym1)) == NULL) + fatalerror("Symbol \"%s\" isn't defined.", tzSym1); + if ((nsym2 = sym_FindSymbol(tzSym2)) == NULL) + fatalerror("Symbol \"%s\" isn't defined.", tzSym2); + + int s1reloc = (nsym1->nType & SYMF_RELOC) != 0; + int s2reloc = (nsym2->nType & SYMF_RELOC) != 0; + + /* Both are non-relocatable */ + if (!s1reloc && !s2reloc) return 1; + + /* One of them relocatable, the other one not. */ + if (s1reloc ^ s2reloc) return 0; + + /* Both of them are relocatable. Make sure they are defined (internal + * coherency with sym_AddReloc and sym_AddLocalReloc). */ + if (!(nsym1->nType & SYMF_DEFINED)) + fatalerror("Relocatable symbol \"%s\" isn't defined.", tzSym1); + if (!(nsym2->nType & SYMF_DEFINED)) + fatalerror("Relocatable symbol \"%s\" isn't defined.", tzSym2); + + /* Both of them must be in the same section for the difference to be + * defined. */ + return nsym1->pSection == nsym2->pSection; +} + /* * Export a symbol */