1#!/bin/bash
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2021 Intel Corporation. All rights reserved.
4
5# This builds SOF twice with some environment variations and then
6# compares the binary outputs.
7
8# Big caveats:
9#
10# - Only -g0 is supported, no BUILD_PATH_PREFIX_MAP
11# - rimage uses the current date (day month year) in the
12#   signature.
13# - Starting from TGL, signatures also include a random salt.
14#
15# rimage makes sof.ri non-deterministic even when sof.elf is
16# deterministic. See https://github.com/thesofproject/rimage/issues/41
17# and below for more details and future directions.
18
19# The following, basic environment variations are currently tested:
20#
21#   - timestamps (obviously)
22#   - source directory
23#   - disorderfs
24
25# For a list of other, non-tested environment variations check the
26# "Achieve deterministic builds" list found at
27# https://reproducible-builds.org/docs/
28
29
30set -e
31
32
33SOF_TOP=$(cd "$(dirname "$0")"/.. && /bin/pwd)
34SOF_PARENT=$(cd "$SOF_TOP"/.. && /bin/pwd)
35
36
37SOF2="$SOF_PARENT"/sof-bind-mount-DO-NOT-DELETE
38# To avoid ripple effects and reduce the number of differences
39# considerably, replace "sof" with something of the same length:
40# SOF2="$SOF_PARENT"/sog
41
42PLATFS=(tgl)
43
44# diffoscope is great but it has hundreds of dependencies, too long to
45# install for CI so we don't use it here. This is just an alias
46# suggestion for local use. Requires less -R.
47diffo ()
48{
49    # Omitting $2 ("and") allows copy/paste of the output of diff -r
50    diffoscope --exclude-directory-metadata=recursive \
51               --text-color=always "$1" "$3"
52}
53
54hexdiff ()
55{
56    # cut -b10 removes the offset
57    diff --color=auto -U20 \
58         <(hexdump -vC "$1" | cut -b10- ) \
59         <(hexdump -vC "$2" | cut -b10- ) | less
60}
61
62init()
63{
64    # -E(xtended) regex
65    local excluded_endings=(
66        '\.log' /deps
67        '\.c?make' '\.ninja' /TargetDirectories.txt
68        /CMakeCache.txt /progress.marks /CMakeRulesHashes.txt /Makefile
69        /CMakeRuleHashes.txt /Makefile2
70        '/depend\.internal' /link.txt '\.includecache'
71        '/.ninja_deps' '/.ninja_log'
72        '.*\.c\.o\.d'
73        '/rimage_ep.*'
74        '/smex_ep.*'
75    )
76
77    # The signing process is not deterministic.
78    # Keep 'reproducible.ri' INCLUDED!
79    excluded_endings+=('/sof-[a-z-]{,6}\.rix?')
80    excluded_endings+=('/sof\.ri' '/sof-no-xman\.ri' )
81
82    GREP_EXCLUDE_ARGS=()
83    for fpath in "${excluded_endings[@]}"; do
84        GREP_EXCLUDE_ARGS+=('-e' "$fpath differ\$")
85    done
86
87    # For debug
88    # declare -p GREP_EXCLUDE_ARGS # exit
89}
90
91
92trap _unmount EXIT
93
94_unmount()
95{
96    set +e
97
98    if mount | grep -q "${SOF2}.*fuse"; then
99        umount "$SOF2"
100        rmdir "$SOF2"
101    fi
102}
103
104
105diff_some_files()
106{
107    local p f
108    ( set +e
109
110      for p in "${PLATFS[@]}"; do
111
112          # Compare some ELF section headers. Look at the smallest .c.o
113          # first
114          for f in CMakeFiles/sof.dir/src/arch/xtensa/init.c.o  sof; do
115
116              diff --report-identical-files  \
117                   {b0,b1}/build_"$p"_?cc/"$f" ||
118                  diff -u --ignore-matching-lines='file format ' \
119                       <(objdump -h b0/build_"$p"_?cc/"$f") \
120                       <(objdump -h b1/build_"$p"_?cc/"$f")
121
122          done
123
124          for f in generated/include/sof_versions.h sof.ri \
125                  src/arch/xtensa/reproducible.ri; do
126              diff -u --report-identical-files  \
127                   {b0,b1}/build_"$p"_?cc/"$f"
128              # report unsupported reproducible.ri
129              test -s b0/build_"$p"_?cc/"$f" ||
130                  printf '\t%s is EMPTY\n' b0/build_"$p"_?cc/"$f"
131          done
132
133      done
134      true
135    ) | sed -e 's/differ/DIFFER/g'
136}
137
138main()
139{
140    init
141
142    test -e "$SOF2"/CMakeLists.txt || {
143        mkdir -p "$SOF2"
144        # This will fail in Docker; Docker should provide this bind
145        # mount for us.
146        disorderfs "${SOF_TOP}" "$SOF2"
147    }
148
149    # Chances of name collisions with the user should be pretty small
150    rm -rf   reprobld/b0/ reprobld/b1/
151    mkdir -p reprobld/b0/ reprobld/b1/
152
153    local oldTZ="$TZ" # typically undefined, coming from some /etc file
154                      # instead.
155
156    export EXTRA_CFLAGS='-g0'
157
158    # Going once
159
160    export TZ='Pacific/Midway' # -11:00
161    "${SOF_TOP}"/scripts/xtensa-build-all.sh     "${PLATFS[@]}"
162    mv build_*_?cc reprobld/b0/
163
164
165    # Going twice
166
167    # Use this to "break" rimage that makes the date (in local time!
168    # BIOS fans?) part of the CSS signature. See caveats above.
169    # +14:00 - -11:00 = +25:00 = always on a different date
170    export TZ='Pacific/Kiritimati' # +14:00
171
172    "$SOF2"/scripts/xtensa-build-all.sh "${PLATFS[@]}"
173    mv build_*_?cc reprobld/b1/
174
175
176    # Restore TZ just in case we want to use dates later.
177    TZ="$oldTZ"
178
179
180    cd reprobld
181    diff -qr b0 b1/ |
182        grep -E -v "${GREP_EXCLUDE_ARGS[@]}" || {
183        printf \
184          "\n\n ---- PASS: no unexpected difference found between %s/b0/ and b1/ --- \n\n" "$(pwd)"
185        exit 0
186    }
187
188    printf "\n\n ---- FAIL: differences found between %s/b0/ and b1/ --- \n\n" "$(pwd)"
189    diff_some_files
190    printf "\n\n ---- FAIL: differences found between %s/b0/ and b1/ --- \n\n" "$(pwd)"
191}
192
193main "$@"
194