The first thing you have to understand here is that a call to foo() made while the constructor of class A is active is dispatched to A::foo(), even if the full object under construction has type B and B overrides foo(). The presence of B::foo() is simply ignored.
This means that your code attempts to call A::foo(). Since A::foo() is a pure virtual function, the behavior of your code is undefined.
C++ language make no guarantees of what kind of "error" should occur in such cases. Which means that your expectations of "linker error" are completely unfounded. If a programs makes an attempt to perform a virtual call to a pure virtual function the behavior is simply undefined. That is the only thing that can be said here from the C++ language point of view.
How this undefined behavior will manifest itself in practical implementations depends on the implementation. Undefined behavior is allowed to manifest itself through compile-time errors, for example.
In your case, your program attempts to make a virtual call to pure virtual function A::foo(). In general case the compiler dispatches virtual calls dynamically, through a run-time mechanism that implements polymorphism (so called virtual method table is the most popular one). In some cases, when compiler can determine the exact type of the object used in the call, it optimizes the code and makes an ordinary direct (non-dynamic) call to a virtual function.
In practice, if a function pure virtual, its virtual method table entry contains a null pointer. A dynamic call to such function typically leads to run-time error. Meanwhile, a direct (optimized) call to such function typically leads to a compiler or linker error.
In your example the compiler did not optimize the call. It made a full-fledged dynamic call to A::foo() through the virtual method table. The null pointer in that table triggered the run-time error.
If you call your pure virtual function directly from the constructor
A() { foo(); }
a typical compiler will normally make a direct (optimized) call to foo(), which will typically lead to a linker error.
A::foo().