Repository: pyinstaller/pyinstaller

Issue:
RHEL8 FIPS Fatal Selftest Failure
## Description of the issue
When building on and for a FIPS enabled RHEL8 machine, running the executables built by PyInstaller will crash immediately with the following error:
```
crypto/fips/fips.c:154: OpenSSL internal error: FATAL FIPS SELFTEST FAILURE
Aborted (core dumped)
```
The issue appears to be related to OpenSSL shared libraries, specifically libs that have associated hidden HMAC files like libcrypto and libssl. See the workaround section at the bottom for additional information.

### Context information (for bug reports)
* Output of `pyinstaller --version`: 6.3.0
* Version of Python: 3.11.5-1.el8_9
* Version of OpenSSL: OpenSSL 1.1.1k  FIPS
* Platform: Red Hat Enterprise Linux 8.9 (FIPS enabled)
* How you installed Python: Installed via yum package manager from the @rhel-8-for-x86_64-appstream-rpms repo.
* Did you also try this on another platform? Yes. When building on a FIPS enabled RHEL7 machine (Python version 3.8.13 installed from source, pyinstaller version 5.7.0, OpenSSL 1.0.2k-fips) for a FIPS enabled RHEL7 machine this issue does not occur. However, those builds will crash with similar or related errors if attempting to run them on a FIPS enabled RHEL8 machine.
I first encountered this issue over half a year ago, but I cannot definitively recall every version I've tested trying to workaround the issue. I believe the issue exists at least as far back as pyinstaller v5.4.x and python v3.8.x.

The issue occurs regardless of the build mode (default or with `--onefile`).

### A minimal example program which shows the error
```
import hashlib

if __name__ == '__main__':
    print("simple test")
```
Building this script with pyinstaller (E.g.: `pyinstaller simple.py --name simple` or `pyinstaller simple.py --name simple --onefile`) and trying to run the resulting executable (E.g.: `dist/simple/simple`) will result in the crash as described at the top of the issue. However, simply running the script via Python (E.g.: `python3.11 simple.py`) will not cause any problems.

### Stacktrace / full error message
```
[redacted]$ gdb dist/simple/simple
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-20.el8
Reading symbols from dist/simple/simple...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/redacted/buildtest/simple/dist/simple/simple
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
process 2664829 is executing new program: /home/redacted/buildtest/simple/dist/simple/simple
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
crypto/fips/fips.c:154: OpenSSL internal error: FATAL FIPS SELFTEST FAILURE

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50        return ret;
(gdb) backtrace
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff73eeea5 in __GI_abort () at abort.c:79
#2  0x00007ffff582cb40 in OPENSSL_die (message=message@entry=0x7ffff5993139 "FATAL FIPS SELFTEST FAILURE",
    file=file@entry=0x7ffff5993126 "crypto/fips/fips.c", line=line@entry=154) at crypto/cryptlib.c:421
#3  0x00007ffff58c0bc8 in FIPS_selftest_check () at crypto/fips/fips.c:154
#4  0x00007ffff57c3085 in init_fips_mode () at crypto/o_init.c:56
#5  OPENSSL_init_library () at crypto/o_init.c:70
#6  0x00007ffff7dd6f4a in call_init (l=<optimized out>, argc=argc@entry=1, argv=argv@entry=0x7fffffffe118,
    env=env@entry=0x7fffffffe128) at dl-init.c:72
#7  0x00007ffff7dd704a in call_init (env=0x7fffffffe128, argv=0x7fffffffe118, argc=1, l=<optimized out>) at dl-init.c:30
#8  _dl_init (main_map=0x4a18f0, argc=1, argv=0x7fffffffe118, env=0x7fffffffe128) at dl-init.c:119
#9  0x00007ffff7535e2c in __GI__dl_catch_exception (exception=<optimized out>, operate=<optimized out>, args=<optimized out>)
    at dl-error-skeleton.c:182
#10 0x00007ffff7dde79e in dl_open_worker (a=0x7fffffff9a90) at dl-open.c:794
#11 dl_open_worker (a=a@entry=0x7fffffff9a90) at dl-open.c:757
#12 0x00007ffff7535dd4 in __GI__dl_catch_exception (exception=<optimized out>, operate=<optimized out>, args=<optimized out>)
    at dl-error-skeleton.c:208
#13 0x00007ffff7dde981 in _dl_open (
    file=0x7ffff609e5d0 "/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so", mode=-2147483646, caller_dlopen=0x7ffff6f3e791 <_PyImport_FindSharedFuncptr+145>, nsid=<optimized out>, argc=1,
    argv=<optimized out>, env=0x7fffffffe128) at dl-open.c:876
#14 0x00007ffff7bcaf8a in dlopen_doit (a=a@entry=0x7fffffff9d00) at dlopen.c:66
#15 0x00007ffff7535dd4 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffff9c60, operate=<optimized out>,
    args=<optimized out>) at dl-error-skeleton.c:208
#16 0x00007ffff7535e93 in __GI__dl_catch_error (objname=0x7fffffff9cb8, errstring=0x7fffffff9cc0, mallocedp=0x7fffffff9cb7,
    operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:227
#17 0x00007ffff7bcb52e in _dlerror_run (operate=operate@entry=0x7ffff7bcaf30 <dlopen_doit>, args=args@entry=0x7fffffff9d00)
    at dlerror.c:142
#18 0x00007ffff7bcb02a in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
#19 0x00007ffff6f3e791 in _PyImport_FindSharedFuncptr (prefix=0x7ffff6fa92c1 "PyInit", shortname=0x7ffff60f2330 "_hashlib",
    pathname=0x7ffff609e5d0 "/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64--Type <RET> for more, q to quit, c to continue without paging--
-linux-gnu.so", fp=0x0) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/dynload_shlib.c:80
#20 0x00007ffff6f3e9f2 in _PyImport_LoadDynamicModuleWithSpec (fp=0x0,
    spec=<ModuleSpec(name='_hashlib', loader=<ExtensionFileLoader(name='_hashlib', path='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so') at remote 0x7ffff60fc890>, origin='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so', loader_state=None, submodule_search_locations=None, _uninitialized_submodules=[], _set_fileattr=True, _cached=None) at remote 0x7ffff60fc790>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/cpython/bytesobject.h:37
#21 _imp_create_dynamic_impl (module=<optimized out>, file=<optimized out>,
    spec=<ModuleSpec(name='_hashlib', loader=<ExtensionFileLoader(name='_hashlib', path='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so') at remote 0x7ffff60fc890>, origin='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so', loader_state=None, submodule_search_locations=None, _uninitialized_submodules=[], _set_fileattr=True, _cached=None) at remote 0x7ffff60fc790>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/import.c:2386
#22 _imp_create_dynamic (module=<optimized out>, args=<optimized out>, nargs=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/clinic/import.c.h:446
#23 0x00007ffff6e68d3b in cfunction_vectorcall_FASTCALL (func=<optimized out>, args=<optimized out>, nargsf=1,
    kwnames=<optimized out>) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/cpython/methodobject.h:52
#24 0x00007ffff6deefab in _PyVectorcall_Call (kwargs=<optimized out>,
    tuple=(<ModuleSpec(name='_hashlib', loader=<ExtensionFileLoader(name='_hashlib', path='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so') at remote 0x7ffff60fc890>, origin='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so', loader_state=None, submodule_search_locations=None, _uninitialized_submodules=[], _set_fileattr=True, _cached=None) at remote 0x7ffff60fc790>,),
    callable=<built-in method create_dynamic of module object at remote 0x7ffff7f76390>,
    func=0x7ffff6e68a60 <cfunction_vectorcall_FASTCALL>, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:245
#25 _PyObject_Call (tstate=0x7ffff738a0d8 <_PyRuntime+166328>,
    callable=<built-in method create_dynamic of module object at remote 0x7ffff7f76390>,
    args=(<ModuleSpec(name='_hashlib', loader=<ExtensionFileLoader(name='_hashlib', path='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so') at remote 0x7ffff60fc890>, origin='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so', loader_state=None, submodule_search_locations=None, _uninitialized_submodules=[], _set_fileattr=True, _cached=None) at remote 0x7ffff60fc790>,),
    kwargs=<optimized out>) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:328
#26 0x00007ffff6dda6b0 in do_call_core (use_tracing=<optimized out>, kwdict={},
    callargs=(<ModuleSpec(name='_hashlib', loader=<ExtensionFileLoader(name='_hashlib', path='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so') at remote 0x7ffff60fc890>, origin='/home/redacted/buildtest/simple/dist/simple/_internal/lib-dynload/_hashlib.cpython-311-x86_64-linux-gnu.so', loader_state=None, submodule_search_locations=None, _uninitialized_submodules=[], _set_fileattr=True, _cached=None) at remote 0x7ffff60fc790>,),
    func=<built-in method create_dynamic of module object at remote 0x7ffff7f76390>, tstate=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:7329
#27 _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:5381
#28 0x00007ffff6e585dd in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff7ff0380, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/internal/pycore_ceval.h:70
#29 _PyEval_Vector (kwnames=<optimized out>, argcount=<optimized out>, args=<optimized out>, locals=0x0, func=<optimized out>,
    tstate=<optimized out>) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:6439
#30 _PyFunction_Vectorcall (func=<optimized out>, stack=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:393
#31 0x00007ffff6e2a77c in _PyObject_VectorcallTstate (kwnames=0x0, nargsf=<optimized out>, args=0x7fffffffa3f0,
    callable=<function at remote 0x7ffff7f3bce0>, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/internal/pycore_call.h:77
#32 object_vacall (tstate=0x7ffff738a0d8 <_PyRuntime+166328>, base=<optimized out>,
    callable=<function at remote 0x7ffff7f3bce0>, vargs=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:819
#33 0x00007ffff6e7e2ca in PyObject_CallMethodObjArgs (obj=0x0, name=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:878
#34 0x00007ffff6ddf998 in import_find_and_load (abs_name='_hashlib', tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/import.c:1737
#35 PyImport_ImportModuleLevelObject (name='_hashlib', globals=<optimized out>, locals=<optimized out>, fromlist=None, level=0)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/import.c:1836
#36 0x00007ffff6dd7095 in import_name (level=0, fromlist=None, name='_hashlib', frame=0x7ffff7ff0308, tstate=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:7429
#37 _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:3951
#38 0x00007ffff6eb47b9 in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff7ff0308, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/internal/pycore_ceval.h:70
#39 _PyEval_Vector (kwnames=0x0, argcount=0, args=0x0, locals=<optimized out>, func=0x7ffff60faca0,
    tstate=0x7ffff738a0d8 <_PyRuntime+166328>) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:6439
#40 PyEval_EvalCode (co=<code at remote 0x5155b0>, globals=<optimized out>, locals=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:1154
#41 0x00007ffff6eb9925 in builtin_exec_impl (module=<optimized out>, closure=<optimized out>,
    locals={'__name__': 'hashlib', '__doc__': "hashlib module - A common interface to many hash functions.\n\nnew(name, data=b'', **kwargs) - returns a new hash object implementing the\n                                given hash function; initializing the hash\n                                using the given binary data.\n\nNamed constructor functions are also available, these are faster\nthan using new(name):\n\nmd5(), sha1(), sha224(), sha256(), sha384(), sha512(), blake2b(), blake2s(),\nsha3_224, sha3_256, sha3_384, sha3_512, shake_128, and shake_256.\n\nMore algorithms may be available on your platform but the above are guaranteed\nto exist.  See the algorithms_guaranteed and algorithms_available attributes\nto find out what algorithm names can be passed to new().\n\nNOTE: If you want the adler32 or crc32 hash functions they are available in\nthe zlib module.\n\nChoose your hash function wisely.  Some have known collision weaknesses.\nsha384 and sha512 will be slow on 32 bit platforms.\n\nHash objects have these met...(truncated), globals=<optimized out>, source=<code at remote 0x5155b0>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/bltinmodule.c:1077
#42 builtin_exec (module=<optimized out>, args=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/clinic/bltinmodule.c.h:465
#43 0x00007ffff6dd4be7 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/cpython/methodobject.h:52
#44 0x00007ffff6e585dd in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff7ff0080, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/internal/pycore_ceval.h:70
#45 _PyEval_Vector (kwnames=<optimized out>, argcount=<optimized out>, args=<optimized out>, locals=0x0, func=<optimized out>,
    tstate=<optimized out>) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:6439
#46 _PyFunction_Vectorcall (func=<optimized out>, stack=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:393
#47 0x00007ffff6e2a77c in _PyObject_VectorcallTstate (kwnames=0x0, nargsf=<optimized out>, args=0x7fffffffab60,
    callable=<function at remote 0x7ffff7f3bce0>, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/internal/pycore_call.h:77
#48 object_vacall (tstate=0x7ffff738a0d8 <_PyRuntime+166328>, base=<optimized out>,
    callable=<function at remote 0x7ffff7f3bce0>, vargs=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:819
#49 0x00007ffff6e7e2ca in PyObject_CallMethodObjArgs (obj=0x0, name=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Objects/call.c:878
#50 0x00007ffff6ddf998 in import_find_and_load (abs_name='hashlib', tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/import.c:1737
#51 PyImport_ImportModuleLevelObject (name='hashlib', globals=<optimized out>, locals=<optimized out>, fromlist=None, level=0)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/import.c:1836
#52 0x00007ffff6dd7095 in import_name (level=0, fromlist=None, name='hashlib', frame=0x7ffff7ff0020, tstate=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:7429
#53 _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:3951
#54 0x00007ffff6eb47b9 in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff7ff0020, tstate=0x7ffff738a0d8 <_PyRuntime+166328>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Include/internal/pycore_ceval.h:70
#55 _PyEval_Vector (kwnames=0x0, argcount=0, args=0x0, locals=<optimized out>, func=0x7ffff6591440,
    tstate=0x7ffff738a0d8 <_PyRuntime+166328>) at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:6439
#56 PyEval_EvalCode (co=<code at remote 0x7ffff7f6ef10>, globals=<optimized out>, locals=<optimized out>)
    at /usr/src/debug/python3.11-3.11.5-1.el8_9.x86_64/Python/ceval.c:1154
#57 0x0000000000403d2f in ?? ()
#58 0x0000000000404004 in ?? ()
#59 0x00007ffff7407d85 in __libc_start_main (main=0x402570, argc=1, argv=0x7fffffffe118, init=0x4086c0, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7fffffffe108) at ../csu/libc-start.c:302
#60 0x000000000040259e in ?? ()
```

### Workaround
Using the spec file or the `--add-binary` option to include HMAC files to match the bundled OpenSSL shared libraries appears to resolve the issue. However, the HMAC file names need to match their associated libraries.
For example, if `dist/simple/_internal` would contain `libcrypto.so.1.1` then the associated HMAC file seems to need to be named `.libcrypto.so.1.1.hmac`.
For onefile builds the HMAC files need to be provided before/during the build. But for non-onefile builds it seems that copying the HMAC files into the _internal directory afterward also works.

The original HMAC files are found alongside the shared libraries, `ls -la /usr/lib64/`. Just note that names need to match. The workaround doesn't seem to work if `.libcrypto.so.1.1.1k.hmac` is bundled alongside `libcrypto.so.1.1`.

### Additional Notes
These HMAC files aren't new to RHEL8. They exist in RHEL7 (and RHEL9) too, but even though PyInstaller doesn't bundle the HMAC files on RHEL7 the issue doesn't occur on RHEL7. If the root cause of this issue isn't PyInstaller, then my best guess is that RHEL8 or OpenSSL 1.1.? FIPS is doing something unique that interacts badly with PyInstaller.


Discussion:
I suppose we could add an additional linux-specific processing pass at the end of analysis, which goes over shared libraries to be collected, and collects associated HMAC files if they exist.

