Discussion:
[Valgrind-developers] Replacement function resolution
p***@free.fr
2017-02-05 19:31:06 UTC
Permalink
Hi all

I'm trying to understand how valgrind resolves replacement functions at runtime.

Here's the code that I'm using:

class MyClass
{
int i;
};

int main()
{
MyClass* myClass = new MyClass();
delete myClass;
}

GCC 4.8.2 emits
gnm test64_482 | grep _Z
U _ZdlPv
U _Znwm


Or demangled

gnm -C test64_482 | grep operator
U operator delete(void*)
U operator new(unsigned long)

I see that this corresponds to the following in vg_replace_malloc.c:

FREE(VG_Z_LIBSTDCXX_SONAME, _ZdaPv, __builtin_vec_delete );
FREE(SO_SYN_MALLOC, _ZdaPv, __builtin_vec_delete );


At runtime

valgrind --trace-malloc=yes ./test64_482
--7974-- _Znwm(4) = 0x7FF760040
--7974-- _ZdlPv(0x7FF760040)

So far so good.

With GCC built from SVN head (but I'd expect the same for any GCC since version 6) this changes a bit. [see also bz 72347]

GCC emits
gnm test64 | grep _Z
U _ZdlPvm
U _Znwm

demangled

gnm -C test64 | grep operator
U operator delete(void*, unsigned long)
U operator new(unsigned long)

valgrind --trace-malloc=yes ./test64
--9374-- malloc(72704) = 0x7FF680040
--9374-- _Znwm(4) = 0x7FF691C80
--9374-- _ZdlPv(0x7FF691C80)
--9374-- free(0x7FF680040)


The extra malloc/free are, I guess, artefacts of the newer libstdc++; I get them even with an empty main().

I don't see how (and I haven't found in the Vagrind code) how the call to ZdlPvm resolves to _ZdlPv. Can anyone tell me where to look in the Valgrind code for this?

A+
Paul
Ivo Raisr
2017-02-07 13:16:25 UTC
Permalink
Post by p***@free.fr
Hi all
I'm trying to understand how valgrind resolves replacement functions at runtime.
Hi Paul,
Have a look at pub_tool_redir.h, mechanics and Z-encoding rules are
described there.
Post by p***@free.fr
GCC 4.8.2 emits
gnm test64_482 | grep _Z
U _ZdlPv
U _Znwm
Or demangled
gnm -C test64_482 | grep operator
U operator delete(void*)
U operator new(unsigned long)
FREE(VG_Z_LIBSTDCXX_SONAME, _ZdaPv, __builtin_vec_delete );
FREE(SO_SYN_MALLOC, _ZdaPv, __builtin_vec_delete );
At runtime
valgrind --trace-malloc=yes ./test64_482
--7974-- _Znwm(4) = 0x7FF760040
--7974-- _ZdlPv(0x7FF760040)
So far so good.
With GCC built from SVN head (but I'd expect the same for any GCC since version 6) this changes a bit. [see also bz 72347]
GCC emits
gnm test64 | grep _Z
U _ZdlPvm
U _Znwm
demangled
gnm -C test64 | grep operator
U operator delete(void*, unsigned long)
U operator new(unsigned long)
valgrind --trace-malloc=yes ./test64
--9374-- malloc(72704) = 0x7FF680040
--9374-- _Znwm(4) = 0x7FF691C80
--9374-- _ZdlPv(0x7FF691C80)
--9374-- free(0x7FF680040)
I think you've already spotted the difference between:
_ZdlPv: operator delete(void*)
[which is replaced by _ZdaPv: __builtin_vec_delete]
and
_ZdlPvm: operator delete(void*, unsigned long)

So the remaining task becomes finding a replacer for _ZdlPvm and if
there is none, providing one.

I.
p***@free.fr
2017-02-08 21:12:20 UTC
Permalink
----- Original Message -----
Post by Ivo Raisr
Post by p***@free.fr
Hi all
I'm trying to understand how valgrind resolves replacement
functions at runtime.
Hi Paul,
Have a look at pub_tool_redir.h, mechanics and Z-encoding rules are
described there.
Hi Ivo

Thanks for the info. What was confusing me the most was why code that called operator delete(void* ptr, std::size_t) was being detected by an unmodified valgrind as operator delete(void* ptr). Looking at the libstdc++ source this is now obvious:

_GLIBCXX_WEAK_DEFINITION void
operator delete(void* ptr, std::size_t) _GLIBCXX_USE_NOEXCEPT
{
::operator delete (ptr);
}

[and libcxx does the same]

So currently this works because the new functions just wrap the old ones.

A+
Paul

Loading...