Multiple construction and destruction of static variable in multiple translation units

In C++11 code I have a static variable with a nontrivial constructor and destructor (which allocates memory):

// in logger.h
class logger {
public:
    class log_data
    {
        // log_data allocates and frees memory during construction/destruction
    }

    static log_data l;
};
// in logger.cpp
logger::log_data logger::l;

logger.cpp is then compiled into both a shared library and an executable which links to the shared library itself. I expect the linker to recognize that logger::l is compiled twice and “merge” the two copies.

The problem is that, at runtime, this is what I think is happening:

  1. The executable initializes logger::l on a given memory address, before main() is called, by calling the constructor
  2. The library does the same, on the same memory address, overwriting it
  3. main() is normally executed
  4. The library deletes the object
  5. The executable deletes the object at the same address

I am pretty sure that’s the case because:

  • on x86, valgrind detects that a block of memory in log_data constructor has been allocated and it is definitely lost at program exit (which makes sense since the first initialization is overwritten by the second). For some reason, it does not complain about double destruction at program exit
  • on armv7, the program segfaults after main() is completed after printing “double free or corruption (fasttop)”. I do not have valgrind available on that platform to double check the allocation, but gdb reports a corrupt stack after main exits.

My question is: is there a way to tell the compiler that a static variable has to be constructed and destructed only once, no matter in how many libraries or executable is compiled? Are there any other solutions?

I tried removing logger.cpp from the executable compilation, and linking it with only 1 shared library which includes logger.cpp. It properly works, but the problem remains because logger.cpp can be included in multiple shared libraries, all used by the same executable

Thank you

7

Both the executable and shared libraries will get their own logger::log_data logger::l; static variable. You can confirm this with a tool like nm.

I’ve set up a basic local reproduction with the following:

// foo.hpp
void foo();

// foo.cpp
#include "foo.hpp"

void foo() {}

// log.hpp
#include <iostream>

class logger {
public:
    logger() {
        std::cout<<"logger()"<<std::endl;
    }
    ~logger() {
        std::cout<<"~logger()"<<std::endl;
    }
    void log() {}
};

extern logger logger_global;

// log.cpp
#include "log.hpp"

logger logger_global;

// log_internal.cpp
#include "log.hpp"

void logger::log() {}

// main.cpp
#include <iostream>

#include "log.hpp"
#include "foo.hpp"

int main() {
    std::cout<<"main "<<std::endl;
    logger_global.log();
    foo();
}

// CMakeLists.txt
cmake_minimum_required(VERSION 3.14)

project(test LANGUAGES C CXX)

add_library(foo SHARED log.cpp foo.cpp)

add_executable(exec main.cpp log.cpp)
target_link_libraries(exec PUBLIC foo)

Running the executable:

$ ./exec
logger()
logger()
main 
~logger()
~logger()

Looking at nm:

$ nm exec | grep logger_global
0000000000001244 t _GLOBAL__sub_I_logger_global
0000000000004151 B logger_global
$ nm libfoo.so | grep logger_global
0000000000004031 B logger_global

Both the executable and the library get their own logger_global and each does its own static construction and destruction. Unlike a static library, the linker can’t grab only what is needed from the shared library. The entire shared library must be loaded into memory and as part of that its static data must be initialized. Running in a debugger, you can verify that logger::logger is called twice with the same this pointer.

You may wonder what _GLOBAL__sub_I_logger_global is and why only the executable has it. This function is automatically generated by the compiler and performs static initialization for the global logger. The shared library actually has one too, but, it has a different name: _GLOBAL__sub_I_log.cpp.

What to do?

You should pull the common logger code out into its own library and have both your library and executable link the logger library.

3

For the double destruction, Valgrind memcheck doesn’t care about destructors. It redirects all allocation and deallocation functions (malloc family and operators new and delete), instruments all memory reads and writes and also wraps all system calls. Of course if multiple calls to a destructor lead to multiple deletes of the same memory then errors will be reported.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật