Skip to content

external symbol g_allocator in header file breaks DelayLoadDLLs on windows #1960

@RuurdBeerstra

Description

@RuurdBeerstra

Describe the bug

I use the AWS SDK dll's on Windows in a binary that can also run outside the cloud on arbitrary windows systems.
Support for AWS was added a few years ago and we used the DelayLoadDLLs directive when building our binaries so the DLL's are not required in a non-AWS environment (and not used/loaded even in AWS until it actually uses AWS functionality).

After an upgrade to the latest AWS SDK this was broken: The linker told me I could not use the DelayLoadDLLs for the AWS dll's because of the "g_allocator" symbol.

4>LINK : fatal error LNK1194: cannot delay-load 'aws-crt-cpp.dll' due to import of data symbol '"__declspec(dllimport) struct aws_allocator * Aws::Crt::g_allocator" (_imp?g_allocator@Crt@Aws@@3PEAUaws_allocator@@ea)'; link without /DELAYLOAD:aws-crt-cpp.dll

Digging through the AWS source I found the culprit: aws/crt/StlAllocator.h has the line:
extern AWS_CRT_CPP_API Allocator *g_allocator;

Which implies that any component that includes AWS header files ends up with an extern DATA reference to the g_allocator.
The DelayLoadDLLs can handle references to functions, but because the value of g_allocator must be resolved at start-time of the binary, this one single pointer caused our product to have a very undesirable hard runtime dependency with AWS.

I did a crude hack to get around this. I replaced the "extern" line with this:

extern AWS_CRT_CPP_API Allocator *Get_g_allocator();
#define g_allocator Get_g_allocator()

So everywhere the code uses "g_allocator" it now calls a getter function for the current value.
In crt/aws-crt-cpp/source/Api.cpp (where g_allocator is defined) I added (after the #includes):

/* Undo hack in StlAllocator.h */
#ifdef g_allocator
#undef g_allocator
#else
#error StlAllocator must define g_allocator as a call to Get_g_allocator()
#endif

The #error is just to make sure my includes and defines and up as intended.
The getter function is trivial:
AWS_CRT_CPP_API Allocator *Get_g_allocator();
Allocator *Get_g_allocator() { return g_allocator; }

Building with these modified header files yields a working configuration that no longer suffers from the hard dependency.

The hack is, of course, ugly as sin.
But because there are some 53 uses of "g_allocator" in a some 20 different files, I did it the quick & dirty way.

Expected Behavior

That we could have a single binary that optionally uses AWS dll's (like was possible in an older version of the AWS SDK).

Current Behavior

Linking a Windows binary that uses DelayLoadDLLs on aws-sdk-core.dll yields this error:

4>LINK : fatal error LNK1194: cannot delay-load 'aws-crt-cpp.dll' due to import of data symbol '"__declspec(dllimport) struct aws_allocator * Aws::Crt::g_allocator" (_imp?g_allocator@Crt@Aws@@3PEAUaws_allocator@@ea)'; link without /DELAYLOAD:aws-crt-cpp.dll

Reproduction Steps

The code is not the problem: Try linking a binary that has a DelayLoadDLLs in the vcxproj files likes this:
aws-cpp-sdk-core.dll

And get the error about the DelayLoad not being possible because of the g_allocator symbol.

Possible Solution

Change all occurrences of g_allocator in the header files to a function call that returns the current value.

Additional Information/Context

The (crude) patch provides a workaround for us, so it is currently not an urgent issue.
But I'd like to see a fix for this in a future version.

AWS CPP SDK version used

git clone from April 25, 2022.

Compiler and Version used

Visual Studio 2019

Operating System and version

Windows 2016

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions