Skip to content

Scalar deleting destructor link error #13

@lucianpin

Description

@lucianpin

The issue occurred as soon as some struct allocated through jxy::make_unique contained some members of other class types that required a constructor and destructor.

struct Globals
{
    Globals() : port(PORT_NAME)
    {
    }

    CommunicationPort port;
};

class CommunicationPort
{
public:

    CommunicationPort(_In_ const wchar_t * portName) : m_portName(portName)
    {
    }

private:

    const jxy::wstring<NonPagedPool, TAG> m_portName;
};

The error: LNK2019: unresolved external symbol "void __cdecl operator delete(void *,unsigned __int64)" (??3@YAXPEAX_K@Z) referenced in function "public: void * __cdecl SomeClass::'scalar deleting destructor'(unsigned int)"

Background: So what is 'scalar deleting destructor'(unsigned int)? According to this article: https://ofekshilon.com/2014/06/09/on-vector-deleting-destructors-and-some-newdelete-internals/

the compiler does not call ~Whatever() and operator-delete directly, but rather generates and invokes a helper function that wraps these two calls. This helper is called scalar deleting destructor

The unsigned int parameter is a flag telling if the function should perform a vector deleting destructor or a scalar deleting destructor and whether to deallocate memory, which is optional.

Raymond Chen spelled out pseudo code for it:

void Whatever::vector deleting destructor(int flags)
{
  if (flags & 2) { // if vector destruct
    size_t* a = reinterpret_cast<size_t*>(this) - 1;
    size_t howmany = *a;
    vector destructor iterator(p, sizeof(Whatever),
      howmany, Whatever::~Whatever);
    if (flags & 1) { // if delete too
      operator delete(a);
    }
  } else { // else scalar destruct
    this->~Whatever(); // destruct one
    if (flags & 1) { // if delete too
      operator delete(this);
    }
  }
}

The actual issue:
11111

When the default deleter calls Pointer->~T(), the compiler is using the hidden wrapper function SomeClass::'scalar deleting destructor', but with flag set not to perform deallocation because obviously the intention was only to call de destructor of SomeClass.

222222

It can be seen that flag is tested and if not set, after calling the destructor, it would jump at the end without calling the operator delete.

However, the code calling operator delete is there so the compiler needs the definition for it. But Jxy library is not providing any code for such signature: void __cdecl operator delete(void *,unsigned __int64) because no code path should presumably call it. In kernel-mode we would eventually use void __cdecl operator delete(void* Memory, POOL_TYPE PoolType, ULONG PoolTag). So that explains the link error.

Solution would be to define void __cdecl operator delete(void* Memory, size_t Size) and maybe also void __cdecl operator delete(void* Memory) noexcept, but to issue a bug check if these operators would ever be called - that should not happen. All this just because the compiler is using the multi-purpose wrapper and the code inside needs to link also to not used delete operator in kernel.

Any other ideas or thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions