Interrupt

Arm-none-eabi-nm: some symbols are not related to any source file

In my embedded project I compile amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.c in this way:

/opt/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc \
	-std=gnu11 \
	-mcpu=cortex-m7 \
	-mthumb \
	-mapcs \
	-mfloat-abi=hard \
	-mfpu=fpv5-d16 \
	-fno-common \
	-fno-math-errno \
	-fsingle-precision-constant \
	-fno-trapping-math \
	-fno-signaling-nans \
	-fno-builtin \
	-fstrict-aliasing \
	-fstack-usage \
	-Wstack-usage=300 \
	-DCPU_MIMXRT1051DVL6B  \
	-D__FREERTOS__=1 \
	-DFSL_RTOS_FREE_RTOS \
	-DFSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE \
	-D__MCUXPRESSO \
	-D__USE_CMSIS \
	-DARM_MATH_CM7 \
	-D__NEWLIB__ \
	-DDEBUG=0 \
	-IDSP/source/ \
	-Iamazon-freertos/lib/FreeRTOS-Plus-TCP/ \
	-Iamazon-freertos/lib/FreeRTOS-Plus-TCP/portable/BufferManagement/ \
	-Iamazon-freertos/lib/FreeRTOS-Plus-TCP/portable/NetworkInterface/imxrt105x/ \
	-Iamazon-freertos/lib/FreeRTOS/ \
	-Iamazon-freertos/lib/include/ \
	-Iamazon-freertos/lib/FreeRTOS/portable/GCC/ARM_CM4F/ \
	-Iamazon-freertos/lib/FreeRTOS/portable/MemMang/ \
	-Og \
	-g3 \
	-Wall \
	-ffunction-sections \
	-fdata-sections \
	-c \
	-MMD \
	-MP \
	-Werror \
	--specs=nano.specs  \
	-Wa,-anhlmsd=build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.lst \
	-o build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.o amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.c 

I have ipconfigUSE_TCP set to 1 in amazon-freertos/lib/FreeRTOS-Plus-TCP/include/FreeRTOSIPConfig.h

FreeRTOS_Sockets.c declares xBoundUDPSocketsList and xBoundTCPSocketsList

/* The list that contains mappings between sockets and port numbers.  Accesses
to this list must be protected by critical sections of one kind or another. */
List_t xBoundUDPSocketsList;

#if ipconfigUSE_TCP == 1
	List_t xBoundTCPSocketsList;
#endif /* ipconfigUSE_TCP == 1 */

Once I have my elf executable linked, run this command:

$ /opt/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-nm -a -l -n -t x --print-size image/DSP.elf | grep -E '^[[:xdigit:]]{8} [[:xdigit:]]{8} B' | grep SocketsList
2001ac7c 00000014 B xBoundTCPSocketsList
2001ac90 00000014 B xBoundUDPSocketsList	/home/max/Lavori/4202/src/repos/toremove/FW/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.c:162

Both symbols exist in the executable, but one (xBoundTCPSocketsList) does not seem to belong to any .c source.
Both appear on the map file:

$ grep -n -A 1 -E 'xBoundTCPSocketsList|xBoundUDPSocketsList' image/DSP.map
61974: .bss.xBoundTCPSocketsList
61975-                0x000000002001ac7c       0x14 ./build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.o
61976:                0x000000002001ac7c                xBoundTCPSocketsList
61977: .bss.xBoundUDPSocketsList
61978-                0x000000002001ac90       0x14 ./build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.o
61979:                0x000000002001ac90                xBoundUDPSocketsList

Even addr2line fails:

$ arm-none-eabi-addr2line -a -e image/DSP.elf  2001ac7c 2001ac90
0x2001ac7c
??:0
0x2001ac90
/home/max/Lavori/4202/src/repos/toremove/FW/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.c:162

even the FreeRTOS_Sockets.lst doesn’t tell me anything more:

 7337              		.global	xBoundTCPSocketsList
 7338              		.global	xBoundUDPSocketsList
 7339              		.section	.bss.xBoundTCPSocketsList,"aw",%nobits
 7340              		.align	2
 7341              		.set	.LANCHOR2,. + 0
 7344              	xBoundTCPSocketsList:
 7345 0000 00000000 		.space	20
 7345      00000000 
 7345      00000000 
 7345      00000000 
 7345      00000000 
 7346              		.section	.bss.xBoundUDPSocketsList,"aw",%nobits
 7347              		.align	2
 7348              		.set	.LANCHOR1,. + 0
 7351              	xBoundUDPSocketsList:
 7352 0000 00000000 		.space	20
 7352      00000000 
 7352      00000000 
 7352      00000000 
 7352      00000000 

There are many other symbols present in the executable but which do not seem to be associated with any .c source file.

Why this behavior? What changes between the two symbols xBoundTCPSocketsList and xBoundUDPSocketsList? Am I getting it wrong or omitting some debugging parameters when compiling? How do I get either nm or some other way to get the .c source where a symbol is declared?

best regards
Max

1 Like

Hi Max,

Could you share the flags you used for linking as well?

of course:

/opt/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc \
	-mcpu=cortex-m7 \
	-mthumb \
	-mapcs \
	-mfloat-abi=hard \
	-mfpu=fpv5-d16 \
	-Wl,--gc-sections \
	-Wl,--print-memory-usage \
	-Wl,--sort-section=alignment \
	-specs=nano.specs \
	-nostartfiles \
	-TDSP/dsp.ld \
	-Wl,--cref,-Map=image/DSP.map \
	-o image/DSP.elf
		<a lot of object files> \
		./build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.o \
	-L DSP \
	-L CMSIS \
	-larm_cortexM7lfsp_math \
	-lm

However, I don’t think the problem is in the linking

In fact I get the same output of arm-none-eabi-nm even if I analyse ./build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.o:

$ arm-none-eabi-nm -a -l -n -t x --print-size build/DSP/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.o | grep -E '^[[:xdigit:]]{8} [[:xdigit:]]{8} B' | grep SocketsList
00000000 00000014 B xBoundTCPSocketsList
00000000 00000014 B xBoundUDPSocketsList	/home/max/Lavori/4202/src/repos/toremove/FW/amazon-freertos/lib/FreeRTOS-Plus-TCP/FreeRTOS_Sockets.c:162

best regards

After looking a bit into this, we think you’ve hit a bug. The following very simple code reproduces it:

#include <stdint.h>
extern int g_my_externd_global;
int g_my_externd_global;
int g_my_private_global;
void main(void) {
  g_my_externd_global = 1;
  g_my_private_global = 1;
  while (1) {}
}

Compiled with:

arm-none-eabi-gcc -g3 -g -mcpu=cortex-m4 -mthumb -fdata-sections -nostdlib -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb -g3 -nostdlib -ffunction-sections -fdata-sections -Os -fno-common  -Wl,--gc-sections -Wl,-Map,build/example.map -Wl,-emain -Ttext=0x0 main.c -o build/example.elf

You can see the forward-declared variable does not have a file name:

arm-none-eabi-nm -S -l build/example.elf
0001001c B __bss_end__
00010014 B __bss_start
00010014 B __bss_start__
00010014 T __data_start
0001001c B __end__
0001001c B _bss_end__
00010014 B _edata
0001001c B _end
00080000 N _stack
00010014 00000004 B g_my_externd_global
00010018 00000004 B g_my_private_global	/Users/chrisc/dev/gcc_bug/main.c:5
00000000 00000014 T main	/Users/chrisc/dev/gcc_bug/main.c:7

Digging into the ELF data, we see that the DWARF information is missing a DW_AT_decl_file section for that symbol:

<1><6f>: Abbrev Number: 4 (DW_TAG_variable)
    <70>   DW_AT_name        : (indirect string, offset: 0x3843): g_my_externd_global
    <74>   DW_AT_decl_file   : 1
    <75>   DW_AT_decl_line   : 3
    <76>   DW_AT_decl_column : 12
    <77>   DW_AT_type        : <0x61>
    <7b>   DW_AT_external    : 1
    <7b>   DW_AT_declaration : 1
 <1><7b>: Abbrev Number: 5 (DW_TAG_variable)
    <7c>   DW_AT_specification: <0x6f>
    <80>   DW_AT_decl_line   : 4
    <81>   DW_AT_decl_column : 5
    <82>   DW_AT_location    : 5 byte block: 3 14 0 1 0 	(DW_OP_addr: 10014)
 <1><88>: Abbrev Number: 6 (DW_TAG_variable)
    <89>   DW_AT_name        : (indirect string, offset: 0x234): g_my_private_global
    <8d>   DW_AT_decl_file   : 1
    <8e>   DW_AT_decl_line   : 5
    <8f>   DW_AT_decl_column : 5
    <90>   DW_AT_type        : <0x61>
    <94>   DW_AT_external    : 1
    <94>   DW_AT_location    : 5 byte block: 3 18 0 1 0 	(DW_OP_addr: 10018)

To me, this is a bug. Either:
(A) gcc should be emitting the DW_AT_decl_file entry, but does not.
or
(B) nm should be able to work out the file by using the DW_AT_specification entry and using the same file as the forward declaration.

Edit: We strongly suspect (B) is the bug. I suggest you open a bug against GNU binutils.

Sorry to ask you maybe stupid questions, but I want to make sure I got this right.

I’ve tried the same example as yours less than #include <stdint.h>

I see this:

<1><29>: Abbrev Number: 2 (DW_TAG_variable)
    <2a>   DW_AT_name        : (indirect string, offset: 0x2b16): g_my_externd_global
    <2e>   DW_AT_decl_file   : 1
    <2f>   DW_AT_decl_line   : 2
    <30>   DW_AT_decl_column : 12
    <31>   DW_AT_type        : <0x35>
    <35>   DW_AT_external    : 1
    <35>   DW_AT_declaration : 1
 <1><35>: Abbrev Number: 3 (DW_TAG_base_type)
    <36>   DW_AT_byte_size   : 4
    <37>   DW_AT_encoding    : 5        (signed)
    <38>   DW_AT_name        : int
 <1><3c>: Abbrev Number: 4 (DW_TAG_variable)
    <3d>   DW_AT_specification: <0x29>
    <41>   DW_AT_decl_line   : 3
    <42>   DW_AT_decl_column : 5
    <43>   DW_AT_location    : 5 byte block: 3 14 0 1 0         (DW_OP_addr: 10014)
 <1><49>: Abbrev Number: 5 (DW_TAG_variable)
    <4a>   DW_AT_name        : (indirect string, offset: 0x1cb): g_my_private_global
    <4e>   DW_AT_decl_file   : 1
    <4f>   DW_AT_decl_line   : 4
    <50>   DW_AT_decl_column : 5
    <51>   DW_AT_type        : <0x35>
    <55>   DW_AT_external    : 1
    <55>   DW_AT_location    : 5 byte block: 3 18 0 1 0         (DW_OP_addr: 10018)

I’ll try dissecting, starting from g_my_private_global @ line 4:

int g_my_private_global;

and the dwarf infos related to it are:

 <1><49>: Abbrev Number: 5 (DW_TAG_variable)
    <4a>   DW_AT_name        : (indirect string, offset: 0x1cb): g_my_private_global
    <4e>   DW_AT_decl_file   : 1
    <4f>   DW_AT_decl_line   : 4   <==================
    <50>   DW_AT_decl_column : 5
    <51>   DW_AT_type        : <0x35>
    <55>   DW_AT_external    : 1
    <55>   DW_AT_location    : 5 byte block: 3 18 0 1 0         (DW_OP_addr: 10018)

here is the name in the field DW_AT_name, a reference to a file (DW_AT_decl_file : 1), and also the address 0x10018 that match the nm output:

00010018 B g_my_private_global	/tmp/example/main.c:4

Then lets see at g_my_externd_global @ lines 2 and 3:

extern int g_my_externd_global;
int g_my_externd_global;

and the dwarf infos related to it are:

<1><29>: Abbrev Number: 2 (DW_TAG_variable)
    <2a>   DW_AT_name        : (indirect string, offset: 0x2b16): g_my_externd_global
    <2e>   DW_AT_decl_file   : 1
    <2f>   DW_AT_decl_line   : 2    <==================
    <30>   DW_AT_decl_column : 12
    <31>   DW_AT_type        : <0x35>
    <35>   DW_AT_external    : 1
    <35>   DW_AT_declaration : 1
 <1><3c>: Abbrev Number: 4 (DW_TAG_variable)
    <3d>   DW_AT_specification: <0x29>
    <41>   DW_AT_decl_line   : 3     <=================
    <42>   DW_AT_decl_column : 5
    <43>   DW_AT_location    : 5 byte block: 3 14 0 1 0         (DW_OP_addr: 10014)

the first block is almost identical to the g_my_private_global's one except for the lack of DW_AT_location replaced by DW_AT_declaration.
Is this the point?

The DW_AT_location is in the following block, where there is no DW_AT_name or DW_AT_decl_file, but there is DW_AT_location (with the actual address 0x10014), and DW_AT_specification` which to me seems to refer to block above.

Is all this reasoning correct?

What is the sequence of steps that should be performed by nm? I mean: nm writes both address and name of the variable. So somehow it already reads and correlates the two blocks. If I understand correctly, what it doesn’t do is to use the file information DW_AT_decl_file from the first block, right?
Or is GCC that should populate with another DW_AT_decl_file field the second block?

Before opening a bug report I would like to be sure that there are no nm parameters (and other binutils tools) for this purpose.

best regards
Max

Hi @mastupristi,

I think you’ve got this right. nm should be looking at <3c>, and it should follow the SW_AT_specification to go find the DW_AT_decl_file under <29> to show the filename.

This is my understanding of how it should work, but clearly it does not.

Best,

François.

I noticed another oddity. Let’s take the following file for reference:

extern int g_my_externd_global;
int g_my_externd_global;
int g_my_private_global;
int main(void)
{
	g_my_externd_global=1;
	g_my_private_global=1;
	return 0;
}

I compile with -Og, no link:

max@resfw04:~/Lavori/4202/prog/test-dwarf$ arm-none-eabi-gcc -g3 -g -mthumb -mcpu=cortex-m4 -nostdlib -ffunction-sections -fdata-sections -Og -fno-common -c main.c -o example.o

max@resfw04:~/Lavori/4202/prog/test-dwarf$ arm-none-eabi-nm -s -l example.o |grep _global
00000000 B g_my_externd_global
00000000 B g_my_private_global	/home/max/Lavori/4202/prog/test-dwarf/main.c:3

max@resfw04:~/Lavori/4202/prog/test-dwarf$ arm-none-eabi-objdump --dwarf=info example.o

example.o:     file format elf32-littlearm

Contents of the .debug_info section:

  Compilation Unit @ offset 0x0:
   Length:        0x6e (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  4
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x6ac): GNU C17 8.2.1 20181213 (release) [gcc-8-branch revision 267074] -mthumb -mcpu=cortex-m4 -mfloat-abi=soft -march=armv7e-m -g3 -g -O0 -ffunction-sections -fdata-sections -fno-common
    <10>   DW_AT_language    : 12	(ANSI C99)
    <11>   DW_AT_name        : (indirect string, offset: 0x116): main.c
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x880): /home/max/Lavori/4202/prog/test-dwarf
    <19>   DW_AT_ranges      : 0x0
    <1d>   DW_AT_low_pc      : 0x0
    <21>   DW_AT_stmt_list   : 0x0
    <25>   DW_AT_GNU_macros  : 0x0
 <1><29>: Abbrev Number: 2 (DW_TAG_variable)
    <2a>   DW_AT_name        : (indirect string, offset: 0x2aa9): g_my_externd_global
    <2e>   DW_AT_decl_file   : 1
    <2f>   DW_AT_decl_line   : 1
    <30>   DW_AT_decl_column : 12
    <31>   DW_AT_type        : <0x35>
    <35>   DW_AT_external    : 1
    <35>   DW_AT_declaration : 1
 <1><35>: Abbrev Number: 3 (DW_TAG_base_type)
    <36>   DW_AT_byte_size   : 4
    <37>   DW_AT_encoding    : 5	(signed)
    <38>   DW_AT_name        : int
 <1><3c>: Abbrev Number: 4 (DW_TAG_variable)
    <3d>   DW_AT_specification: <0x29>
    <41>   DW_AT_decl_line   : 2
    <42>   DW_AT_decl_column : 5
    <43>   DW_AT_location    : 5 byte block: 3 0 0 0 0 	(DW_OP_addr: 0)
 <1><49>: Abbrev Number: 5 (DW_TAG_variable)
    <4a>   DW_AT_name        : (indirect string, offset: 0x1d3): g_my_private_global
    <4e>   DW_AT_decl_file   : 1
    <4f>   DW_AT_decl_line   : 3
    <50>   DW_AT_decl_column : 5
    <51>   DW_AT_type        : <0x35>
    <55>   DW_AT_external    : 1
    <55>   DW_AT_location    : 5 byte block: 3 0 0 0 0 	(DW_OP_addr: 0)
 <1><5b>: Abbrev Number: 6 (DW_TAG_subprogram)
    <5c>   DW_AT_external    : 1
    <5c>   DW_AT_name        : (indirect string, offset: 0x2448): main
    <60>   DW_AT_decl_file   : 1
    <61>   DW_AT_decl_line   : 4
    <62>   DW_AT_decl_column : 5
    <63>   DW_AT_prototyped  : 1
    <63>   DW_AT_type        : <0x35>
    <67>   DW_AT_low_pc      : 0x0
    <6b>   DW_AT_high_pc     : 0x24
    <6f>   DW_AT_frame_base  : 1 byte block: 9c 	(DW_OP_call_frame_cfa)
    <71>   DW_AT_GNU_all_call_sites: 1
 <1><71>: Abbrev Number: 0

But if I compile with -O0:

max@resfw04:~/Lavori/4202/prog/test-dwarf$ arm-none-eabi-gcc -g3 -g -mthumb -mcpu=cortex-m4 -nostdlib -ffunction-sections -fdata-sections -O0 -fno-common -c main.c -o example.o

max@resfw04:~/Lavori/4202/prog/test-dwarf$ arm-none-eabi-nm -s -l example.o |grep _global
00000000 B g_my_externd_global	/home/max/Lavori/4202/prog/test-dwarf/main.c:1
00000000 B g_my_private_global	/home/max/Lavori/4202/prog/test-dwarf/main.c:3

max@resfw04:~/Lavori/4202/prog/test-dwarf$ arm-none-eabi-objdump --dwarf=info example.o

example.o:     file format elf32-littlearm

Contents of the .debug_info section:

  Compilation Unit @ offset 0x0:
   Length:        0x6e (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  4
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x1b68): GNU C17 8.2.1 20181213 (release) [gcc-8-branch revision 267074] -mthumb -mcpu=cortex-m4 -mfloat-abi=soft -march=armv7e-m -g3 -g -Og -ffunction-sections -fdata-sections -fno-common
    <10>   DW_AT_language    : 12	(ANSI C99)
    <11>   DW_AT_name        : (indirect string, offset: 0x116): main.c
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x7d9): /home/max/Lavori/4202/prog/test-dwarf
    <19>   DW_AT_ranges      : 0x0
    <1d>   DW_AT_low_pc      : 0x0
    <21>   DW_AT_stmt_list   : 0x0
    <25>   DW_AT_GNU_macros  : 0x0
 <1><29>: Abbrev Number: 2 (DW_TAG_variable)
    <2a>   DW_AT_name        : (indirect string, offset: 0x2aa8): g_my_externd_global
    <2e>   DW_AT_decl_file   : 1
    <2f>   DW_AT_decl_line   : 1
    <30>   DW_AT_decl_column : 12
    <31>   DW_AT_type        : <0x35>
    <35>   DW_AT_external    : 1
    <35>   DW_AT_declaration : 1
 <1><35>: Abbrev Number: 3 (DW_TAG_base_type)
    <36>   DW_AT_byte_size   : 4
    <37>   DW_AT_encoding    : 5	(signed)
    <38>   DW_AT_name        : int
 <1><3c>: Abbrev Number: 4 (DW_TAG_variable)
    <3d>   DW_AT_specification: <0x29>
    <41>   DW_AT_decl_line   : 2
    <42>   DW_AT_decl_column : 5
    <43>   DW_AT_location    : 5 byte block: 3 0 0 0 0 	(DW_OP_addr: 0)
 <1><49>: Abbrev Number: 5 (DW_TAG_variable)
    <4a>   DW_AT_name        : (indirect string, offset: 0x1d3): g_my_private_global
    <4e>   DW_AT_decl_file   : 1
    <4f>   DW_AT_decl_line   : 3
    <50>   DW_AT_decl_column : 5
    <51>   DW_AT_type        : <0x35>
    <55>   DW_AT_external    : 1
    <55>   DW_AT_location    : 5 byte block: 3 0 0 0 0 	(DW_OP_addr: 0)
 <1><5b>: Abbrev Number: 6 (DW_TAG_subprogram)
    <5c>   DW_AT_external    : 1
    <5c>   DW_AT_name        : (indirect string, offset: 0x2447): main
    <60>   DW_AT_decl_file   : 1
    <61>   DW_AT_decl_line   : 4
    <62>   DW_AT_decl_column : 5
    <63>   DW_AT_prototyped  : 1
    <63>   DW_AT_type        : <0x35>
    <67>   DW_AT_low_pc      : 0x0
    <6b>   DW_AT_high_pc     : 0x18
    <6f>   DW_AT_frame_base  : 1 byte block: 9c 	(DW_OP_call_frame_cfa)
    <71>   DW_AT_GNU_all_call_sites: 1
 <1><71>: Abbrev Number: 0

therefore with -O0 the file name information is correctly recovered and displayed by nm.
Instead with -Og the problem occurs.

However, the debug information is not so different as to justify this behavior.

The problem must be somewhere else.

best regards
Max

Hmmm, that’s an interesting result. It’s clear to me this is a bug, but I’m not sure whether it is on the nm or gcc side.

Ultimately, the problem looks very similar to the one fixed for functions here.

Notice how in the variable block there is no traversal of the DW_AT_specification attribute. I believe an update will need to be made to the switch case there to fix the recovery of line information for some global variables.

00000000 B g_my_externd_global /home/max/Lavori/4202/prog/test-dwarf/main.c:1
00000000 B g_my_private_global /home/max/Lavori/4202/prog/test-dwarf/main.c:3

It’s the interaction of -fno-common (handling for tentative definitions) and the optimization setting (-Og vs -O0) that are controlling whether the variable is allocated in the .o or not. If you remove -fno-common, you would see no line info for either global in the .o debug info.

Best,
Chris

I opened a bug on the binutils’ bugzilla. A patch was pushed on master that I tried on both x86 and ARM (recompiling the toolchain arm-none-eabi). I published the results.
This new nm works in almost all cases but fails to retrieve file names only for object files compiled with -fcommon, while the related elf files are OK.
So I assume there is still something to fix.
In the report I’ve attached also the python script I used to do the tests

best regards
Max

Awesome to see it got fixed! In the case of symbols in the COMMON section, I can imagine why it’s tricky: they may have multiple definitions so it’s not clear which to display.