Although this probably also means that collected shared libraries with associated HMACs must not be processed in any way (e.g., when building with `strip=True`)...
> Although this probably also means that collected shared libraries with associated HMACs must not be processed in any way (e.g., when building with strip=True)...

Which would impede any ambition of using rpath instead `LD_LIBRARY_PATH`.
> > Although this probably also means that collected shared libraries with associated HMACs must not be processed in any way (e.g., when building with strip=True)...
> 
> Which would impede any ambition of using rpath instead `LD_LIBRARY_PATH`.

Indeed.
If I'm reading this right, all it would take to implement would be to iterate through the binaries and append `source.with_name(f".{source.name}.hmac")`? If it's that easy then I guess we might as well – even if we do eventually find an rpath editing library and end up pulling this feature in favour of rpath.


Resolving patch:
diff --git a/PyInstaller/building/build_main.py b/PyInstaller/building/build_main.py
--- a/PyInstaller/building/build_main.py
+++ b/PyInstaller/building/build_main.py
@@ -35,7 +35,7 @@
     _check_guts_toc, _check_guts_toc_mtime, _should_include_system_binary, format_binaries_and_datas, compile_pymodule,
     add_suffix_to_extension, postprocess_binaries_toc_pywin32, postprocess_binaries_toc_pywin32_anaconda
 )
-from PyInstaller.compat import is_win, is_conda, is_darwin
+from PyInstaller.compat import is_win, is_conda, is_darwin, is_linux
 from PyInstaller.depend import bindepend
 from PyInstaller.depend.analysis import initialize_modgraph
 from PyInstaller.depend.utils import create_py3_base_library, scan_code_for_ctypes
@@ -810,6 +810,20 @@ def assemble(self):
             if is_conda:
                 self.binaries = postprocess_binaries_toc_pywin32_anaconda(self.binaries)
 
+        # On linux, check for HMAC files accompanying shared library files and, if available, collect them.
+        # These are present on Fedora and RHEL, and are used in FIPS-enabled configurations to ensure shared
+        # library's file integrity.
+        if is_linux:
+            for dest_name, src_name, typecode in self.binaries:
+                if typecode not in {'BINARY', 'EXTENSION'}:
+                    continue  # Skip symbolic links
+                src_lib_path = pathlib.Path(src_name)
+                src_hmac_path = src_lib_path.with_name(f".{src_lib_path.name}.hmac")
+                if not src_hmac_path.is_file():
+                    continue
+                dest_hmac_path = pathlib.PurePath(dest_name).with_name(src_hmac_path.name)
+                self.datas.append((str(dest_hmac_path), str(src_hmac_path), 'DATA'))
+
         # Final normalization of `datas` and `binaries`:
         #  - normalize both TOCs together (to avoid having duplicates across the lists)
         #  - process the combined normalized TOC for symlinks
diff --git a/PyInstaller/building/utils.py b/PyInstaller/building/utils.py
--- a/PyInstaller/building/utils.py
+++ b/PyInstaller/building/utils.py
@@ -25,7 +25,7 @@
 
 from PyInstaller import compat
 from PyInstaller import log as logging
-from PyInstaller.compat import (EXTENSION_SUFFIXES, is_darwin, is_win)
+from PyInstaller.compat import EXTENSION_SUFFIXES, is_darwin, is_win, is_linux
 from PyInstaller.config import CONF
 from PyInstaller.exceptions import InvalidSrcDestTupleError
 from PyInstaller.utils import misc
@@ -130,12 +130,6 @@ def process_collected_binary(
     if not use_strip and not use_upx and not is_darwin:
         return src_name
 
-    # Skip processing if this is Windows .manifest file. We used to process these as part of support for collecting
-    # WinSxS assemblies, but that was removed in PyInstaller 6.0. So in case we happen to get a .manifest file here,
-    # return it as-is.
-    if is_win and src_name.lower().endswith(".manifest"):
-        return src_name
-
     # Match against provided UPX exclude patterns.
     upx_exclude = upx_exclude or []
     if use_upx:
@@ -148,6 +142,31 @@ def process_collected_binary(
                 use_upx = False
                 break
 
+    # Additional automatic disablement rules for UPX and strip.
+
+    # On Windows, avoid using UPX with binaries that have control flow guard (CFG) enabled.
+    if use_upx and is_win and versioninfo.pefile_check_control_flow_guard(src_name):
+        logger.info('Disabling UPX for %s due to CFG!', src_name)
+        use_upx = False
+
+    # Avoid using UPX with Qt plugins, as it strips the data required by the Qt plugin loader.
+    if use_upx and misc.is_file_qt_plugin(src_name):
+        logger.info('Disabling UPX for %s due to it being a Qt plugin!', src_name)
+        use_upx = False
+
+    # On linux, if a binary has an accompanying HMAC file, avoid modifying it in any way.
+    if (use_upx or use_strip) and is_linux:
+        src_path = pathlib.Path(src_name)
+        hmac_path = src_path.with_name(f".{src_path.name}.hmac")
+        if hmac_path.is_file():
+            logger.info('Disabling UPX and/or strip for %s due to accompanying .hmac file!', src_name)
+            use_upx = use_strip = False
+        del src_path, hmac_path
+
+    # Exit early if no processing is required after above rules are applied.
+    if not use_strip and not use_upx and not is_darwin:
+        return src_name
+
     # Prepare cache directory path. Cache is tied to python major/minor version, but also to various processing options.
     pyver = f'py{sys.version_info[0]}{sys.version_info[1]}'
     arch = platform.architecture()[0]
@@ -200,52 +219,6 @@ def process_collected_binary(
         # ... otherwise remove it.
         os.remove(cached_name)
 
-    cmd = None
-
-    if use_upx:
-        # If we are to apply both strip and UPX, apply strip first.
-        if use_strip:
-            src_name = process_collected_binary(
-                src_name,
-                dest_name,
-                use_strip=True,
-                use_upx=False,
-                target_arch=target_arch,
-                codesign_identity=codesign_identity,
-                entitlements_file=entitlements_file,
-                strict_arch_validation=strict_arch_validation,
-            )
-        # We need to avoid using UPX with Windows DLLs that have Control Flow Guard enabled, as it breaks them.
-        if is_win and versioninfo.pefile_check_control_flow_guard(src_name):
-            logger.info('Disabling UPX for %s due to CFG!', src_name)
-        elif misc.is_file_qt_plugin(src_name):
-            logger.info('Disabling UPX for %s due to it being a Qt plugin!', src_name)
-        else:
-            upx_exe = 'upx'
-            upx_dir = CONF['upx_dir']
-            if upx_dir:
-                upx_exe = os.path.join(upx_dir, upx_exe)
-
-            upx_options = [
-                # Do not compress icons, so that they can still be accessed externally.
-                '--compress-icons=0',
-                # Use LZMA compression.
-                '--lzma',
-                # Quiet mode.
-                '-q',
-            ]
-            if is_win:
-                # Binaries built with Visual Studio 7.1 require --strip-loadconf or they will not compress.
-                upx_options.append('--strip-loadconf')
-
-            cmd = [upx_exe, *upx_options, cached_name]
-    elif use_strip:
-        strip_options = []
-        if is_darwin:
-            # The default strip behavior breaks some shared libraries under macOS.
-            strip_options = ["-S"]  # -S = strip only debug symbols.
-        cmd = ["strip", *strip_options, cached_name]
-
     # Ensure parent path exists
     os.makedirs(os.path.dirname(cached_name), exist_ok=True)
 
@@ -255,9 +228,69 @@ def process_collected_binary(
     shutil.copyfile(src_name, cached_name)
     os.chmod(cached_name, 0o755)
 
-    if cmd:
+    # Apply strip
+    if use_strip:
+        strip_options = []
+        if is_darwin:
+            # The default strip behavior breaks some shared libraries under macOS.
+            strip_options = ["-S"]  # -S = strip only debug symbols.
+
+        cmd = ["strip", *strip_options, cached_name]
         logger.info("Executing: %s", " ".join(cmd))
-        subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+        try:
+            p = subprocess.run(
+                cmd,
+                stdin=subprocess.DEVNULL,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                check=True,
+                errors='ignore',
+                encoding='utf-8',
+            )
+            logger.debug("Output from strip command:\n%s", p.stdout)
+        except subprocess.CalledProcessError as e:
+            logger.warning("Failed to run strip on %r!", cached_name, exc_info=True)
+            logger.warning("Output from strip command:\n%s", e.stdout)
+        except Exception:
+            logger.warning("Failed to run strip on %r!", cached_name, exc_info=True)
+
+    # Apply UPX
+    if use_upx:
+        upx_exe = 'upx'
+        upx_dir = CONF['upx_dir']
+        if upx_dir:
+            upx_exe = os.path.join(upx_dir, upx_exe)
+
+        upx_options = [
+            # Do not compress icons, so that they can still be accessed externally.
+            '--compress-icons=0',
+            # Use LZMA compression.
+            '--lzma',
+            # Quiet mode.
+            '-q',
+        ]
+        if is_win:
+            # Binaries built with Visual Studio 7.1 require --strip-loadconf or they will not compress.
+            upx_options.append('--strip-loadconf')
+
+        cmd = [upx_exe, *upx_options, cached_name]
+        logger.info("Executing: %s", " ".join(cmd))
+        try:
+            p = subprocess.run(
+                cmd,
+                stdin=subprocess.DEVNULL,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                check=True,
+                errors='ignore',
+                encoding='utf-8',
+            )
+            logger.debug("Output from upx command:\n%s", p.stdout)
+        except subprocess.CalledProcessError as e:
+            logger.warning("Failed to upx strip on %r!", cached_name, exc_info=True)
+            logger.warning("Output from upx command:\n%s", e.stdout)
+        except Exception:
+            logger.warning("Failed to run upx on %r!", cached_name, exc_info=True)
 
     # On macOS, we need to modify the given binary's paths to the dependent libraries, in order to ensure they are
     # relocatable and always refer to location within the frozen application. Specifically, we make all dependent
@@ -304,7 +337,7 @@ def process_collected_binary(
 
 
 def _compute_file_digest(filename):
-    hasher = hashlib.md5()
+    hasher = hashlib.sha1()
     with open(filename, "rb") as fp:
         for chunk in iter(lambda: fp.read(16 * 1024), b""):
             hasher.update(chunk)


Test patch: