Package: src:ffmpeg Version: 7:6.1-5 Severity: normal Hi,
While developing a program against the ffmpeg libraries, I noticed that a number of threads were being implicitly created at process startup. Attached is a minimal test case that replicates the behavior. It prints the output of the avfilter_configuration() function and then prints the number of running threads. Here is the output on my machine: --prefix=/usr --extra-version=5 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libharfbuzz --enable-libfribidi --enable-libglslang --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librist --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --disable-sndio --enable-libjxl --enable-pocketsphinx --enable-librsvg --enable-libvpl --disable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libsvtav1 --enable-libx264 --enable-libplacebo --enable-librav1e --enable-shared Threads: 12 It looks like this is due to the following dependency chain: 1. libavfilter depends on libsphinxbase: $ objdump -p /usr/lib/x86_64-linux-gnu/libavfilter.so.9 | grep sphinxbase NEEDED libsphinxbase.so.3 2. libsphinxbase depends on libblas: $ objdump -p /usr/lib/x86_64-linux-gnu/libsphinxbase.so.3 | grep libblas NEEDED libblas.so.3 3. libblas depends on libopenblas: $ objdump -p /usr/lib/x86_64-linux-gnu/libblas.so.3 | grep libopenblas NEEDED libopenblas.so.0 The libopenblas.so.0 library contains an __attribute__((constructor)) function that runs an initialization function inside the libopenblas library. In any application that happens to link against ffmpeg's libavfilter (or libavdevice, which depends on libavfilter). This can be confirmed with gdb on the attached test case program: $ gdb ./avfilter_test GNU gdb (Debian 13.2-1) 13.2 Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./avfilter_test... (No debugging symbols found in ./avfilter_test) (gdb) break clone3 Function "clone3" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (clone3) pending. (gdb) run Starting program: /tmp/avfilter_test [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:44 44 ../sysdeps/unix/sysv/linux/x86_64/clone3.S: No such file or directory. (gdb) bt #0 clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:44 #1 0x00007ffff71269cd in __GI___clone_internal (cl_args=cl_args@entry=0x7fffffffe500, func=func@entry=0x7ffff70a6130 <start_thread>, arg=arg@entry=0x7fffe55ff6c0) at ../sysdeps/unix/sysv/linux/clone-internal.c:54 #2 0x00007ffff70a6030 in create_thread (pd=pd@entry=0x7fffe55ff6c0, attr=attr@entry=0x7fffffffe600, stopped_start=stopped_start@entry=0x7fffffffe5f6, stackaddr=stackaddr@entry=0x7fffe4dff000, stacksize=<optimized out>, thread_ran=thread_ran@entry=0x7fffffffe5f7) at ./nptl/pthread_create.c:297 #3 0x00007ffff70a6aee in __pthread_create_2_1 (newthread=<optimized out>, attr=<optimized out>, start_routine=<optimized out>, arg=<optimized out>) at ./nptl/pthread_create.c:833 #4 0x00007fffe80b7769 in blas_thread_init () at /lib/x86_64-linux-gnu/libopenblas.so.0 #5 0x00007fffe7e6807f in gotoblas_init () at /lib/x86_64-linux-gnu/libopenblas.so.0 #6 0x00007ffff7fcfe3e in call_init (env=0x7fffffffe7f8, argv=0x7fffffffe7e8, argc=1, l=<optimized out>) at ./elf/dl-init.c:74 #7 call_init (l=<optimized out>, argc=1, argv=0x7fffffffe7e8, env=0x7fffffffe7f8) at ./elf/dl-init.c:26 #8 0x00007ffff7fcff24 in _dl_init (main_map=0x7ffff7ffe2c0, argc=1, argv=0x7fffffffe7e8, env=0x7fffffffe7f8) at ./elf/dl-init.c:121 #9 0x00007ffff7fe5500 in _dl_start_user () at /lib64/ld-linux-x86-64.so.2 #10 0x0000000000000001 in () #11 0x00007fffffffeaf7 in () #12 0x0000000000000000 in () (gdb) Stack frames 5 and 6 show the dynamic loader calling gotoblas_init() in libopenblas.so.0, before main() starts. And here's that function in openblas: https://sources.debian.org/src/openblas/0.3.25%2Bds-1/driver/others/memory.c/#L1507 Anyway, it would be nice if all programs linked against libavfilter or libavdevice weren't forced to start up a thread pool for some other library that happens to get pulled in but is otherwise unused. I'm not aware of a technique to prevent a constructor function in a shared library from running. It looks like this is caused by ffmpeg being compiled with --enable-pocketsphinx (at least on amd64), and indirectly by libopenblas's API relying on an implicit __attribute__((constructor)) library initialization function rather than having an explicit library initialization function. Feel free to reassign this bug to src:openblas, or to clone it and reassign to src:openblas. Thanks! -- Robert Edmonds edmo...@debian.org
/* * Compile with: * * gcc -O2 -Wall -o avfilter_test avfilter_test.c $(pkg-config --cflags --libs libavfilter) */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <libavfilter/avfilter.h> int main(void) { puts(avfilter_configuration()); putchar('\n'); char *cmd = NULL; asprintf(&cmd, "cat /proc/%d/status | grep ^Threads", (int)getpid()); return system(cmd); }