Skip to content

clang -gsplit-dwarf on only some source files causes gdb to be unable to find debug information on global symbols #156732

@aury6623

Description

@aury6623

I'm working on a large project built with clang and lld, but where gdb is primarily used for debugging. I've found that gdb often, but not always, fails to find debug information for global symbols. When printing a global variable, it often gives the error <variable> has unknown type; cast it to its declared type despite type information for other global symbols from the same file being available. Sometimes it works or errors depending on where in the function I'm currently debugging (i.e. where I've placed the breakpoint), despite the symbol being global.

I'm using clang/lld 21.1.0 and gdb 16.3, both built from git. I have tried multiple different versions of both clang and gdb, all have the same issue.

I've come up with a minimal reproducer, which I've packed into a makefile:

CC=clang
GDB=gdb

all: print_version clean_obj setup
	$(CC) -g -o ext.o -c ext.c
	$(CC) -g -o main.o -c main.c
	$(CC) -g -gsplit-dwarf -o problem.o -c problem.c
	$(CC) -fuse-ld=lld -Wl,--gdb-index main.o ext.o problem.o
	$(GDB) --quiet a.out < cmd.gdb

ext.c:
	echo "int ext;" > ext.c
problem.c:
	echo "int problem;" > problem.c
main.c:
	echo -e '#include <stdio.h>\nint local;\nextern int ext;\nint main(void) {\n    printf("%d, %d\\n", local, ext);\n}' > main.c
cmd.gdb:
	echo -e 'set confirm off\nbreak main\nrun\nprint local\nprint ext\nquit' > cmd.gdb
setup: ext.c problem.c main.c cmd.gdb

print_version:
	@echo -n "  $(CC) version:  "
	@$(CC) --version | head -1
	@echo -n "  $(GDB) version:    "
	@$(GDB) --version | head -1

clean_obj:
	rm -f *.o *.dwo a.out
clean_src:
	rm -f ext.c problem.c main.c cmd.gdb
clean: clean_obj clean_src

Save the makefile, ensure that clang, gdb, and lld are all available in your PATH, then run "make". You should see output like this:

$ make
  clang version:  clang version 21.1.0 (https://github.com/llvm/llvm-project.git 3623fe661ae35c6c80ac221f14d85be76aa870f1)
  gdb version:    GNU gdb (GDB) 16.3
rm -f *.o *.dwo a.out
echo "int ext;" > ext.c
echo "int problem;" > problem.c
echo -e '#include <stdio.h>\nint local;\nextern int ext;\nint main(void) {\n    printf("%d, %d\\n", local, ext);\n}' > main.c
echo -e 'set confirm off\nbreak main\nrun\nprint local\nprint ext\nquit' > cmd.gdb
clang  -g -o ext.o -c ext.c
clang  -g -o main.o -c main.c
clang  -g -gsplit-dwarf -o problem.o -c problem.c
clang  -fuse-ld=lld -Wl,--gdb-index main.o ext.o problem.o
gdb  --quiet a.out < cmd.gdb
Reading symbols from a.out...
(gdb) (gdb) Breakpoint 1 at 0x16e4: file main.c, line 5.
(gdb) Starting program: a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main () at main.c:5
5           printf("%d, %d\n", local, ext);
(gdb) $1 = 0
(gdb) 'ext' has unknown type; cast it to its declared type                     <----------- should say (gdb) $2 = 0

gdb will print two variables, a global variable declared within the same file (local) and a global variable declared with extern and defined in a separate source file (ext). local prints 0, as expected, but ext gets the unknown type error, despite extern int ext; being two lines above the breakpoint!

The problem is caused by the use of -gsplit-dwarf only on some, but not all source files. Notably, the one source file it's being used on here, problem.c is is completely unrelated to ext.c or main.c. problem.c is not used at all in the execution of the program. If I put -gsplit-dwarf on all or no source files, the problem goes away. Using --gdb-index with lld is also required for the problem to occur.

I've tried various combinations of gdb vs lldb, gcc vs clang, and ld vs lld. Here's what I've found on that front:

  • If I use gcc instead of clang (with or without using lld), the problem goes away.
  • If I use lldb instead of gdb, the problem goes away.
  • If I disable use of lld and --gdb-index, and use the system linker instead, with either gcc or clang, the problem goes away.

By "problem goes away", I mean that the debugger will print both variables in my reproducer instead of only local.

Since the problem goes away with gcc, with or without lld, I don't think the issue is with lld. That leaves either clang or gdb.
Since the problem goes away with gcc, that's a strike against clang. But since the problem goes away with lldb, that's a strike against gdb.

So I'm not sure whether gdb or clang is at fault here. I'm starting by reporting the issue here. If it turns out that gdb is likely the culprit, I'll file a bug report with gdb instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:codegenIR generation bugs: mangling, exceptions, etc.debuginfo

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions