1

I am trying to write a program on C++ but without using libstdc++. My program uses some header-only templates, and also it uses allocate_shared, to which I supply the custom allocator to avoid operators new and delete.

My problem is that I can only get rid of operator new. Operator delete is still referenced in the resulting object file.

#include <cstdlib>
#include <memory>

template <class T>
struct Mallocator {
  typedef T value_type;
  Mallocator() = default;
  template <class U> constexpr Mallocator(const Mallocator<U>&) noexcept {}
  T* allocate(std::size_t n) {
    return (T*)std::malloc(n*sizeof(T));
  }
  void deallocate(T* p, std::size_t) noexcept { std::free(p); }
};
template <class T, class U>
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }

struct A {
    std::shared_ptr<int> a;
    A(const std::shared_ptr<int> b) : a(b) {}
};

int main()
{
    A a(std::allocate_shared<int>(Mallocator<int>(), 5));
    return 0;
}

Lets compile:

$ c++ -c malloca.cpp

And see what's there:

$ nm -u malloca.o | c++filt | grep new
$ nm -u malloca.o | c++filt | grep delete
U operator delete(void*, unsigned long)

Operator delete is still there, despite the custom allocator being provided. How can I get rid of it?

stsp
  • 308
  • 1
  • 6
  • 8
    "I am trying to write a program on C++ but without using libstdc++" - Why? This seems, on the surface, like a very silly/stupid thing to do. Please explain *why* you are doing this. – Jesper Juhl Feb 21 '18 at 21:38
  • 1
    consider for the sake of discussion that you're relying on functionality that might itself have to allocate. That functionality could be using `new` and `delete`. That said this seems very silly. – Mgetz Feb 21 '18 at 21:49
  • @JesperJuhl for example, because libstdc++ is not available on target platform? – SergeyA Feb 21 '18 at 21:54
  • What is the point of this exercise? Tell me it's not a benighted instructor's assignment. Even if standard lib is not available on the platform, there's no reason not to use `new` and `delete`. – Jive Dadson Feb 21 '18 at 22:02
  • @SergeyA if that was the case I very much would expect OP to have stated that up front. – Jesper Juhl Feb 21 '18 at 22:03
  • 2
    You can override globals `::operator new` and `::operator delete` if that helps. Please answer, "What's the point?" – Jive Dadson Feb 21 '18 at 22:05
  • You're soaking in it. Chances border on 100% that `new` and `delete` are based on `malloc` and `free`. – Jive Dadson Feb 21 '18 at 22:09
  • Firstly, I suspect this may be a bug. I wouldn't normally expect delete to be referenced if the custom allocator/deallocator is provided. Secondly, what I do is a small templated wrapper that I plug into a purely C program where size matters. I'd like to get zero overhead, and linking with libstdc++ just for the _dead_ operator delete looks quite useless. And yes, overriding global operator delete works. Thanks for suggestions. – stsp Feb 21 '18 at 23:47
  • http://www.avabodh.com/cxxin/nostdlib.html I followed all suggestions of this article. I really assume that working w/o a standard library is perfectly fine and supported. – stsp Feb 21 '18 at 23:57
  • @stsp only in very limited cases, but most language features are not available because the runtime is not initialized without the stdlib present. – Mgetz Feb 22 '18 at 13:33
  • @Mgetz I expect all language features to be available (modulo RTTI and exceptions that I explicitly disabled), and most of the STL, too, as they are header-only. This worked very well (i.e. templates, polymorphism, STL - all worked) till I started to use memory management. So its really not something unusual. Its still quite feature-rich and should work properly. – stsp Feb 23 '18 at 14:36
  • @stsp a 'freestanding' use of C++ only has access to a few headers: `` `` and I think three others that don't depend on any runtime. Anything depending on runtime: RTTI, exceptions, all require a runtime environment which would require libstdc++ or libc++. Alternatively you can supply your own implementations, which some OS kernel developers have done. https://wiki.osdev.org/C++ has more information on freestanding environment requirements. – Mgetz Feb 23 '18 at 15:07
  • @Mgetz your understanding contradicts with an article that I referred to above. Here it is again: avabodh.com/cxxin/nostdlib.html I think "freestanding" use is a bit more restricted than what I do. It assumes you have no standard libraries at all, neither libc nor libstdc++. My use is to have libc and all STL headers. So its really much richer than freestanding would allow. – stsp Feb 23 '18 at 15:28

1 Answers1

3

I believe, the call of operator delete there is a peculiarity of GCC STL implementation.

Looking at the (rather convoluted) code of shared_ptr_base.h, I can see that _Sp_counted_base will call delete from it's _M_destroy function. This function is overridden in _Sp_counted_ptr_inplace (the one you end up using with allocate_shared), so this code is never executed. But it's presence there provides unresolved symbol.

Overriding global operator delete get's rid of it.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Thanks. I provided an empty inline version of operator delete and added -Wno-inline-new-delete - that helped. Still I wonder if it is unsolvable by some other means, like dead code elimination or the like... – stsp Feb 21 '18 at 23:18
  • @stsp you need to look into a "standalone" build, that will block you from using almost all the stdlib however. It's generally used to build operating system kernels. – Mgetz Feb 22 '18 at 13:32
  • @stsp dead code elimination might help, but for this you need to use the link time optimization. I never used it myself, but here is one SO question I found with quick search: https://stackoverflow.com/questions/31688069/requirements-to-use-flto – SergeyA Feb 22 '18 at 14:35
  • Does anyone think this may be a bug worth reporting to the glibc or gcc team? My feeling is that the never used, overloaded call should not propagate to the final object. But I don't know who should be responsible for this, libstdc++ itself or the compiler's optimizer? Maybe libstdc++ should have used std::enable_if to conditionalize it, or is it a task of the dead code elimination? I am coming from an assumption that if you explicitly want to disable libstdc++, you have guidelines and compiler options to do exactly that. But in this particular case they don't work as expected. – stsp Feb 23 '18 at 14:44
  • Doesn't seem like a bug in libstdc++ at all (where is the bug??) and as for compiler, lack of optimization is never a bug. You can switch to Clang and it's own libc++. – SergeyA Feb 23 '18 at 15:55
  • The bug can be suspected if working w/o libstdc++ is a valid and supported scenario, which I assume it is. Then it is obviously a bug as this scenario doesn't work. If this is not supported then its just a missing optimization. clang++ -std=c++11 -stdlib=libc++ produces the same problem. – stsp Feb 23 '18 at 16:37
  • This is weird. When I examined libc++ implementation, I didn't find the delete call. However, I might have missed it, I never want for the full experiment. As for the bug, I am not sure if it is supported scenario. – SergeyA Feb 23 '18 at 16:49