[PATCH 1/3] PE-COFF: Fix weak external symbol resolution when strong undef is seen first

Martin Storsjö martin@martin.st
Tue Jun 2 09:01:33 GMT 2026


On Tue, 2 Jun 2026, Jan Beulich wrote:

>> 1. In coff_link_add_symbols, when the generic linker resolves a weak
>>    undefined against an existing strong undefined (NOACT in the action
>>    table), the COFF-specific symbol_class and aux record were not stored
>>    because the existing hash entry already had non-null class/type from
>>    the first (strong) object file.
>>
>> 2. In _bfd_coff_generic_relocate_section, the weak alias fallback only
>>    triggered for bfd_link_hash_undefweak symbols.  When a strong undef
>>    is seen first, the hash type stays bfd_link_hash_undefined (the
>>    generic linker does not downgrade it), so the fallback was skipped.
>
> For both of these, where is it written down what the required behavior
> is? From your description it sounds as if you're moving from one extreme
> ("strong" as the overall result) to the other ("weak" as the overall
> result), when it kind of feels as if the type of reference may need to
> be retained per incoming object file. (Or else "strong" being the
> overall result would look to be more likely to be correct.)

This stems from how the COFF weak external symbols work - they are quite 
different from ELF weak symbols - but with these changes, they can provide 
mostly the same user facing behaviour.

In COFF, a "weak external" symbol is an undefined symbol (section number 
0, which means undefined symbol, but storage class 
IMAGE_SYM_CLASS_WEAK_EXTERNAL instead of IMAGE_SYM_CLASS_EXTERNAL), which 
contains an AUX symbol entry, pointing at another symbol (which may be 
defined in the same object file, or undefined, meant to be resolved in 
another object file). If there is no other definition available of that 
symbol, the linker should use the symbol pointed to by the weak external 
instead.

This mechanism works for both cases of weak symbols:

- For a weak declaration (where we reference a symbol which may or may not 
exist elsewhere), we have a weak external pointing at a null symbol. So if 
nothing else provides a definition of it, we don't fail the link with an 
undefined symbol, but we instead redirect uses of it towards the null 
symbol. This works with uses like this:

void __attribute__((weak)) maybe_exists(void);

void call(void) {
     if (maybe_exists)
         maybe_exists();
}

- For a weak definition, we have the COFF weak external point at the 
concrete symbol definition instead. If there's a strong definition of it 
elsewhere, that should be used, but if there is none, then the weak 
external, pointing at the fallback function implementation, should be used 
instead.


So if the linker already has seen a regular (strong) undefined symbol, and 
sees a weak external of the same symbol from another object file (which on 
the object file level is an "undefined weak external", with a reference to 
another symbol), that weak external serves as a potential definition of 
that symbol, which should be retained and used, in case there's no other 
(non-weak) definition of the symbol.

// Martin



More information about the Binutils mailing list