Skip to content

Protobuf's constant initialization is not actually constant on Windows #10159

@davidben

Description

@davidben

What version of protobuf and what language are you using?
Version: main
Language: C++

What operating system (Linux, Windows, ...) and version?
Windows 10 20H2

What runtime / compiler are you using (e.g., python version or gcc version)
clang-cl from Visual Studio 2019

What did you do?
Steps to reproduce the behavior:

  1. Install Visual Studio 2019
  2. In the installer, include "C++ Clang tools for Windows"
  3. git clone https://github.com/protocolbuffers/protobuf.git
  4. cd protobuf
  5. git submodule update --init --recursive
  6. cmake -B build-shared -D BUILD_SHARED_LIBS=1 -T ClangCL
  7. cmake --build build-shared --parallel 16

What did you expect to see
The build should succeed

What did you see instead?
The build fails with errors like:

...\protobuf\src\google\protobuf\compiler\plugin.pb.cc(75,31): message : required by 'require_constant_initializatio
n' attribute here [...\protobuf\build-shared\libprotoc.vcxproj]
...\protobuf\src\google/protobuf/port_def.inc(656,30): message : expanded from macro 'PROTOBUF_CONSTINIT' [...\pr
otobuf\build-shared\libprotoc.vcxproj]
...\protobuf\src\google\protobuf\compiler\plugin.pb.cc(91,125): error : variable does not have a constant initialize
r [...\protobuf\build-shared\libprotoc.vcxproj]

Anything else we should know about your project / environment
This was an issue updating protobuf in Chromium. We've applied a workaround by patching out the PROTOBUF_CONSTINIT in some configurations, but the root problem is that protobuf's new constant initialization was not actually cross-platform, and relies on assumption that aren't actually universally true.

While, for the repro steps, I used clang-cl, this is only because protobuf has a pre-C++20 version of PROTOBUF_CONSTINIT for Clang, [[clang::require_constant_initialization]]. When protobuf moves to C++20, I expect it will have the same problem on MSVC.

This happens because the protobuf constant initialization assumes it can reference &fixed_address_empty_string. fixed_address_empty_string lives in the protobuf dll, so when referenced from another dll or exe, is __declspec(dllimport). But pointers to dllimport variables require a static initializer on Windows. See https://godbolt.org/z/5rn4xWrhE. As I understand it, Windows does not have a suitable relocation to express this.

As a result, the new constant initialization actually requires a per-message static initializer, which trips PROTOBUF_CONSTINIT and fails to build.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions