Skip to content

Commit b66fae3

Browse files
committed
Fix #6028: normalize SDCC assembly before preprocessing
1 parent 8bb772d commit b66fae3

5 files changed

Lines changed: 253 additions & 8 deletions

File tree

‎lib/cppcheck.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
10321032
// Get directives
10331033
std::list<Directive> directives;
10341034
preprocessor.createDirectives(directives);
1035-
preprocessor.simplifyPragmaAsm();
1035+
preprocessor.simplifyAsm();
10361036

10371037
std::set<std::string> configurations;
10381038
std::set<std::string> configDefines = { "__cplusplus" };
@@ -1060,7 +1060,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
10601060
if (!mSettings.keepComments)
10611061
Preprocessor::removeComments(data.tokens);
10621062
Preprocessor::createDirectives(data.tokens, directives);
1063-
Preprocessor::simplifyPragmaAsm(data.tokens);
1063+
Preprocessor::simplifyAsm(data.tokens);
10641064
// Discover new configurations from included file
10651065
if (configurations.size() < maxConfigs)
10661066
preprocessor.getConfigs(data.filename, data.tokens, configDefines, configurations);

‎lib/preprocessor.cpp‎

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,15 +1133,89 @@ std::size_t Preprocessor::calculateHash(const std::string &toolinfo) const
11331133
return (std::hash<std::string>{})(hashData);
11341134
}
11351135

1136-
void Preprocessor::simplifyPragmaAsm()
1136+
static simplecpp::Token* simplifySdccAsm(simplecpp::TokenList& tokenList, simplecpp::Token* start)
11371137
{
1138-
simplifyPragmaAsm(mTokens);
1138+
const simplecpp::Token* previous = start->previousSkipComments();
1139+
if (sameline(start, previous) && previous->op != '{' && previous->op != '}' && previous->op != ';')
1140+
return nullptr;
1141+
// Do not rewrite a macro definition or a GNU/MS-style asm statement.
1142+
for (const simplecpp::Token* tok = previous; sameline(start, tok); tok = tok->previous) {
1143+
if (tok->op == '#')
1144+
return nullptr;
1145+
}
1146+
const simplecpp::Token* first = start->nextSkipComments();
1147+
if (!first || (sameline(start, first) && first->op != ';') ||
1148+
first->op == '(' || first->op == '{' || first->str() == "volatile" ||
1149+
first->str() == "__volatile" || first->str() == "__volatile__" ||
1150+
first->str() == "goto" || first->str() == "inline")
1151+
return nullptr;
1152+
1153+
simplecpp::Token* end = start->next;
1154+
const simplecpp::Token* comment = nullptr;
1155+
bool nestedAsm = false;
1156+
for (; end; end = end->next) {
1157+
if (end->comment || sameline(end, comment))
1158+
continue;
1159+
if (end->op == ';') {
1160+
comment = end;
1161+
continue;
1162+
}
1163+
if (end->str() == "__endasm" && !sameline(end, end->previousSkipComments())) {
1164+
const simplecpp::Token* next = end->nextSkipComments();
1165+
if (!sameline(end, next) || next->op == ';')
1166+
break;
1167+
}
1168+
// An incomplete block must not consume C code or a different conditional
1169+
// branch. Leave such input to the normal preprocessor/tokenizer.
1170+
if (end->str() == "__asm")
1171+
nestedAsm = true;
1172+
if (end->op == '{' || end->op == '}' ||
1173+
(end->op == '#' && !sameline(end, end->previousSkipComments())))
1174+
return end->previous;
1175+
}
1176+
if (!end)
1177+
return tokenList.back();
1178+
// Do not normalize an inner block after rejecting an ambiguous outer one.
1179+
if (nestedAsm)
1180+
return end;
1181+
1182+
// Preserve an asm statement as an analysis barrier, including for empty
1183+
// blocks. Hide assembler # operands before simplecpp stringifies them.
1184+
std::unique_ptr<simplecpp::Token> open(new simplecpp::Token("(", start->location));
1185+
std::unique_ptr<simplecpp::Token> close(new simplecpp::Token(")", start->location));
1186+
// A second terminator would break an unbraced do/while or if/else body.
1187+
simplecpp::Token* terminator = end->next;
1188+
while (terminator && terminator->comment)
1189+
terminator = terminator->next;
1190+
if (terminator && terminator->op == ';')
1191+
tokenList.deleteToken(terminator);
1192+
start->setstr("asm");
1193+
end->setstr(";");
1194+
while (start->next != end)
1195+
tokenList.deleteToken(start->next);
1196+
open->previous = start;
1197+
open->next = close.get();
1198+
close->previous = open.get();
1199+
close->next = end;
1200+
start->next = open.release();
1201+
end->previous = close.release();
1202+
return end;
1203+
}
1204+
1205+
void Preprocessor::simplifyAsm()
1206+
{
1207+
simplifyAsm(mTokens);
11391208
}
11401209

1141-
void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList &tokenList)
1210+
void Preprocessor::simplifyAsm(simplecpp::TokenList &tokenList)
11421211
{
11431212
// assembler code..
11441213
for (simplecpp::Token *tok = tokenList.front(); tok; tok = tok->next) {
1214+
if (tok->str() == "__asm") {
1215+
if (simplecpp::Token* end = simplifySdccAsm(tokenList, tok))
1216+
tok = end;
1217+
continue;
1218+
}
11451219
if (tok->op != '#')
11461220
continue;
11471221
if (sameline(tok, tok->previousSkipComments()))

‎lib/preprocessor.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor {
144144
*/
145145
std::size_t calculateHash(const std::string &toolinfo) const;
146146

147-
void simplifyPragmaAsm();
147+
void simplifyAsm();
148148

149-
static void simplifyPragmaAsm(simplecpp::TokenList &tokenList);
149+
static void simplifyAsm(simplecpp::TokenList &tokenList);
150150

151151
static void getErrorMessages(ErrorLogger &errorLogger, const Settings &settings);
152152

‎test/cli/other_test.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,25 @@ def test_preprocessor_error(tmpdir):
9090
assert exitcode != 0
9191

9292

93+
@pytest.mark.parametrize('in_header', [False, True])
94+
def test_sdcc_asm_operands(tmp_path, in_header): # #6028
95+
source = ('int f(void) {\n'
96+
' __asm\n'
97+
' movx @dptr,a\n'
98+
' mov b,#(s_XINIT>>8)\n'
99+
' __endasm;\n'
100+
' return 1/0;\n'
101+
'}\n')
102+
main_file = tmp_path / 'main.c'
103+
asm_file = tmp_path / 'asm.h' if in_header else main_file
104+
asm_file.write_text(source)
105+
if in_header:
106+
main_file.write_text('#include "asm.h"\n')
107+
exitcode, _, stderr = cppcheck(['--error-exitcode=1', '--template={file}:{line}:{id}', str(main_file)])
108+
assert exitcode == 1
109+
assert stderr == '{}:6:zerodiv\n'.format(asm_file)
110+
111+
93112
__ANSI_BOLD = "\x1b[1m"
94113
__ANSI_FG_RED = "\x1b[31m"
95114
__ANSI_FG_DEFAULT = "\x1b[39m"

‎test/testpreprocessor.cpp‎

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class TestPreprocessor : public TestFixture {
136136
if (inlineSuppression)
137137
preprocessor.inlineSuppressions(*inlineSuppression);
138138
preprocessor.removeComments();
139-
preprocessor.simplifyPragmaAsm();
139+
preprocessor.simplifyAsm();
140140

141141
std::map<std::string, std::string> cfgcode;
142142
if (cfgs.empty()) {
@@ -255,6 +255,13 @@ class TestPreprocessor : public TestFixture {
255255
TEST_CASE(pragma);
256256
TEST_CASE(pragma_asm_1);
257257
TEST_CASE(pragma_asm_2);
258+
TEST_CASE(sdccAsmOperands);
259+
TEST_CASE(sdccAsmEmptyAndAdjacent);
260+
TEST_CASE(sdccAsmComments);
261+
TEST_CASE(sdccAsmIncomplete);
262+
TEST_CASE(sdccAsmOtherSyntax);
263+
TEST_CASE(sdccAsmConditional);
264+
TEST_CASE(sdccAsmControlFlow);
258265
TEST_CASE(endifsemicolon);
259266
TEST_CASE(missing_doublequote);
260267
TEST_CASE(handle_error);
@@ -1562,6 +1569,151 @@ class TestPreprocessor : public TestFixture {
15621569
ASSERT_EQUALS("asm ( )\n;\n\nbbb", actual.at(""));
15631570
}
15641571

1572+
void sdccAsmOperands() { // #6028
1573+
{
1574+
const char code[] = "void f() {\n"
1575+
"__asm\n"
1576+
" movx @dptr,a\n"
1577+
"__endasm;\n"
1578+
"}\n";
1579+
ASSERT_EQUALS("void f ( ) {\nasm ( )\n\n;\n}", getcode(settings0, *this, code).at(""));
1580+
}
1581+
{
1582+
const char code[] = "__asm\n"
1583+
" mov b,#(s_XINIT>>8)\n"
1584+
"__endasm";
1585+
ASSERT_EQUALS("asm ( )\n\n;", getcode(settings0, *this, code).at(""));
1586+
}
1587+
{
1588+
const char code[] = "int f(int x) {\n"
1589+
" ++x;\n"
1590+
" __asm\n"
1591+
" anl a,#0x0f\n"
1592+
" inc a\n"
1593+
" movc a,@a+pc\n"
1594+
" ret\n"
1595+
" __endasm;\n"
1596+
" return x;\n"
1597+
"}\n";
1598+
ASSERT_EQUALS("int f ( int x ) {\n++ x ;\nasm ( )\n\n\n\n\n;\nreturn x ;\n}",
1599+
getcode(settings0, *this, code).at(""));
1600+
}
1601+
ASSERT_EQUALS("", errout_str());
1602+
}
1603+
1604+
void sdccAsmEmptyAndAdjacent() {
1605+
ASSERT_EQUALS("asm ( )\n;", getcode(settings0, *this, "__asm\n__endasm").at(""));
1606+
const char code[] = "__asm\n"
1607+
"__endasm;\n"
1608+
"__asm\n"
1609+
" nop\n"
1610+
"__endasm;\n"
1611+
"int after;\n";
1612+
ASSERT_EQUALS("asm ( )\n;\nasm ( )\n\n;\nint after ;", getcode(settings0, *this, code).at(""));
1613+
ASSERT_EQUALS("", errout_str());
1614+
}
1615+
1616+
void sdccAsmComments() {
1617+
const char code[] = "/* __asm */\n"
1618+
"const char *s = \"__asm __endasm\";\n"
1619+
"__asm ; __endasm } #error ignored\n"
1620+
" ; __asm {\n"
1621+
" mov a,b ; __endasm\n"
1622+
" /* __endasm */ nop\n"
1623+
" mov a,__endasm\n"
1624+
"__endasm;\n"
1625+
"int after; // __asm\n";
1626+
ASSERT_EQUALS("\nconst char * s = \"__asm __endasm\" ;\nasm ( )\n\n\n\n\n;\nint after ;",
1627+
getcode(settings0, *this, code).at(""));
1628+
ASSERT_EQUALS("", errout_str());
1629+
}
1630+
1631+
void sdccAsmIncomplete() {
1632+
ASSERT_EQUALS("__asm\nnop", getcode(settings0, *this, "__asm\nnop").at(""));
1633+
ASSERT_EQUALS("__asm\nnop\n__asm\nnop", getcode(settings0, *this, "__asm\nnop\n__asm\nnop").at(""));
1634+
ASSERT_EQUALS("__asm\nnop\n__asm\nnop\n__endasm ;\nint after ;",
1635+
getcode(settings0, *this, "__asm\nnop\n__asm\nnop\n__endasm;\nint after;").at(""));
1636+
{
1637+
const char code[] = "void f() {\n"
1638+
"__asm\n"
1639+
" nop\n"
1640+
"}\n"
1641+
"int after;\n"
1642+
"__endasm;\n";
1643+
ASSERT_EQUALS("void f ( ) {\n__asm\nnop\n}\nint after ;\n__endasm ;",
1644+
getcode(settings0, *this, code).at(""));
1645+
}
1646+
{
1647+
const char code[] = "__asm\n"
1648+
" nop\n"
1649+
"#define N 1\n"
1650+
"__endasm;\n"
1651+
"int after;\n";
1652+
ASSERT_EQUALS("__asm\nnop\n\n__endasm ;\nint after ;", getcode(settings0, *this, code).at(""));
1653+
}
1654+
ASSERT_EQUALS("", errout_str());
1655+
}
1656+
1657+
void sdccAsmOtherSyntax() {
1658+
const char code[] = "void f() {\n"
1659+
" __asm (\"nop\");\n"
1660+
" __asm\n"
1661+
" (\"nop\");\n"
1662+
" __asm volatile (\"nop\");\n"
1663+
" __asm goto (\"\" : : : : label);\n"
1664+
"label: ;\n"
1665+
" __asm { nop }\n"
1666+
"}\n"
1667+
"__asm void g(void) {\n"
1668+
" nop\n"
1669+
"}\n";
1670+
ASSERT_EQUALS("void f ( ) {\n__asm ( \"nop\" ) ;\n__asm\n( \"nop\" ) ;\n"
1671+
"__asm volatile ( \"nop\" ) ;\n__asm goto ( \"\" : : : : label ) ;\n"
1672+
"label : ;\n__asm { nop }\n}\n__asm void g ( void ) {\nnop\n}",
1673+
getcode(settings0, *this, code).at(""));
1674+
ASSERT_EQUALS("\nvoid f ( ) { $__asm ( \"nop\" ) ; }",
1675+
getcode(settings0, *this, "#define ASM __asm\nvoid f() { ASM(\"nop\"); }").at(""));
1676+
ASSERT_EQUALS("", errout_str());
1677+
}
1678+
1679+
void sdccAsmConditional() {
1680+
const char code[] = "void f() {\n"
1681+
"#ifdef USE_ASM\n"
1682+
" __asm\n"
1683+
" mov b,#(s_XINIT>>8)\n"
1684+
" movx @dptr,a\n"
1685+
" __endasm;\n"
1686+
"#else\n"
1687+
" fallback();\n"
1688+
"#endif\n"
1689+
" after();\n"
1690+
"}\n";
1691+
ASSERT_EQUALS("void f ( ) {\n\nasm ( )\n\n\n;\n\n\n\nafter ( ) ;\n}",
1692+
getcodeforcfg(settings0, *this, code, "USE_ASM", "file.c"));
1693+
ASSERT_EQUALS("void f ( ) {\n\n\n\n\n\n\nfallback ( ) ;\n\nafter ( ) ;\n}",
1694+
getcodeforcfg(settings0, *this, code, "", "file.c"));
1695+
ASSERT_EQUALS("", errout_str());
1696+
}
1697+
1698+
void sdccAsmControlFlow() {
1699+
const char code[] = "void f(int x) {\n"
1700+
" if (x)\n"
1701+
" __asm\n"
1702+
" nop\n"
1703+
" __endasm;\n"
1704+
" else\n"
1705+
" fallback();\n"
1706+
" do\n"
1707+
" __asm\n"
1708+
" nop\n"
1709+
" __endasm /* comment */;\n"
1710+
" while (x);\n"
1711+
"}\n";
1712+
ASSERT_EQUALS("void f ( int x ) {\nif ( x )\nasm ( )\n\n;\nelse\nfallback ( ) ;\n"
1713+
"do\nasm ( )\n\n;\nwhile ( x ) ;\n}", getcode(settings0, *this, code).at(""));
1714+
ASSERT_EQUALS("", errout_str());
1715+
}
1716+
15651717
void endifsemicolon() {
15661718
const char filedata[] = "void f() {\n"
15671719
"#ifdef A\n"

0 commit comments

Comments
 (0)