You can do that in multiple ways.
First you can use the extra bits of a pointer for a counter to fit it all into 64 bits.
Second, you can use a 128 bit compare and swap which has been supported by CPUs for about 20 years now.
Third, you can not use pointers and use indices of whatever bit resolution you want, using the extra bits for a counter.
Finally, how does a garbage collector change this ?
If make a long list using shared_ptr will overflow the stack when the head destructor executes.
If we set aside for a second the insanity in making a linked list where every pointer destruction calls the next pointer in the list's destructor, how is this unique to a shared_ptr ?