| #!/bin/bash |
| |
| # require sparse to build to prevent bugs |
| which sparse > /dev/null 2>&1 |
| if [ "$?" != 0 ]; then |
| echo "Must install sparse to build" |
| exit 1 |
| fi |
| |
| # |
| # one of the problems with using sparse in userspace is that it picks up |
| # things in system headers that we don't care about. We're willing to |
| # take on the burden of filtering them out so that we can have it tell |
| # us about problems in our code. |
| # |
| # system headers using __transparent_union__ |
| RE="^/.*error: ignoring attribute __transparent_union__" |
| |
| # we don't care if system headers have gcc attributes sparse doesn't |
| # know about |
| RE="$RE|error: attribute '__leaf__': unknown attribute" |
| |
| # yes, sparse, that's the size of memseting a 4 meg buffer all right |
| RE="$RE|warning: memset with byte count of 4194304" |
| |
| # some sparse versions don't know about some builtins |
| RE="$RE|error: undefined identifier '__builtin_fpclassify'" |
| |
| # I didn't really dig in, but io_uring and glibc are fighting |
| RE="$RE|note: .*through.*uring" |
| RE="$RE|RWF.* redefined" |
| RE="$RE|uio-ext.h.*original definition" |
| |
| # |
| # don't filter out 'too many errors' here, it can signify that |
| # sparse doesn't understand something and is throwing a *ton* |
| # of useless errors before giving up and existing. Check |
| # unfiltered sparse output. |
| # |
| |
| # |
| # I'm not sure this is needed. |
| # |
| search=$(gcc -print-search-dirs | awk '($1 == "install:"){print "-I" $2}') |
| |
| # |
| # We're trying to use sparse against glibc headers which go wild trying to |
| # use internal compiler macros to test features. We copy gcc's and give |
| # them to sparse. But not __SIZE_TYPE__ 'cause sparse defines that one. |
| # |
| defines=".sparse.gcc-defines.h" |
| gcc -dM -E -x c - < /dev/null | grep -v __SIZE_TYPE__ > $defines |
| include="-include $defines" |
| |
| # |
| # I have no idea why, but sometime sparse gets angry at the |
| # __PRETTY_FUNCTION__ generated by glibc's bonkers assert() macro. It |
| # isn't consistent, hitting (for example) 2 of 11 identical call sites |
| # in a file. As in, extract the statements, normalize the line numbers, |
| # and the text is identical. It only hits the function family |
| # (__PRETTY_FUNCTION__, __FUNCTION__, __func__). Built-in string |
| # identifiers like __FILE__ or __DATE__ are fine. |
| # |
| # We don't need meaningful strings here, so just use one that works. |
| # |
| echo "#define __PRETTY_FUNCTION__ __FILE__ " >> $defines |
| |
| # |
| # As gcc's default standard support has increased, sparse can be left |
| # behind. As of this writing, there are a handful of headers that we |
| # use that used to provide defines for functionality that is now |
| # natively supported by gcc. In each of these cases you can look at the |
| # gcc header and find "__STDC_VERSION__ <=> 201710L" tests. This tests |
| # the version of the standard that gcc is building against and restores |
| # the defines for sparse in the cases where the headers were emitting |
| # code for gcc that sparse doesn't understand. |
| # |
| . <(awk ' |
| ($2 == "__STDC_VERSION__") { |
| print $2 "=" substr($3, 1, length($3) - 1) |
| }' < .sparse.gcc-defines.h ) |
| if [ $__STDC_VERSION__ -gt 201710 ]; then |
| cat >> $defines <<EOF |
| // stdbool.h |
| #define bool _Bool |
| #define true 1 |
| #define false 0 |
| // assert.h |
| #define static_assert _Static_assert |
| // stdarg.h |
| #define __builtin_c23_va_start __builtin_va_start |
| EOF |
| fi |
| |
| # |
| # sparse doesn't seem to notice when it's on a 64bit host. It warns that |
| # 64bit values don't fit in 'unsigned long' without this. |
| # |
| if grep -q "__LP64__ 1" $defines; then |
| m64="-m64" |
| else |
| m64="" |
| fi |
| |
| sparse $m64 $include $search/include "$@" 2>&1 | grep -E -v "($RE)" | tee .sparse.output |
| if [ -s .sparse.output ]; then |
| exit 1 |
| else |
| exit 0 |
| fi |