1#! /bin/sh
2# SPDX-License-Identifier: GPL-2.0
3#
4# (c) 2019, Google
5progname="${0##*/}"
6
7# Change to EXPORT_SYMBOL, EXPORT_SYMBOL_GPL or other namespace variant default
8EXPORT_SYMBOL=${EXPORT_SYMBOL_GPL:-${progname#add_}}
9
10USAGE="USAGE: ${progname} [--no-skip-arch] < kernel_build_error_log
11       ${progname} [--no-skip-arch] kernel_build_error_log
12       grep /<module>[.]ko build_error_log | ${progname} [--no-skip-arch]
13       ${EDITOR:-vi} \`${progname} [--no-skip-arch] < kernel_build_error_log\`
14
15To acquire the kernel_build_error_log eg:
16\$ ./build_sm8250.sh -j50 2>&1 | tee kernel_build_error_log
17
18To only create commit related to symbols needed for cam_spec.ko module:
19\$ grep /cam_spec[.]ko kernel_build_error_log | ${progname}
20
21The script will only affect the current directory level and downward,
22this allows one to segregate the adjusted content.  Any symbols that
23are needed outside the range of that directory will result in errors
24and the git commit phase will not be performed.
25
26Add ${EXPORT_SYMBOL} for any noted missing symbols, output the list of files
27modified to stdout (so it can be passed to an editor command line should you
28need to check or adjust the results). Automatically commit the list of files
29into git.
30
31Deals as simply as it can to handle __trace_<symbols>, sorting the result.
32
33Keep in mind exports can change, be added or subtracted, and that preliminary
34work may expose or remove required symbols to resolve during later work.  As
35such this script only adds, so you may need to revert the results and try
36again to get the most up to date set.  By making this part automated it can
37deal with the tens or thousands of exports that need to be discovered or
38added.  If you need to adjust a subsystem, run this script in the subsystem
39directory, and it will only adjust from that point downwards leaving other
40higher up trees alone."
41
42if [ X"--help" = X"${1}" -o X"{-h}" = X"${1}" ]; then
43  echo "${USAGE}" >&2
44  exit
45fi
46skip_arch=true
47if [ X"--no-skip-arch" = X"${1}" ]; then
48  skip_arch=false
49  shift
50fi
51INPUT=
52if [ 1 = ${#} ]; then
53  INPUT=${1}
54  shift
55fi
56if [ 0 != ${#} ]; then
57  echo "Unexpected Argument: ${*}" >&2
58  echo >&2
59  echo "${USAGE}" >&2
60  exit 1
61fi
62
63# A _real_ embedded tab character
64TAB="`echo | tr '\n' '\t'`"
65# A _real_ embedded escape character
66ESCAPE="`echo | tr '\n' '\033'`"
67# Colours
68RED="${ESCAPE}[38;5;196m"
69BLUE="${ESCAPE}[35m"
70NORMAL="${ESCAPE}[0m"
71
72# Common grep/sed regex expressions
73STRUCT_TYPE="struct[ ${TAB}][^ ${TAB}]\{1,\}"
74VAR_TYPE="\(${STRUCT_TYPE}\|bool\|char\|int\|long\|long long\|u\{0,1\}int[0-9]*t\)[ ${TAB}]\{1,\}[*]\{0,1\}"
75STRUCT_TYPE="${STRUCT_TYPE}[ ${TAB}]\{1,\}[*]\{0,1\}"
76
77# Files that exports should never exist, or need to be carefully considered
78skip_arch_files() {
79  if ${skip_arch}; then
80    grep -v '\(^\|^[^:]*/\)arch/[^a][^r][^m][^:]*:'
81  else
82    cat -
83  fi
84}
85
86# Following sucks in all the data from stdin or ${INPUT}
87#  Check for depmod output (undefined!) or built-in kernel complaints (undefined
88#  references).  The later is not advised because it means the kernel may be
89#  dependent on a module, we collect it to provide a signal for future work.
90echo "Reading symbols" >&2
91MODULES="`
92  sed -n \
93      -e 's/^ERROR: "\([^"]*\)" [[]\(.*[.]ko\)[]] undefined!$/\1 \2/p' \
94      -e 's/^[^:]*:[0-9]*: undefined reference to \`\([^'\'']*\)'\''$/\1/p' \
95      ${INPUT} |
96    sort -u`"
97SYMBOLS="`echo \"${MODULES}\" | sed 's/ .*$//' | sort -u`"
98if [ -z "${SYMBOLS}" ]; then
99  echo "${BLUE}WARNING${NORMAL}: no symbols found" >&2
100  exit
101fi
102
103SYMBOLS_PLUS_TRACE="${SYMBOLS}"
104TRACE_MODULES="`echo \"${MODULES}\" |
105                  sed -n 's/__tracepoint_.* //p' |
106                  sort -u`"
107TRACE_MODULES_2=
108c=`echo "${TRACE_MODULES}" | wc -l`
109if [ 1 -eq ${c} ];then
110  TRACE_MODULES="${TRACE_MODULES%/*.ko}"
111  if [ -z "${INPUT}" ]; then
112    TRACE_MODULES_2=*
113  else
114    TRACE_MODULES_2=${INPUT}
115  fi
116elif [ -z "${INPUT}" ]; then
117  TRACE_MODULES=*
118else
119  TRACE_MODULES=${INPUT}
120fi
121TRACE_FILE="`grep -rl '^#define CREATE_TRACE_POINTS' ${TRACE_MODULES}`"
122if [ -z "${TRACE_FILE}" -a -n "${TRACE_MODULES_2}" ]; then
123  TRACE_FILE="`grep -rl '^#define CREATE_TRACE_POINTS' ${TRACE_MODULES_2}`"
124fi
125c=0
126if [ -n "${TRACE_FILE}" ]; then
127  c=`echo "${TRACE_FILE}" | wc -l`
128fi
129if [ 1 -eq ${c} ]; then
130  SYMBOLS_PLUS_TRACE="${SYMBOLS}
131`echo \"${SYMBOLS}\" |
132   sed -n 's/^__tracepoint_\(.*\)/EXPORT_TRACEPOINT_SYMBOL_GPL(\1);/p'`"
133else
134  TRACE_FILE=
135fi
136
137echo "Finding symbols in the current tree" >&2
138DATA="`grep -Fr \"${SYMBOLS_PLUS_TRACE}\" * |
139         grep \"^[^:]*[.][cS]:[^ ${TAB}]\" |
140         skip_arch_files`"
141echo "Editing the files that contain the symbols" >&2
142TMP=`mktemp -d`
143
144[ 'USAGE: report_strip_of_tag tag
145
146Report if a problematic function segment tag is stripped from the file
147
148expects ${F}, ${TMP}, ${TAB}, ${RED} and ${NORMAL} to be defined' ]
149report_strip_of_tag() {
150  if diff --side-by-side --suppress-common-lines ${F} ${TMP}/${F##*/} |
151    sed -n "s/\(.*[ ${TAB}]\)\(__${1}\)\([^a-zA-Z0-9_].*[^ ${TAB}]\)[ ${TAB}]\{1,\}|[ ${TAB}]\{1,\}/\1${BLUE}\2${NORMAL}\3 -> /p" |
152       grep __${1} >&2; then
153    echo "${RED}ERROR${NORMAL}: scrubbing __${1} from function in ${F}" >&2
154    echo "       Compile check, possibly edit ${F} and friends." >&2
155    echo "       Then re-run ${progname} to complete." >&2
156    echo FAILED
157  fi
158}
159
160for s in ${SYMBOLS}; do
161  # Already there?
162  if echo "${DATA}" |
163       grep "EXPORT_\(TRACEPOINT_SYMBOL_GPL*(${s#__tracepoint_}\|SYMBOL\(_GPL\)*(${s}\))" >/dev/null; then
164    echo INFO: ${s} found and already exported >&2
165    continue
166  fi
167  m="`echo \"${DATA}\" | grep \"^[^:]*:\([^ ${TAB}].*[ *]\|\)${s}(\"`"
168  c=0
169  if [ -n "${m}" ]; then
170    c=`echo "${m}" | wc -l`
171  fi
172  if [ 0 -eq ${c} ]; then
173    m="`echo \"${DATA}\" | grep \"^[^:]*:\(const[ ${TAB}]\{1,\}\|\)${STRUCT_TYPE}${s}\([[][]]\|\) = {\"`"
174    c=0
175    if [ -n "${m}" ]; then
176      c=`echo "${m}" | wc -l`
177    fi
178    if [ 0 -eq ${c} ]; then
179      m="`echo \"${DATA}\" | grep \"^[^:]*:\(const[ ${TAB}]\{1,\}\|\)${VAR_TYPE}${s}[\[ ${TAB};=]\"`"
180      c=0
181      if [ -n "${m}" ]; then
182        c=`echo "${m}" | wc -l`
183      fi
184      if [ 0 -eq ${c} ]; then
185        if [ -z "${TRACE_FILE}" -o "${s}" = "${s#__tracepoint_}" ]; then
186          echo "${RED}ERROR${NORMAL}: ${s} not found" >&2
187          if [ "${s}" != "${s#__tracepoint_}" -o "${s}" = "${s#__}" ]; then
188            echo FAILED
189          else
190            echo "${RED}WARNING${NORMAL}: ${s} might be a linker variable? skipping marking as failure" >&2
191          fi
192        else
193          echo ${TRACE_FILE} ${s}
194        fi
195      elif [ 1 -eq ${c} ]; then
196        echo "${DATA}" |
197          sed -n "s/^\([^:]*\):\(const[ $TAB]\{1,\}\|\)${VAR_TYPE}${s}.*/\1 ${s}/p"
198      else
199        echo "${RED}ERROR${NORMAL}: ${c} matches: `echo \"${m}\" |
200                                                   tr '\n' '&' |
201                                                   sed -e 's/&$//' \
202                                                       -e 's/&/ & /g'`" >&2
203        echo FAILED
204      fi
205    elif [ 1 -eq ${c} ]; then
206      echo "${DATA}" |
207        sed -n "s/^\([^:]*\):\(const[ $TAB]\{1,\}\|\)${STRUCT_TYPE}\(${s}\)\([[][]]\|\) = {.*/\1 \3/p"
208    else
209      echo "${RED}ERROR${NORMAL}: ${c} matches: `echo \"${m}\" |
210                                                 tr '\n' '&' |
211                                                 sed -e 's/&$//' \
212                                                     -e 's/&/ & /g'`" >&2
213      echo FAILED
214    fi
215  elif [ 1 -eq ${c} ]; then
216    echo "${DATA}" |
217      sed -n "s/^\([^:]*\):\([^ ${TAB}].*[ *]\|\)\(${s}\)(.*/\1 \3/p"
218  else
219    echo "${RED}ERROR${NORMAL}: ${c} matches: `echo \"${m}\" |
220                                               tr '\n' '&' |
221                                               sed -e 's/&$//' \
222                                                   -e 's/&/ & /g'`" >&2
223    echo FAILED
224  fi
225done |
226  sort -u |
227  while read F s; do
228    if [ "FAILED" = "${F}" ]; then
229      echo FAILED
230      continue
231    fi
232    if [ "${s}" != "${s#__tracepoint_}" ]; then
233      sed "\$ {
234             /^[^E]/ {
235               a \
236
237             }
238             a \
239               newEXPORT_TRACEPOINT_SYMBOL_GPL(${s#__tracepoint_});
240           }" ${F}
241    else
242      cat ${F}
243    fi |
244    sed "/^\([^ ${TAB}].*[ *]\|\)${s}(/ {
245           : loop1
246           N
247           /\(\n}\);*$/ {
248             s//\1/
249             s/^\([^\n]*[^\n ${TAB}]\|\)[ ${TAB}]\{1,\}__\(init\|exit\)[ ${TAB}]\{1,\}\([^\n]*(\)/\1 \3/
250             s/^\([^\n]*[^\n ${TAB}]\|\)[ ${TAB}]\{1,\}__\(init\|exit\)\([^ ${TAB}a-zA-Z0-9_][^\n]*(\)/\1 \3/
251             s/^__\(init\|exit\)[ ${TAB}]\{1,\}\([^\n]*(\)/\2/
252             s/^__\(init\|exit\)\([^ ${TAB}a-zA-Z0-9_][^\n]*(\)/\2/
253             a \
254               new${EXPORT_SYMBOL}(${s});
255             b next1
256           }
257           b loop1
258           : next1
259         }
260         /^\(const[ ${TAB}]\{1,\}\|\)${STRUCT_TYPE}${s}\([[][]]\|\)[ ${TAB}]*=[ ${TAB}]*{/ {
261           : loop2
262           N
263           /\n};$/ {
264             a \
265               new${EXPORT_SYMBOL}(${s});
266             b next2
267           }
268           b loop2
269           : next2
270         }
271         /^\(const[ ${TAB}]\{1,\}\|\)${VAR_TYPE}${s}\(\([[][]]\|\)[ ${TAB}]*=.*\|\);/ {
272           a \
273             new${EXPORT_SYMBOL}(${s});
274         }
275         /^\(const[ ${TAB}]\{1,\}\|\)${VAR_TYPE}${s}\([[][]]\|\)[ ${TAB}]*=[ ${TAB}]*{\{0,1\}\$/ {
276           : loop3
277           N
278           /;$/ {
279             a \
280               new${EXPORT_SYMBOL}(${s});
281             b next3
282           }
283           b loop3
284           : next3
285         }" |
286      sed '/^newEXPORT/ {
287             s//EXPORT/
288             N
289             : loop
290             N
291             s/\(\n\)\n$/\1/
292             t loop
293           }' >${TMP}/${F##*/} &&
294      ! cmp -s ${F} ${TMP}/${F##*/} &&
295      [ -s ${TMP}/${F##*/} ] &&
296      report_strip_of_tag init &&
297      report_strip_of_tag exit &&
298      cp ${TMP}/${F##*/} ${F} &&
299      echo INFO: export ${s} in ${F} >&2 ||
300      (
301        echo "${RED}ERROR${NORMAL}: export ${s} in ${F}" >&2
302        echo FAILED
303      )
304    echo ${F}
305    rm ${TMP}/${F##*/}
306  done >${TMP}/${progname}.out
307FILES="`grep -v '^FAILED$' ${TMP}/${progname}.out | sort -u`"
308echo "${FILES}"
309grep '^FAILED$' ${TMP}/${progname}.out >/dev/null
310ret=${?}
311rm -rf ${TMP}
312if [ 0 -ne ${ret} ]; then
313  echo "DONE" >&2
314  GIT_FILES="`git diff |
315                sed -n 's@^diff --git a/\(.*\) b/\1$@\1@p' |
316                sort -u`"
317  if [ -z "${GIT_FILES}" ]; then
318    echo "${BLUE}WARNING${NORMAL}: no changes to commit" >&2
319    if [ -n "${FILES}" ]; then
320      echo "         but we altered files!" >&2
321    fi
322    exit
323  fi
324  SEMI=""
325  if [ X"${GIT_FILES}" != X"`echo ${FILES} | tr ' ' '\n' | sort -u`" ]; then
326    SEMI="Semi-"
327    FILES=`echo ${FILES} ${GIT_FILES} |
328             tr ' ' '\n' |
329             sort -u`
330    echo "${BLUE}WARNING${NORMAL}: list of files in git different from files we expected to edit, continuation?" >&2
331  fi
332
333  AUTHOR_NAME="`git config user.name`"
334  AUTHOR_EMAIL="`git config user.email`"
335  if [ -z "${AUTHOR_EMAIL}" ]; then
336    AUTHOR_EMAIL="`${USER}@google.com`"
337  fi
338  if [ -z "${AUTHOR_NAME}" ]; then
339    convert_ldap_to_name() {
340      echo "${1##*/}" |
341        sed "s@[-_]@ @g
342             s@   *@ @g" |
343        sed 's@\(^\| \)a@\1A@g
344             s@\(^\| \)b@\1B@g
345             s@\(^\| \)c@\1C@g
346             s@\(^\| \)d@\1D@g
347             s@\(^\| \)e@\1E@g
348             s@\(^\| \)f@\1F@g
349             s@\(^\| \)g@\1G@g
350             s@\(^\| \)h@\1H@g
351             s@\(^\| \)i@\1I@g
352             s@\(^\| \)j@\1J@g
353             s@\(^\| \)k@\1K@g
354             s@\(^\| \)l@\1L@g
355             s@\(^\| \)m@\1M@g
356             s@\(^\| \)n@\1N@g
357             s@\(^\| \)o@\1O@g
358             s@\(^\| \)p@\1P@g
359             s@\(^\| \)q@\1Q@g
360             s@\(^\| \)r@\1R@g
361             s@\(^\| \)s@\1S@g
362             s@\(^\| \)t@\1T@g
363             s@\(^\| \)u@\1U@g
364             s@\(^\| \|Mc\)v@\1V@g
365             s@\(^\| \)w@\1W@g
366             s@\(^\| \)x@\1X@g
367             s@\(^\| \)y@\1Y@g
368             s@\(^\| \)z@\1Z@g'
369    }
370    AUTHOR_NAME="`convert_ldap_to_name ${USER}`"
371  fi
372
373  git commit ${FILES} -m "GKI: add missing exports for <config>=m for <compatible>
374
375<edit>
376
377${SEMI}Automatically generated by ${0##*/}
378
379The following symbols are exported:
380`echo \"${SYMBOLS}\" |
381   sed 's/^/ - /'`
382
383Signed-off-by: ${AUTHOR_NAME} <${AUTHOR_EMAIL}>
384Test: compile" ||
385    echo "${RED}ERROR${NORMAL}: failed to commit changes to git" >&2
386  exit
387fi
388echo "${RED}FAILED${NORMAL}: could not complete task" >&2
389exit 1
390