DeadCat
DeadCat

Reputation: 29

Where is the location information in a dwarf file for a var declared as extern?

I am wondering how the location information of a variable is presented in a dwarf-file (a file with debug symbols), when the variable is declared as extern. A very simple example to illustrate my question:

simple.c:

#include <stdio.h>

extern int itest;

void main() {
    printf("val: %d and addr: %p\n", itest, (void*) &itest);
}

simple_int.c:

int itest = 4;

compile:

gcc -g simple.c simple_int.c -o simple

When I inspect the executable simple using

dwarfdump simple | less

I get information for the variable itest like this:

< 1><0x00000072>    DW_TAG_variable
                      DW_AT_name                  itest
                      DW_AT_decl_file             0x00000001 /<censored>/simple.c
                      DW_AT_decl_line             0x00000003
                      DW_AT_decl_column           0x0000000c
                      DW_AT_type                  <0x00000058>
                      DW_AT_external              yes(1)
                      DW_AT_declaration           yes(1)

There is no entry from which I could derive the location.

Usually I know entries like

DW_AT_location              len 0x0009: 0x031440000000000000: 
                          DW_OP_addr 0x00004014

or

DW_AT_location              len 0x0002: 0x9164: 
                            DW_OP_fbreg -28

So in which format is the location information stored for this variable declared as extern? Where can I read something about the location?

Thank you for your help

Upvotes: 0

Views: 485

Answers (3)

Dmitry Grigoryev
Dmitry Grigoryev

Reputation: 3203

With some compilers, declaring variables as "extern" produces two DW_TAG_variable entries, one with variable name and type, and a second one with location. You need to parse both entries to associate the name and the location.

Here's an example from GCC10:

test.c:

extern unsigned char test_var;
unsigned char test_var;

test.dwarf:

 <1><29e2>: Abbrev Number: 7 (DW_TAG_variable)
    <29e3>   DW_AT_name        : test_var
    <29ec>   DW_AT_decl_file   : 2
    <29ed>   DW_AT_decl_line   : 5
    <29ee>   DW_AT_decl_column : 22
    <29ef>   DW_AT_type        : <0x29d1>
    <29f3>   DW_AT_external    : 1
    <29f3>   DW_AT_declaration : 1
 <1><29f3>: Abbrev Number: 8 (DW_TAG_variable)
    <29f4>   DW_AT_specification: <0x29e2>
    <29f8>   DW_AT_decl_line   : 6
    <29f9>   DW_AT_decl_column : 15
    <29fa>   DW_AT_location    : 9 byte block: 3 40 d0 0 40 1 0 0 0     (DW_OP_addr: 14000d040)

Upvotes: 0

Employed Russian
Employed Russian

Reputation: 213526

When I execute your repro commands, I observe (using readelf -wi simple):

  Compilation Unit @ offset 0:
...
 <0><c>: Abbrev Number: 2 (DW_TAG_compile_unit)
    <d>   DW_AT_producer    : (indirect string, offset: 0x2f): GNU C17 12.2.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables
    <11>   DW_AT_language    : 29       (C11)
    <12>   DW_AT_name        : (indirect line string, offset: 0): simple.c
    <16>   DW_AT_comp_dir    : (indirect line string, offset: 0x9): /tmp
    <1a>   DW_AT_low_pc      : 0x1139
    <22>   DW_AT_high_pc     : 0x2a
    <2a>   DW_AT_stmt_list   : 0
...
  Compilation Unit @ offset 0xb7:
...
 <1><72>: Abbrev Number: 5 (DW_TAG_variable)
    <73>   DW_AT_name        : (indirect string, offset: 0x92): itest
    <77>   DW_AT_decl_file   : 1
    <78>   DW_AT_decl_line   : 3
    <79>   DW_AT_decl_column : 12
    <7a>   DW_AT_type        : <0x58>
    <7e>   DW_AT_external    : 1
    <7e>   DW_AT_declaration : 1
...
 <0><c3>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c4>   DW_AT_producer    : (indirect string, offset: 0x2f): GNU C17 12.2.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables
    <c8>   DW_AT_language    : 29       (C11)
    <c9>   DW_AT_name        : (indirect line string, offset: 0x23): simple_int.c
    <cd>   DW_AT_comp_dir    : (indirect line string, offset: 0x9): /tmp
    <d1>   DW_AT_stmt_list   : 0x5a
 <1><d5>: Abbrev Number: 2 (DW_TAG_variable)
    <d6>   DW_AT_name        : (indirect string, offset: 0x92): itest
    <da>   DW_AT_decl_file   : 1
    <db>   DW_AT_decl_line   : 1
    <dc>   DW_AT_decl_column : 5
    <dd>   DW_AT_type        : <0xeb>
    <e1>   DW_AT_external    : 1
    <e1>   DW_AT_location    : 9 byte block: 3 18 40 0 0 0 0 0 0        (DW_OP_addr: 4018)

Just installed dwarfdump and confirmed that it also sees the definition:

...
COMPILE_UNIT<header overall offset = 0x000000b7>:
< 0><0x0000000c>  DW_TAG_compile_unit
                    DW_AT_producer              GNU C17 12.2.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables
                    DW_AT_language              DW_LANG_C11
                    DW_AT_name                  simple_int.c
                    DW_AT_comp_dir              /tmp
                    DW_AT_stmt_list             0x0000005a

LOCAL_SYMBOLS:
< 1><0x0000001e>    DW_TAG_variable
                      DW_AT_name                  itest
                      DW_AT_decl_file             0x00000001 /tmp/simple_int.c
                      DW_AT_decl_line             0x00000001
                      DW_AT_decl_column           0x00000005
                      DW_AT_type                  <0x00000034>
                      DW_AT_external              yes(1)
                      DW_AT_location              len 0x0009: 0x031840000000000000:
                          DW_OP_addr 0x00004018

It seems that you are either not looking far enough for the definition of the variable, or the file with the definition is compiled without -g, or you have over-reduced the test case if the DW_AT_location is really not present.

Upvotes: 2

Ty Cooper
Ty Cooper

Reputation: 105

This might be worth a shot because compiler optimizations can cause certain debugging information to be lost, you could try recompiling with lower optimization levels or even omitting entirely with the '-O0' flag.

gcc -g -O0 simple.c simple_int.c -o simple

Upvotes: -1

Related Questions