https://bugs.kde.org/show_bug.cgi?id=520170

--- Comment #2 from Michael Catanzaro <[email protected]> ---
(In reply to Paul Floyd from comment #1)
> Let's say your app links with liba and libb and they link
> 
> liba -> dynamic libstdc++.so
> libb -> static libstdc++.a

The allocation and deallocation both occur in vte at termpropsregistry.cc:199,
so at least there is only one library involved.

> 2. If you use "identical code folding" then the linker might be seeing that
> deallocation functions have identical machine code and only generating one
> version.

I don't think vte or freedesktop-sdk uses any special linker flags to enable
such behavior, so if it's not something that ld.bfd performs by default, then
that seems unlikely?

> 3. If you use tcmalloc, that manually does identical code folding and only
> generates one alias for free/delete/delete[].

No tcmalloc involved.

Thanks for the tips. Looks like this is going to be pretty hard to track down.
:( I don't have experience with reading assembly code, and the disassembled
function where the allocation and deallocation occurs is pretty complicated at
2377 lines long, since it contains lots of inlined C++ functions. But at least
it's the same function for both the allocation and deallocation,
_ZN3vte8property8Registry12install_manyESt16initializer_listINS1_8PropertyEE.
I'm going to attach the disassembly.

Although I can only barely understand what I see, I do notice something
interesting: there is a correspondence between the target of the call
instructions and gobbledygook in vg_replace_malloc.c. So despite my
inexperience, that seems clearly interesting. At least it *almost* explains why
we wind up where we do. For example, the disassembly contains:

   0x00007ffff64c95f9 <+185>:   call   0x7ffff647bf40 <_ZdlPvm@plt>

and the same at +1567 and +2203. And hey! that _ZdlPvm is a match for the 
/*------------------- C++14 delete sized -------------------*/ section of
vg_replace_malloc.c:

// operator delete(void*, unsigned long)
#elif __SIZEOF_SIZE_T__ == 8
 DELETE_SIZED(VG_Z_LIBSTDCXX_SONAME,  _ZdlPvm,               __builtin_delete,
DeleteSized );
 DELETE_SIZED(VG_Z_LIBCXX_SONAME,     _ZdlPvm,               __builtin_delete,
DeleteSized );
 DELETE_SIZED(VG_Z_LIBC_SONAME,       _ZdlPvm,               __builtin_delete,
DeleteSized );
 DELETE_SIZED(SO_SYN_MALLOC,          _ZdlPvm,               __builtin_delete,
DeleteSized );
#endif

I also see:

   0x00007ffff64c98ac <+876>:   call   0x7ffff647c050 <_Znwm@plt>

and the same at +1066 and +1685. That matches:

// operator new(unsigned long)
#if VG_WORDSIZE == 8
 ALLOC_or_BOMB(VG_Z_LIBSTDCXX_SONAME, _Znwm,          __builtin_new);
 ALLOC_or_BOMB(VG_Z_LIBCXX_SONAME,    _Znwm,          __builtin_new);
 ALLOC_or_BOMB(VG_Z_LIBC_SONAME,      _Znwm,          __builtin_new);
 ALLOC_or_BOMB(SO_SYN_MALLOC,         _Znwm,          __builtin_new);
#endif

With the magic of gdb breakpoints, I see that a breakpoint for
std::__new_allocator<vte::property::Registry::Property>::allocate stops here:

=> 0x00007ffff64c9958 <+1048>:  lea    (%rax,%rax,2),%rax
   0x00007ffff64c995c <+1052>:  shl    $0x4,%rax
   0x00007ffff64c9960 <+1056>:  mov    %rax,%rdi
   0x00007ffff64c9963 <+1059>:  mov    %rax,-0xc8(%rbp)
   0x00007ffff64c996a <+1066>:  call   0x7ffff647c050 <_Znwm@plt>

where it's about to call operator new (not operator new[]).

A breakpoint for
std::__new_allocator<vte::property::Registry::Property>::deallocate stops here:

=> 0x00007ffff64c9b59 <+1561>:  mov    %rcx,%rdi
   0x00007ffff64c9b5c <+1564>:  sub    %rcx,%rsi
   0x00007ffff64c9b5f <+1567>:  call   0x7ffff647bf40 <_ZdlPvm@plt>

before the call to operator delete.

What's especially weird is what I do *not* see: there is no _Znam anywhere in
the disassembly, which is very strange because there ought to be, because
valgrind's stack trace shows that its operator new[] is being called:

==84==  Address 0x1b841470 is 0 bytes inside a block of size 48 alloc'd
==84==    at 0x4FE55F3: operator new[](unsigned long) (vg_replace_malloc.c:717)

And the gobbledygook for operator new[] on 64-bit Linux is _Znam:

// operator new[](unsigned long),
#if VG_WORDSIZE == 8
 ALLOC_or_BOMB(VG_Z_LIBSTDCXX_SONAME, _Znam,             __builtin_vec_new );
 ALLOC_or_BOMB(VG_Z_LIBCXX_SONAME,    _Znam,             __builtin_vec_new );
 ALLOC_or_BOMB(VG_Z_LIBC_SONAME,      _Znam,             __builtin_vec_new );
 ALLOC_or_BOMB(SO_SYN_MALLOC,         _Znam,             __builtin_vec_new );
#endif

I suppose that's a good thing, because it sure looks like the libstdc++ source
code wants operator new, not operator new[]. So the disassembly appears to
correspond to the source code, but it does not correspond to what valgrind
reports. That's pretty weird!

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to