Skip to content

Commit bfc3b0f

Browse files
wtarreaupaulmckrcu
authored andcommitted
tools/nolibc: Fix missing strlen() definition and infinite loop with gcc-12
When built at -Os, gcc-12 recognizes an strlen() pattern in nolibc_strlen() and replaces it with a jump to strlen(), which is not defined as a symbol and breaks compilation. Worse, when the function is called strlen(), the function is simply replaced with a jump to itself, hence becomes an infinite loop. One way to avoid this is to always set -ffreestanding, but the calling code doesn't know this and there's no way (either via attributes or pragmas) to globally enable it from include files, effectively leaving a painful situation for the caller. Alexey suggested to place an empty asm() statement inside the loop to stop gcc from recognizing a well-known pattern, which happens to work pretty fine. At least it allows us to make sure our local definition is not replaced with a self jump. The function only needs to be renamed back to strlen() so that the symbol exists, which implies that nolibc_strlen() which is used on variable strings has to be declared as a macro that points back to it before the strlen() macro is redifined. It was verified to produce valid code with gcc 3.4 to 12.1 at different optimization levels, and both with constant and variable strings. In case this problem surfaces again in the future, an alternate approach consisting in adding an optimize("no-tree-loop-distribute-patterns") function attribute for gcc>=12 worked as well but is less pretty. Reported-by: kernel test robot <[email protected]> Link: https://lore.kernel.org/r/[email protected] Fixes: 66b6f75 ("rcutorture: Import a copy of nolibc") Fixes: 96980b8 ("tools/nolibc/string: do not use __builtin_strlen() at -O0") Cc: "Paul E. McKenney" <[email protected]> Cc: Alexey Dobriyan <[email protected]> Signed-off-by: Willy Tarreau <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
1 parent 9abf231 commit bfc3b0f

File tree

1 file changed

+8
-5
lines changed

1 file changed

+8
-5
lines changed

tools/include/nolibc/string.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,28 +125,31 @@ char *strcpy(char *dst, const char *src)
125125
}
126126

127127
/* this function is only used with arguments that are not constants or when
128-
* it's not known because optimizations are disabled.
128+
* it's not known because optimizations are disabled. Note that gcc 12
129+
* recognizes an strlen() pattern and replaces it with a jump to strlen(),
130+
* thus itself, hence the asm() statement below that's meant to disable this
131+
* confusing practice.
129132
*/
130133
static __attribute__((unused))
131-
size_t nolibc_strlen(const char *str)
134+
size_t strlen(const char *str)
132135
{
133136
size_t len;
134137

135-
for (len = 0; str[len]; len++);
138+
for (len = 0; str[len]; len++)
139+
asm("");
136140
return len;
137141
}
138142

139143
/* do not trust __builtin_constant_p() at -O0, as clang will emit a test and
140144
* the two branches, then will rely on an external definition of strlen().
141145
*/
142146
#if defined(__OPTIMIZE__)
147+
#define nolibc_strlen(x) strlen(x)
143148
#define strlen(str) ({ \
144149
__builtin_constant_p((str)) ? \
145150
__builtin_strlen((str)) : \
146151
nolibc_strlen((str)); \
147152
})
148-
#else
149-
#define strlen(str) nolibc_strlen((str))
150153
#endif
151154

152155
static __attribute__((unused))

0 commit comments

Comments
 (0)