Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static object constructors do not execute on the NATIVE_POSIX_64 target #39347

Closed
aleksei-timofeyev opened this issue Oct 12, 2021 · 14 comments · Fixed by #46357
Closed

Static object constructors do not execute on the NATIVE_POSIX_64 target #39347

aleksei-timofeyev opened this issue Oct 12, 2021 · 14 comments · Fixed by #46357
Assignees
Labels
area: C++ bug The issue is a bug, or the PR is fixing a bug priority: low Low impact/importance bug

Comments

@aleksei-timofeyev
Copy link

Describe the bug
All C++ global object constructors are not executed upon startup of a NATIVE_POSIX_64 executable. This breaks code that relies upon constructor side effects (such as registering unit tests with a test framework).

To Reproduce
Steps to reproduce the behavior:

  1. Create a trivial C++ application with the following main.cc:
#include <string>
#include <iostream>

struct SideEffectConstructor {
    SideEffectConstructor(std::string s) {
        std::cerr << "Constructing " << s << std::endl;
    }
};

static auto object_a = SideEffectConstructor{"Object A"};
static auto object_b = SideEffectConstructor{"Object B"};
static auto object_c = SideEffectConstructor{"Object C"};
static auto object_d = SideEffectConstructor{"Object D"};

void main() {
    exit(0);
}
  1. Build the application for the native_posix_64 target with following Kconfig:
CONFIG_CPLUSPLUS=y
CONFIG_LIB_CPLUSPLUS=y
CONFIG_STD_CPP20=y
CONFIG_ASSERT=y
CONFIG_SPEED_OPTIMIZATIONS=y
  1. Execute the application.
  2. Observe the following console output:
(.venv) bash-5.1$ ./build/zephyr/zephyr.elf 
*** Booting Zephyr OS build v2.7.0-rc3-259-ga6dcf333a1b8  ***

Expected behavior
The following lines should be printed to the terminal exactly once:

Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D

Impact
This bug results in a major discrepancy between the behavior of code running on the native_posix_64 board and the behavior of that same code running on physical board. This bug also interferes with the use of common unit test frameworks that rely on auto-registration of tests via global object constructors.

Environment (please complete the following information):

  • OS: (Linux)
  • Toolchain: Host GNU 11.2.1
  • Zephyr OS build v2.7.0-rc3-259-ga6dcf333a1b8

Additional context
The bug first appears at commit d10b46d.

#36858

@aleksei-timofeyev aleksei-timofeyev added the bug The issue is a bug, or the PR is fixing a bug label Oct 12, 2021
@cfriedt cfriedt added priority: low Low impact/importance bug area: C++ labels Oct 12, 2021
@stephanosio
Copy link
Member

@palchak-google FYI

@palchak-google
Copy link
Contributor

Can you confirm that those four objects are actually in the resulting executable? What happens if you disable optimizations?

When I built my original example, I had all optimizations disabled. I think what may be happening is that the compiler can see that those four static objects are never used and thus they're being elided.

@aleksei-timofeyev
Copy link
Author

aleksei-timofeyev commented Oct 14, 2021

Can you confirm that those four objects are actually in the resulting executable? What happens if you disable optimizations?

CONFIG_CPLUSPLUS=y
CONFIG_LIB_CPLUSPLUS=y
CONFIG_STD_CPP20=y
CONFIG_ASSERT=y
CONFIG_NO_OPTIMIZATIONS=y

With configuration above all objects are present:

 .bss._ZL8object_a
                0x0000000000411d3d        0x1 app/libapp.a(main.cpp.obj)
 .bss._ZL8object_b
                0x0000000000411d3e        0x1 app/libapp.a(main.cpp.obj)
 .bss._ZL8object_c
                0x0000000000411d3f        0x1 app/libapp.a(main.cpp.obj)
 .bss._ZL8object_d
                0x0000000000411d40        0x1 app/libapp.a(main.cpp.obj)

Below you can find 2 archives with following files:
zephyr.elf zephyr.lst zephyr.map zephyr.stat

With patch:
1.tar.gz
Without patch:
2.tar.gz

@palchak-google
Copy link
Contributor

I am unable to reproduce this issue. I'm building with:

OS: Linux
Toolchain: gcc (Debian 10.3.0-11) 10.3.0
Zephyr: commit 86ad718 (HEAD, tag: v2.7.99, tag: v2.7.0-rc3)

I get the same output with native_posix and native_posix_64 targets:

$ ./zephyr/zephyr.elf
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build v2.7.99  ***

I do not have the ability at the moment to upgrade to GCC 11.2.1 to confirm whether the issue is related to the toolchain version (10.3.0 vs 11.2.1).

Project files used:
static_objects.zip

@aleksei-timofeyev
Copy link
Author

I am unable to reproduce this issue with GCC 10.3.1

sh-5.0# west build -p auto -b native_posix_64 samples/subsys/cpp/cpp_static
-- west build: generating a build system
Including boilerplate (Zephyr base): /root/zephyrproject/zephyr/cmake/app/boilerplate.cmake
-- Application: /root/zephyrproject/zephyr/samples/subsys/cpp/cpp_static
-- Zephyr version: 2.7.99 (/root/zephyrproject/zephyr), build: v2.7.99-728-gfe7529f917ad
-- Found Python3: /usr/bin/python3.9 (found suitable exact version "3.9.7") found components: Interpreter 
-- Found west (found suitable version "0.11.1", minimum required is "0.7.1")
-- Board: native_posix_64
-- Cache files will be written to: /root/.cache/zephyr
-- Found dtc: /usr/bin/dtc (found suitable version "1.6.0", minimum required is "1.4.6")
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /root/zephyrproject/zephyr/boards/posix/native_posix/native_posix_64.dts
-- Generated zephyr.dts: /root/zephyrproject/zephyr/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /root/zephyrproject/zephyr/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /root/zephyrproject/zephyr/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /root/zephyrproject/zephyr/build/zephyr/dts.cmake
Parsing /root/zephyrproject/zephyr/Kconfig
Loaded configuration '/root/zephyrproject/zephyr/boards/posix/native_posix/native_posix_64_defconfig'
Merged configuration '/root/zephyrproject/zephyr/samples/subsys/cpp/cpp_static/prj.conf'
Configuration saved to '/root/zephyrproject/zephyr/build/zephyr/.config'
Kconfig header saved to '/root/zephyrproject/zephyr/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /root/zephyrproject/zephyr/CMakeLists.txt:1649 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /root/zephyrproject/zephyr/build
-- west build: building application
[1/100] Preparing syscall dependency handling

[95/100] Linking CXX executable zephyr/zephyr_prebuilt.elf

[100/100] Linking CXX executable zephyr/zephyr.elf
sh-5.0# build/zephyr/zephyr.elf
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build v2.7.99-728-gfe7529f917ad  ***

and able to reproduce with GCC 11.2.1

sh-5.1# west build -p auto -b native_posix_64 samples/subsys/cpp/cpp_static
-- west build: generating a build system
Including boilerplate (Zephyr base): /root/zephyrproject/zephyr/cmake/app/boilerplate.cmake
-- Application: /root/zephyrproject/zephyr/samples/subsys/cpp/cpp_static
-- Zephyr version: 2.7.99 (/root/zephyrproject/zephyr), build: v2.7.99-729-g95ad27fe1f8a
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.0") found components: Interpreter 
-- Found west (found suitable version "0.11.1", minimum required is "0.7.1")
-- Board: native_posix_64
-- Cache files will be written to: /root/.cache/zephyr
-- Found dtc: /usr/bin/dtc (found suitable version "1.6.1", minimum required is "1.4.6")
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /root/zephyrproject/zephyr/boards/posix/native_posix/native_posix_64.dts
-- Generated zephyr.dts: /root/zephyrproject/zephyr/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /root/zephyrproject/zephyr/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /root/zephyrproject/zephyr/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /root/zephyrproject/zephyr/build/zephyr/dts.cmake
Parsing /root/zephyrproject/zephyr/Kconfig
Loaded configuration '/root/zephyrproject/zephyr/boards/posix/native_posix/native_posix_64_defconfig'
Merged configuration '/root/zephyrproject/zephyr/samples/subsys/cpp/cpp_static/prj.conf'
Configuration saved to '/root/zephyrproject/zephyr/build/zephyr/.config'
Kconfig header saved to '/root/zephyrproject/zephyr/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 11.2.1
-- The CXX compiler identification is GNU 11.2.1
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /root/zephyrproject/zephyr/CMakeLists.txt:1649 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /root/zephyrproject/zephyr/build
-- west build: building application
[1/100] Preparing syscall dependency handling

[5/100] Generating include/generated/driver-validation.h
/root/zephyrproject/zephyr/scripts/gen_kobject_list.py:60: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives
  from distutils.version import LooseVersion
[6/100] Generating include/generated/kobj-types-enum.h...ated/otype-to-str.h, include/generated/otype-to-size.h
/root/zephyrproject/zephyr/scripts/gen_kobject_list.py:60: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives
  from distutils.version import LooseVersion
[95/100] Linking CXX executable zephyr/zephyr_prebuilt.elf

[97/100] Generating dev_handles.c
/root/zephyrproject/zephyr/scripts/gen_handles.py:34: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives
  from distutils.version import LooseVersion
[100/100] Linking CXX executable zephyr/zephyr.elf
sh-5.1# build/zephyr/zephyr.elf
*** Booting Zephyr OS build v2.7.99-729-g95ad27fe1f8a  ***

For testing I used containers created from images:

podman images
REPOSITORY                                 TAG         IMAGE ID      CREATED      SIZE
registry.fedoraproject.org/fedora-toolbox  33          1942cc76c9c8  13 days ago  355 MB
registry.fedoraproject.org/fedora-toolbox  35          6387bd6f2368  13 days ago  497 MB
history
    1  sudo dnf group install "Development Tools" "C Development Tools and Libraries"
    2  dnf install git cmake ninja-build gperf ccache dfu-util dtc wget   python3-pip python3-tkinter xz file glibc-devel.i686 libstdc++-devel.i686 python38   SDL2-devel
    3  cmake --version
    4  pip3 install --user -U west
    5  PATH=/root/.local/bin:$PATH
    6  pip3 install --user -U west
    7  west init ~/zephyrproject
    8  cd ~/zephyrproject/
    9  west update
   10  west zephyr-export
   11  pip3 --use-feature=2020-resolver install --user -r ~/zephyrproject/zephyr/scripts/requirements.txt
   12  pip3  install --user -r ~/zephyrproject/zephyr/scripts/requirements.txt
   13  pip3 install --user -U wheel
   14  pip3  install --user -r ~/zephyrproject/zephyr/scripts/requirements.txt
   15  dnf search psutil
   16  dnf install pytho3-psutil
   17  dnf install python3-psutil
   18  pip3  install --user -r ~/zephyrproject/zephyr/scripts/requirements.txt
   19  cd ~/zephyrproject/zephyr
   20  mkdir samples/subsys/cpp/cpp_static
   21  vi samples/subsys/cpp/cpp_static/CMakeLists.txt
   22  vi samples/subsys/cpp/cpp_static/main.cpp
   23  vi samples/subsys/cpp/cpp_static/prj.conf
   24  west build -p auto -b native_posix_64 samples/subsys/cpp/cpp_static
   25  ls
   26  mv samples/subsys/cpp/cpp_static/main.cpp samples/subsys/cpp/cpp_static/main.cc
   27  west build -p auto -b native_posix_64 samples/subsys/cpp/cpp_static
   28  ls
   29  rm -rf build/
   30  west build -p auto -b native_posix_64 samples/subsys/cpp/cpp_static
   31  build/zephyr/zephyr.elf
   32  history

@palchak-google
Copy link
Contributor

palchak-google commented Nov 3, 2021

Well we have ourselves a real head scratcher here. I was able to install GCC v11.2.0 last week, and when building with that toolchain I also failed to reproduce the issue (for either native_posix or native_posix_64 target):

~/projects/zephyr-upstream/app/static_objs/BUILD.64 ❯❯❯ cmake .. -DBOARD=native_posix_64
Including boilerplate (Zephyr workspace): /usr/local/google/home/palchak/projects/zephyr-upstream/zephyr/cmake/app/boilerplate.cmake
-- Application: /usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs
-- Zephyr version: 2.7.0-rc3 (/usr/local/google/home/palchak/projects/zephyr-upstream/zephyr), build: v2.7.99
-- Found Python3: /usr/bin/python3.9 (found suitable exact version "3.9.7") found components: Interpreter 
-- Found west (found suitable version "0.11.0", minimum required is "0.7.1")
-- Board: native_posix_64
-- Cache files will be written to: /usr/local/google/home/palchak/.cache/zephyr
-- Found dtc: /usr/bin/dtc (found suitable version "1.6.0", minimum required is "1.4.6")
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /usr/local/google/home/palchak/projects/zephyr-upstream/zephyr/boards/posix/native_posix/native_posix_64.dts
-- Generated zephyr.dts: /usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64/zephyr/dts.cmake
Parsing /usr/local/google/home/palchak/projects/zephyr-upstream/zephyr/Kconfig
Loaded configuration '/usr/local/google/home/palchak/projects/zephyr-upstream/zephyr/boards/posix/native_posix/native_posix_64_defconfig'
Merged configuration '/usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/prj.conf'
Configuration saved to '/usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64/zephyr/.config'
Kconfig header saved to '/usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 11.2.0
-- The CXX compiler identification is GNU 11.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Deprecation Warning at /usr/local/google/home/palchak/projects/zephyr-upstream/modules/lib/civetweb/CMakeLists.txt:2 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


CMake Warning at /usr/local/google/home/palchak/projects/zephyr-upstream/zephyr/CMakeLists.txt:1643 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /usr/local/google/home/palchak/projects/zephyr-upstream/app/static_objs/BUILD.64
~/projects/zephyr-upstream/app/static_objs/BUILD.64 ❯❯❯ ninja
[1/99] Preparing syscall dependency handling

[94/99] Linking CXX executable zephyr/zephyr_prebuilt.elf

[99/99] Linking CXX executable zephyr/zephyr.elf
~/projects/zephyr-upstream/app/static_objs/BUILD.64 ❯❯❯ ./zephyr/zephyr.elf
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build v2.7.99  ***

As a sanity check, what happens if you build the executable without Zephyr at all? That is, change the signature of main to int main() and then build a simple executable:

gcc -o test.elf ../main.cc -lstdc++

I get the following output (as expected):

~/projects/zephyr-upstream/app/static_objs/BUILD.noz ❯❯❯ ./test.elf
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D

@palchak-google
Copy link
Contributor

Also, since you happen to be using GCC-11, take note of issue #40023.

@github-actions
Copy link

github-actions bot commented Jan 3, 2022

This issue has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 14 days. Note, that you can always re-open a closed issue at any time.

@wojciechslenska
Copy link
Contributor

Hello,
I observed this problem after upgrade of Ubuntu from 20.04 to 22.04. In tests I used static_obs example from this thread. Only change is that C++ is set to 17 instead of 20.

Ubuntu 20.04 GCC 9.4 -> works perfect

user@ubuntu20 /b/g/z/static_objs (develop)> west build -b native_posix -t run
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /build/griffin_root/zephyr_base/static_objs
-- Found Python3: /usr/bin/python3.8 (found suitable exact version "3.8.10") found components: Interpreter 
-- Cache files will be written to: /home/user/.cache/zephyr
-- Zephyr version: 3.0.99 (/build/griffin_root/zephyr_base/zephyr)
-- Found west (found suitable version "0.12.0", minimum required is "0.7.1")
-- Board: native_posix
-- Found dtc: /usr/bin/dtc (found suitable version "1.5.0", minimum required is "1.4.6")
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /build/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix.dts
-- Generated zephyr.dts: /build/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /build/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /build/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /build/griffin_root/zephyr_base/static_objs/build/zephyr/dts.cmake
Parsing /build/griffin_root/zephyr_base/zephyr/Kconfig
Loaded configuration '/build/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix_defconfig'
Merged configuration '/build/griffin_root/zephyr_base/static_objs/prj.conf'
Configuration saved to '/build/griffin_root/zephyr_base/static_objs/build/zephyr/.config'
Kconfig header saved to '/build/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /build/griffin_root/zephyr_base/zephyr/CMakeLists.txt:1803 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /build/griffin_root/zephyr_base/static_objs/build
-- west build: running target run
[1/102] Preparing syscall dependency handling

[2/102] Generating include/generated/version.h
-- Zephyr version: 3.0.99 (/build/griffin_root/zephyr_base/zephyr), build: zephyr-v3.0.0-2756-gc5dc184b7432
[97/102] Linking CXX executable zephyr/zephyr_pre0.elf

[101/102] Linking CXX executable zephyr/zephyr.elf

[101/102] cd /build/griffin_root/zephyr_base/static_objs/build && /build/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.exe
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build zephyr-v3.0.0-2756-gc5dc184b7432  ***

Ubuntu 22.04 GCC 11.2 -> ctors are not executed

user@ubuntu22 /m/w/c/g/z/static_objs (develop)> west build -b native_posix -t run
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /media/wsl/code/griffin_root/zephyr_base/static_objs
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.4") found components: Interpreter
-- Cache files will be written to: /home/user/.cache/zephyr
-- Zephyr version: 3.0.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr)
-- Found west (found suitable version "0.13.1", minimum required is "0.7.1")
-- Board: native_posix
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix.dts
-- Generated zephyr.dts: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/dts.cmake
Parsing /media/wsl/code/griffin_root/zephyr_base/zephyr/Kconfig
Loaded configuration '/media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix_defconfig'
Merged configuration '/media/wsl/code/griffin_root/zephyr_base/static_objs/prj.conf'
Configuration saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/.config'
Kconfig header saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 11.2.0
-- The CXX compiler identification is GNU 11.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /media/wsl/code/griffin_root/zephyr_base/zephyr/CMakeLists.txt:1803 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /media/wsl/code/griffin_root/zephyr_base/static_objs/build
-- west build: running target run
[1/102] Preparing syscall dependency handling

[2/102] Generating include/generated/version.h
-- Zephyr version: 3.0.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr), build: zephyr-v3.0.0-2756-gc5dc184b7432
[97/102] Linking CXX executable zephyr/zephyr_pre0.elf

[101/102] Linking CXX executable zephyr/zephyr.elf

[101/102] cd /media/wsl/code/griffin_root/zephyr_base/static_objs/build && /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.exe
*** Booting Zephyr OS build zephyr-v3.0.0-2756-gc5dc184b7432  ***

When I compiled and run this as normal application constructors works properly.

user@ubuntu22 /m/w/c/g/z/static_objs (develop)> gcc -o test.elf main.cc -lstdc++
user@ubuntu22 /m/w/c/g/z/static_objs (develop)> ./test.elf
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D

@stephanosio stephanosio reopened this Apr 27, 2022
@stephanosio stephanosio removed the Stale label Apr 27, 2022
@palchak-google
Copy link
Contributor

The differing results with different toolchains points indicates that this likely has something to do with a change in libstdc++. For reference, I used the following toolchain and library to build for native_posix, native_posix_64, and native Linux (non-Zephyr) targets:

  • gcc (Debian 11.2.0-16) 11.2.0
  • libstdc++-11-dev:amd64 11.2.0-16
  • libstdc++-11-dev:i386 11.2.0-16

These version are all installed via official Debian packages.

Re-building with the latest ToT for Zephyr again fails to reproduce for me:

Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build zephyr-v3.0.0-3121-gd490358e283f  ***

@wojciechslenska
Copy link
Contributor

I made some more checks in Ubuntu 22.04, looks like constructors works properly only in GCC 9.

wsl@T15 /m/w/c/g/z/static_objs (develop)> sudo apt install gcc-9 g++-9 g++-9-multilib
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
g++-9 is already the newest version (9.4.0-5ubuntu1).
g++-9-multilib is already the newest version (9.4.0-5ubuntu1).
gcc-9 is already the newest version (9.4.0-5ubuntu1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
wsl@T15 /m/w/c/g/z/static_objs (develop)> sudo update-alternatives --set gcc /usr/bin/gcc-9
update-alternatives: using /usr/bin/gcc-9 to provide /usr/bin/gcc (gcc) in manual mode
wsl@T15 /m/w/c/g/z/static_objs (develop)> sudo update-alternatives --set g++ /usr/bin/g++-9
update-alternatives: using /usr/bin/g++-9 to provide /usr/bin/g++ (g++) in manual mode
wsl@T15 /m/w/c/g/z/static_objs (develop)> gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-5ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-bVKGhJ/gcc-9-9.4.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-5ubuntu1) 
wsl@T15 /m/w/c/g/z/static_objs (develop)> rm build -fr
wsl@T15 /m/w/c/g/z/static_objs (develop)> west build -b native_posix -t run
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /media/wsl/code/griffin_root/zephyr_base/static_objs
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.4") found components: Interpreter 
-- Cache files will be written to: /home/wsl/.cache/zephyr
-- Zephyr version: 3.0.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr)
-- Found west (found suitable version "0.13.1", minimum required is "0.7.1")
-- Board: native_posix
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix.dts
-- Generated zephyr.dts: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/dts.cmake
Parsing /media/wsl/code/griffin_root/zephyr_base/zephyr/Kconfig
Loaded configuration '/media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix_defconfig'
Merged configuration '/media/wsl/code/griffin_root/zephyr_base/static_objs/prj.conf'
Configuration saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/.config'
Kconfig header saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /media/wsl/code/griffin_root/zephyr_base/zephyr/CMakeLists.txt:1803 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /media/wsl/code/griffin_root/zephyr_base/static_objs/build
-- west build: running target run
[1/102] Preparing syscall dependency handling

[3/102] Generating include/generated/version.h
-- Zephyr version: 3.0.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr), build: zephyr-v3.0.0-3163-g7ad2e604bb31
[97/102] Linking CXX executable zephyr/zephyr_pre0.elf

[101/102] Linking CXX executable zephyr/zephyr.elf

[101/102] cd /media/wsl/code/griffin_root/zephyr_base/static_objs/build && /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.exe
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build zephyr-v3.0.0-3163-g7ad2e604bb31  ***

For any other GCC (10, 11, 12) from ubuntu 22.04 constructors are not executed. In this example I used 10, but 11 and 12 works exactly the same.

wsl@T15 /m/w/c/g/z/static_objs (develop)> sudo apt install gcc-10 g++-10 g++-10-multilib
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
g++-10 is already the newest version (10.3.0-15ubuntu1).
g++-10-multilib is already the newest version (10.3.0-15ubuntu1).
gcc-10 is already the newest version (10.3.0-15ubuntu1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
wsl@T15 /m/w/c/g/z/static_objs (develop)> sudo update-alternatives --set gcc /usr/bin/gcc-10
update-alternatives: using /usr/bin/gcc-10 to provide /usr/bin/gcc (gcc) in manual mode
wsl@T15 /m/w/c/g/z/static_objs (develop)> sudo update-alternatives --set g++ /usr/bin/g++-10
update-alternatives: using /usr/bin/g++-10 to provide /usr/bin/g++ (g++) in manual mode
wsl@T15 /m/w/c/g/z/static_objs (develop)> gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/10/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 10.3.0-15ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-mutex
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.3.0 (Ubuntu 10.3.0-15ubuntu1) 
wsl@T15 /m/w/c/g/z/static_objs (develop)> rm build -fr
wsl@T15 /m/w/c/g/z/static_objs (develop)> west build -b native_posix -t run
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /media/wsl/code/griffin_root/zephyr_base/static_objs
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.4") found components: Interpreter 
-- Cache files will be written to: /home/wsl/.cache/zephyr
-- Zephyr version: 3.0.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr)
-- Found west (found suitable version "0.13.1", minimum required is "0.7.1")
-- Board: native_posix
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix.dts
-- Generated zephyr.dts: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/dts.cmake
Parsing /media/wsl/code/griffin_root/zephyr_base/zephyr/Kconfig
Loaded configuration '/media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix_defconfig'
Merged configuration '/media/wsl/code/griffin_root/zephyr_base/static_objs/prj.conf'
Configuration saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/.config'
Kconfig header saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 10.3.0
-- The CXX compiler identification is GNU 10.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /media/wsl/code/griffin_root/zephyr_base/zephyr/CMakeLists.txt:1803 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /media/wsl/code/griffin_root/zephyr_base/static_objs/build
-- west build: running target run
[1/102] Preparing syscall dependency handling

[2/102] Generating include/generated/version.h
-- Zephyr version: 3.0.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr), build: zephyr-v3.0.0-3163-g7ad2e604bb31
[97/102] Linking CXX executable zephyr/zephyr_pre0.elf

[101/102] Linking CXX executable zephyr/zephyr.elf

[101/102] cd /media/wsl/code/griffin_root/zephyr_base/static_objs/build && /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.exe
*** Booting Zephyr OS build zephyr-v3.0.0-3163-g7ad2e604bb31  ***

Exactly the same behavior I observed for native_posix and native_posix_64

Nevertheless current behavior, when constructors are not executed directly after start of the application, and after reverting of this commit d10b46d is matching to this Enhancement #40444

@palchak-google
Copy link
Contributor

Commit d10b46d addresses a defect in Zephyr reported in #39347. However, further investigation has revealed that d10b46d does not correctly resolve the root cause of the issue. Additionally, d10b46d is currently masking a second, latent defect in Zephyr.

Background

To understand the root cause of the issue, it's necessary to have familiarity with the start-up sequence for processes on Linux. This article provides a great explanation of each of the steps in detail.

Root cause of #36858

The root cause of #36858 lies in the interaction between the link script used by Zephyr for native_posix targets and the start-up sequence for glibc. To understand what's happening it helps to look at the map file for a native_posix binary. Here is the map for a binary built on my system using the example code from #36858.

.init_array     0x000000000040bd90        0x0
                [!provide]                        PROVIDE (__init_array_start = .)
 *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))
 *(SORT_BY_ALIGNMENT(.init_array) SORT_BY_ALIGNMENT(EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors))
                [!provide]                        PROVIDE (__init_array_end = .)

.ctors
 *crtbegin.o(SORT_BY_ALIGNMENT(.ctors))
 *crtbegin?.o(SORT_BY_ALIGNMENT(.ctors))
 *(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))
 *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.ctors.*)))
 *(SORT_BY_ALIGNMENT(.ctors))

ctors           0x00000000080532c8       0x14
                0x00000000080532c8                . = ALIGN (0x4)
                0x00000000080532c8                __CTOR_LIST__ = .
                0x00000000080532c8        0x4 LONG 0x3 (((__CTOR_END__ - __CTOR_LIST__) / 0x4) - 0x2)
 *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.ctors*)))
 .ctors         0x00000000080532cc        0x4 /usr/lib/gcc/x86_64-linux-gnu/11/32/crtbegin.o
 .ctors         0x00000000080532d0        0x4 app/libapp.a(main.cc.obj)
 .ctors         0x00000000080532d4        0x4 /usr/lib/gcc/x86_64-linux-gnu/11/32/crtend.o
                0x00000000080532d8        0x4 LONG 0x0
                0x00000000080532dc                __CTOR_END__ = .

init_array      0x00000000080532dc        0x0
                0x00000000080532dc                . = ALIGN (0x4)
                0x00000000080532dc                __init_array_start = .
 *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.init_array*)))
                0x00000000080532dc                __init_array_end = .

Note here that the ctors section ends up containing five quad words:

$ > readelf -x ctors zephyr.elf

Hex dump of section 'ctors':
  0x080532c8 03000000 ffffffff 90960408 00000000 ................
  0x080532d8 00000000                            ....

The first word (0x03) and the last word (0x0) are inserted by Zephyr's link script (the QUAD directives in the map file). The middle three words come from crtbegin.o, main.cc.obj, and crtend.o. The word from crtbegin.o is a value of -1, the word from crtend.o is 0x0, and the word from main.cc.obj is 0x08049690 which is the address of the initialization function that invokes the constructors for our static objects:

$> objdump -t zephyr.elf | grep "08049690" -  
08049690 l     F .text	000000c4              _GLOBAL__sub_I_zephyr_app_main

It's also important to note that there are two symbols in the binary, both named __CTOR_LIST__, that point into the ctors section:

$> objdump -t zephyr.elf | grep "CTOR_LIST" -
080532cc l     O ctors	00000000              __CTOR_LIST__
080532c8 g       ctors	00000000              __CTOR_LIST__

The first symbol refers to a local object (the 'l' and 'O' flags) at address 0x080532cc, which is the address of the value -1 that was contributed by crtbegin.o. The second symbol is a global symbol referring to the 0x03 inserted by Zephyr's link script.

The final piece of the puzzle lies in the _init function for this native_posix executable:

$> objdump -dj .init zephyr.elf 

zephyr.elf:     file format elf64-x86-64

Disassembly of section .init:

0000000000402000 <_init>:
  402000:	48 83 ec 08          	sub    $0x8,%rsp
  402004:	48 8b 05 dd 9f 00 00 	mov    0x9fdd(%rip),%rax        # 40bfe8 <__gmon_start__@Base>
  40200b:	48 85 c0             	test   %rax,%rax
  40200e:	74 02                	je     402012 <_init+0x12>
  402010:	ff d0                	call   *%rax
  402012:	e8 49 04 00 00       	call   402460 <frame_dummy>
  402017:	e8 84 63 00 00       	call   4083a0 <__do_global_ctors_aux>
  40201c:	48 83 c4 08          	add    $0x8,%rsp
  402020:	c3                   	ret    

The _init function makes a direct call to the __do_global_ctors_aux function, consistent with the normal start-up sequence for a Linux process. And __do_global_ctors_aux in turn iterates through the list denoted by the __CTOR_LIST__ symbol and calls each function pointer contained in the list. All of this happens before control passes to Zephyr's init code. When Zephyr's init code runs it repeats the process of iterating through the __CTOR_LIST__ (Zephyr provides its own implementation of __do_global_ctors_aux), resulting in duplicate calls to all static object constructors. This explains the behavior reported in #36858. This also explains why commit d10b46d appeared to fix the issue: it eliminated a call to Zephyr's __do_global_ctors_aux function and thus prevented duplicate constructor calls.

Root cause of #39347

Commit d10b46d fixed the Zephyr start-up process on some systems but broke the start-up process on others. An understanding as to why is provided again by the map file. This excerpt is taken from the map file for the native_posix binary provided by aleksei-timofeyev in an earlier comment:

ctors           0x0000000000411608       0x10
                0x0000000000411608                . = ALIGN (0x8)
                0x0000000000411608                __CTOR_LIST__ = .
                0x0000000000411608        0x8 QUAD 0x0 (((__CTOR_END__ - __CTOR_LIST__) / 0x8) - 0x2)
 *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.ctors*)))
                0x0000000000411610        0x8 QUAD 0x0
                0x0000000000411618                __CTOR_END__ = .

init_array      0x0000000000411618       0x10
                0x0000000000411618                . = ALIGN (0x4)
                0x0000000000411618                __init_array_start = .
 *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.init_array*)))
 .init_array    0x0000000000411618        0x8 /usr/lib/gcc/x86_64-redhat-linux/11/crtbegin.o
 .init_array    0x0000000000411620        0x8 app/libapp.a(main.cpp.obj)
                0x0000000000411628                __init_array_end = .

On Aleksei's system, the ctors section in the final binary is empty, while the init_array section contains the pointer to the initialization function for the static objects. Also note that Aleksei built 64-bit executables, so the words are 8 bytes long.

$> readelf -x ctors zephyr.elf 

Hex dump of section 'ctors':
  0x00411608 00000000 00000000 00000000 00000000 ................

$> readelf -x init_array zephyr.elf

Hex dump of section 'init_array':
  0x00411618 60244000 00000000 f2354000 00000000 `$@......5@.....

$> objdump -t zephyr.elf| grep "402460" -
0000000000402460 l     F .text	0000000000000000              frame_dummy

$> objdump -t zephyr.elf| grep "4035f2" - 
00000000004035f2 l     F .text	0000000000000015              _GLOBAL__sub_I_main.cpp

The other critical detail is that the _init function on Aleksei's system is different:

$> objdump -dj .init zephyr.elf

zephyr.elf:     file format elf64-x86-64

Disassembly of section .init:

0000000000402000 <_init>:
  402000:	f3 0f 1e fa          	endbr64 
  402004:	48 83 ec 08          	sub    $0x8,%rsp
  402008:	48 8b 05 e9 ef 00 00 	mov    0xefe9(%rip),%rax        # 410ff8 <__gmon_start__@Base>
  40200f:	48 85 c0             	test   %rax,%rax
  402012:	74 02                	je     402016 <_init+0x16>
  402014:	ff d0                	call   *%rax
  402016:	48 83 c4 08          	add    $0x8,%rsp
  40201a:	c3                   	ret    

On Aleksei's sytem, the _init function does not directly call __do_global_constructors_aux. This means that on Aleksei's sytem, no global constructors run before control is passed to Zephyr's init code. And since commit d10b46d disables Zephyr's execution of all initialization functions, on Aleksei's sytem the static object constructors never run.

Different behavior on different systems

The difference in process start-up behavior between my system and Aleksei's system can be attributed to significant changes that were introduced to glibc starting with version 2.34. Many of the changes are explained and motivated in this StackOverflow answer. The one change that has a critical impact on this issue is the elimination of the direct call to __do_global_constructors_aux within the _init function.

Without commit d10b46d, on systems with glibc < 2.34 the call to __do_global_constructors_aux in _init causes global constructors to run twice. On systems with glibc >= 2.34, global constructors run exactly once during Zephyr's init.

With commit d10b46d, on systems with glibc < 2.34 global constructors run exactly once prior to Zephyr's init, and on systems with glibc >= 2.34 global constructors never run at all.

Glibc v2.34 was officially released in August of 2021, though some distributions adopted it earlier. It seems all but certain that Ubuntu 20.04 included a version of glibc prior older than 2.34 while Ubuntu 22.04 uses a version of glibc >= 2.34. This explains wojciechslenska's results.

Fixing both #39347 and #36858

The solution that works for both systems using glibc < 2.34 and systems using glibc >= 2.34 is to revert commit d10b46d and instead modify the Zephyr link script (for native_posix platforms) to split up the the contents of the .ctors section into two different lists.

The first list contains only the symbols provided by the crtbegin.o and crtend.o objects. Including only the symbols from crtbegin.o and crtend.o ensures that this list always remains empty. The address of this empty list is assigned to the __CTOR_LIST__ symbol. For systems with glibc < 2.34 the call to __do_global_ctors_aux in _init processes this empty list, and because it's empty no initialization functions actually run. For systems with glibc >= 2.34 this empty list is simply never used.

The second list contains all of the remaining symbols from the .ctors sections, that is, all .ctors* symbols except for those provided by crtbegin.o and crtend.o objects. This address of this second list is assigned to a newly invented symbol called __ZEPYHYR_CTOR_LIST__. Only Zephyr is aware of this second list, and thus only Zephyr's init code calls the functions included in this list. For systems with glibc < 2.34, this ensures that initialization functions in .ctors sections run exactly once during Zephyr's start-up. For systems with glibc >= 2.34, initialization functions are placed in .init_array sections instead of .ctors sections, meaning this second list is also always empty. As a result on these systems the call to Zephyr's __do_global_ctors_aux has no effect.

Finally, one important feature of this solution that is not present in commit d10b46d is that with this solution static object constructors run as part of Zephyr's start-up routine (instead of before Zephyr). This means that static object constructors run after kernel and driver initialization, exactly as occurs on non-posix platforms. This sequencing is important, as constructors may interact with the kernel or with drivers. It is certainly a defect for any such interactions to occur before the kernel and drivers have been initialized, as is possible today with commit d10b46d.

I will work on producing a patch that implements this solution in the coming weeks. If someone else wants to take a shot at implementation in the mean time, please do.

Coda

At the beginning I mentioned that commit d10b46d is currently masking a latent defect in Zephyr. Recall the relevant map file section for and contents of the ctors section for the native_posix executable built on my system:

ctors           0x00000000080532c8       0x14
                0x00000000080532c8                . = ALIGN (0x4)
                0x00000000080532c8                __CTOR_LIST__ = .
                0x00000000080532c8        0x4 LONG 0x3 (((__CTOR_END__ - __CTOR_LIST__) / 0x4) - 0x2)
 *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.ctors*)))
 .ctors         0x00000000080532cc        0x4 /usr/lib/gcc/x86_64-linux-gnu/11/32/crtbegin.o
 .ctors         0x00000000080532d0        0x4 app/libapp.a(main.cc.obj)
 .ctors         0x00000000080532d4        0x4 /usr/lib/gcc/x86_64-linux-gnu/11/32/crtend.o
                0x00000000080532d8        0x4 LONG 0x0
                0x00000000080532dc                __CTOR_END__ = .

$ > readelf -x ctors zephyr.elf

Hex dump of section 'ctors':
  0x080532c8 03000000 ffffffff 90960408 00000000 ................
  0x080532d8 00000000  

The __do_global_constructors_aux function expects the __CTOR_LIST__ symbol to point to a list of pointers to initialization functions. Furthermore, that list is expected to have a specific binary format, as documented here. To summarize the format, the first entry of the list is always ignored, and it may contain either 0, -1, or a count of the number of function pointers following it. The last entry of the list must be the value zero (aka a null function pointer).

Looking closely at the initialization function list in the final binary we see that it contains five entries:

{
    [0] = 0x03
    [1] = 0xFFFFFFFF
    [2] = 0x08049690
    [3] = 0x0
    [4] = 0x0 
}

The first entry indicates that the list contains three valid pointers, and the last entry is the trailing zero. However, entry[1], which is supposedly a function pointer, has the value -1. Similarly, entry[3] is a nullptr. Only the entry[2] is a valid function pointer. What Zephyr's link script has done is take a valid list (entries 1-3) and wrapped with with a leading size (0x03) and a trailing zero, resulting in a final list that contains invalid pointers. And indeed, if commit d10b46d is reverted, Zephyr's version of __do_global_ctors_aux produces a segfault when it tries to deference the entry[1]:

$> ./zephyr.elf
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D
*** Booting Zephyr OS build zephyr-v3.0.0-3121-gd490358e283f  ***
[1]    41238 segmentation fault  ./zephyr.elf

Notice in this output that even though Zephyr generates a __CTOR_LIST__ with invalid entries, the static object constructors are correctly executed before Zephyr's init code runs. The explanation for why the call to __do_global_ctors_aux in _init succeeds while Zephyr's version of __do_global_ctors_aux fails lies in the symbol table:

$> objdump -t zephyr.elf | grep "CTOR_LIST" -
080532cc l     O ctors	00000000              __CTOR_LIST__
080532c8 g       ctors	00000000              __CTOR_LIST__

The executable contains two symbols named __CTOR_LIST__ that point to different addresses. The __do_global_ctors_aux function called from _init references the first symbol, the one that describes a local object. That symbol represents address 0x080532cc, which corresponds to the entry[1]. When entry[1] is treated as the start of the list (instead of entry[0]) the code does not encounter any invalid entries prior to the (first) trailing null.

In contrast, Zephyr's version of __do_global_ctors_aux references the second symbol, the global version. This symbol points to entry[0]. Thus when Zephyr's __do_global_ctors_aux attempts to dereference entry[1] as a function pointer a fault occurs.

Because the proposed fix for #39347 and #36858 will also eliminate this latent defect, it doesn't seem necessary to file a separate bug report for it. However, if others disagree, then please feel free to copy this coda into a stand-alone report.

palchak-google added a commit to palchak-google/zephyr that referenced this issue Jun 8, 2022
Rename the symbols used to denote the locations of the global
constructor lists and modify the Zephyr start-up code accordingly.
On POSIX systems this ensures that the native libc init code won't
find any constructors to run before Zephyr loads.

Fixes zephyrproject-rtos#39347, zephyrproject-rtos#36858

Signed-off-by: David Palchak <palchak@google.com>
@palchak-google
Copy link
Contributor

@aleksei-timofeyev
@wojciechslenska

I have submitted a fix for this issue. It works as best as I have been able to test. Will you please confirm the fix works properly on your system(s)?
#46357

carlescufi pushed a commit that referenced this issue Jun 9, 2022
Rename the symbols used to denote the locations of the global
constructor lists and modify the Zephyr start-up code accordingly.
On POSIX systems this ensures that the native libc init code won't
find any constructors to run before Zephyr loads.

Fixes #39347, #36858

Signed-off-by: David Palchak <palchak@google.com>
@wojciechslenska
Copy link
Contributor

@palchak-google
Hello,
I can confirm that now constructors are working properly on Ubuntu 22.04 on GCC 10.3.0

wsl@T15 /m/w/c/g/z/static_objs (wsl/database_test)> west build -b native_posix -t run
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /media/wsl/code/griffin_root/zephyr_base/static_objs
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.4") found components: Interpreter 
-- Cache files will be written to: /home/wsl/.cache/zephyr
-- Zephyr version: 3.1.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr)
-- Found west (found suitable version "0.13.1", minimum required is "0.7.1")
-- Board: native_posix
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix.dts
-- Generated zephyr.dts: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/dts.cmake
Parsing /media/wsl/code/griffin_root/zephyr_base/zephyr/Kconfig
Loaded configuration '/media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix_defconfig'
Merged configuration '/media/wsl/code/griffin_root/zephyr_base/static_objs/prj.conf'
Configuration saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/.config'
Kconfig header saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 10.3.0
-- The CXX compiler identification is GNU 10.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /media/wsl/code/griffin_root/zephyr_base/zephyr/CMakeLists.txt:1809 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /media/wsl/code/griffin_root/zephyr_base/static_objs/build
-- west build: running target run
[1/102] Preparing syscall dependency handling

[2/102] Generating include/generated/version.h
-- Zephyr version: 3.1.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr), build: zephyr-v3.1.0-346-gb9e833366f13
[97/102] Linking CXX executable zephyr/zephyr_pre0.elf

[101/102] Linking CXX executable zephyr/zephyr.elf

[101/102] cd /media/wsl/code/griffin_root/zephyr_base/static_objs/build && /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.exe
*** Booting Zephyr OS build zephyr-v3.1.0-346-gb9e833366f13  ***
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D

and GCC 12.0.1

wsl@T15 /m/w/c/g/z/static_objs (wsl/database_test)> west build -b native_posix -t run
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /media/wsl/code/griffin_root/zephyr_base/static_objs
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.4") found components: Interpreter 
-- Cache files will be written to: /home/wsl/.cache/zephyr
-- Zephyr version: 3.1.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr)
-- Found west (found suitable version "0.13.1", minimum required is "0.7.1")
-- Board: native_posix
-- Found toolchain: host (gcc/ld)
-- Found BOARD.dts: /media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix.dts
-- Generated zephyr.dts: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/devicetree_unfixed.h
-- Generated device_extern.h: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/device_extern.h
-- Including generated dts.cmake file: /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/dts.cmake
Parsing /media/wsl/code/griffin_root/zephyr_base/zephyr/Kconfig
Loaded configuration '/media/wsl/code/griffin_root/zephyr_base/zephyr/boards/posix/native_posix/native_posix_defconfig'
Merged configuration '/media/wsl/code/griffin_root/zephyr_base/static_objs/prj.conf'
Configuration saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/.config'
Kconfig header saved to '/media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 12.0.1
-- The CXX compiler identification is GNU 12.0.1
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/gcc
CMake Warning at /media/wsl/code/griffin_root/zephyr_base/zephyr/CMakeLists.txt:1809 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: /media/wsl/code/griffin_root/zephyr_base/static_objs/build
-- west build: running target run
[1/102] Preparing syscall dependency handling

[2/102] Generating include/generated/version.h
-- Zephyr version: 3.1.99 (/media/wsl/code/griffin_root/zephyr_base/zephyr), build: zephyr-v3.1.0-346-gb9e833366f13
[97/102] Linking CXX executable zephyr/zephyr_pre0.elf

[101/102] Linking CXX executable zephyr/zephyr.elf

[101/102] cd /media/wsl/code/griffin_root/zephyr_base/static_objs/build && /media/wsl/code/griffin_root/zephyr_base/static_objs/build/zephyr/zephyr.exe
*** Booting Zephyr OS build zephyr-v3.1.0-346-gb9e833366f13  ***
Constructing Object A
Constructing Object B
Constructing Object C
Constructing Object D

I see the same behavior on native_posix and native_posix_64
Many thanks for your contribution

wcappelle pushed a commit to wcappelle/zephyr that referenced this issue Oct 7, 2022
Rename the symbols used to denote the locations of the global
constructor lists and modify the Zephyr start-up code accordingly.
On POSIX systems this ensures that the native libc init code won't
find any constructors to run before Zephyr loads.

Fixes zephyrproject-rtos#39347, zephyrproject-rtos#36858

Signed-off-by: David Palchak <palchak@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: C++ bug The issue is a bug, or the PR is fixing a bug priority: low Low impact/importance bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants