Help with Dwarf file output from avr-gcc

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm building a  software debugger (for AtMega328p to start with, may extend later). So far I've successfully parsed the debug file (created with objdump) and extracted function names, typedefs, addresses of globals, values of (global) array contents etc.

But I'm having trouble understanding​ how the local variables work. I've read through this and this but they don't refer specifically to the avr-gcc output format.

As a simple example I'm trying this function:

int myFunc(int var1, int var2)
{
 int varR;
 long long1;
 long1 = var1 * 2;
 varR = long1 / var2 * var1;
 return varR;
}

​in order to create a mixture of locals stored in registers and on the stack. These are shown quite clearly in Atmel Studio when I debug with a hardware device:

so as you can see, only 'var1' has been allocated to the stack, and the others to registers. So far so good. But when I look at the relevant section of the debug file, I see this (for 'var1' and 'var2'):

 <2><673>: Abbrev Number: 5 (DW_TAG_formal_parameter)
    <674>   DW_AT_name        : (indirect string, offset: 0x31f): var1
    <678>   DW_AT_decl_file   : 1
    <679>   DW_AT_decl_line   : 24
    <67a>   DW_AT_type        : <0x62b>
    <67e>   DW_AT_location    : 0x47 (location list)
 <2><682>: Abbrev Number: 5 (DW_TAG_formal_parameter)
    <683>   DW_AT_name        : (indirect string, offset: 0x324): var2
    <687>   DW_AT_decl_file   : 1
    <688>   DW_AT_decl_line   : 24
    <689>   DW_AT_type        : <0x62b>
    <68d>   DW_AT_location    : 0x6b (location list)

Their locations are described in the same way, as entries 0x47 and 0x6b in the location list. But when I look at the location list for those numbers I see this:

    0000003f <End of list>
    00000047 0000008a 000000ae (DW_OP_reg24 (r24); DW_OP_piece: 1; DW_OP_reg25 (r25); DW_OP_piece: 1)
    00000057 000000ae 0000010a (DW_OP_fbreg: -11)
    00000063 <End of list>
    0000006b 0000008a 000000d6 (DW_OP_reg22 (r22); DW_OP_piece: 1; DW_OP_reg23 (r23); DW_OP_piece: 1)
    0000007b 000000d6 0000010a (DW_OP_fbreg: -9)
    00000087 <End of list>

Again they have the same structure, even though Atmel Studio is telling me that one of these variables is stored in r22/r23 and one at fp-11.

So, my question is, how do I determine where to look when I want to find the value of these variables? I've attached the full debug file if that helps!

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, solved this one now (I think). I found the ultimate reference for Dwarf 2 which helped!

    00000047 0000008a 000000ae (DW_OP_reg24 (r24); DW_OP_piece: 1; DW_OP_reg25 (r25); DW_OP_piece: 1)
    00000057 000000ae 0000010a (DW_OP_fbreg: -11)
    00000063 <End of list>

So, if the program counter is between 0x8a and 0xae, then my local variable sits in r24/25. If it's after 0xae then  it's at offset 11 from the frame.

The compiler is being clever and shifting things between registers and stack all the time to make the most efficient code.

I also need to check for the DW_OP_piece attributes, since in theory a variable needn't be in consecutive addresses, but I guess this won't be a problem until I get to working with non-basic types.

 

Last Edited: Tue. Mar 21, 2017 - 03:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

quilkin wrote:
I don't see why it should move halfway through the function but I'll go with that and see how I get on.
The compiler will tend to cache into registers variables it has immediate need to use but then move them out and other things into the working registers when those new items need fast access. Obviously the ultimate would be to try and keep everything in registers all the time but often there aren't enough for that. You will also find (optimised) code reordered where possible so all the actions on certain values are still performed while they are in registers 9assuming the program logic is maintained). That of course (in a debugger like AS7 leads to the "yellow arrow jumping around". If you are writing a debugger that can single step code you are going to see this too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oops, yes, thanks, edited my post before I saw your reply.

Since my debugger is aimed at inexperienced users, by default I'm calling for zero optimisation on the file that's being debugged. It'll waste program space but be less confusing to watch the single-step.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

quilkin wrote:
by default I'm calling for zero optimisation on the file that's being debugged.
An atrocious idea. No one but no one should be encouraged to use the -O0 mode in GCC. It is only there as a test mode for the compiler developers so they can see code pre-optimisation. Almost the entire raison d'etre for using avr-gcc (an "optimising compiler") is lost if you use -O0 to run the main feature off.

 

What's more the code you now generate may not even work. suppose the user had used:

MCUSR = (1 << JTD);
MCUSR = (1 << JTD);

to switch off JTAG relying on the 4 cycle gate here (or maybe the CCP in Xmega?). You simply cannot get code to make those two writes in 4 cycles if you switch the optimiser off. So now they are going to spend days trying to work out why something that looks right in the C does not actually work in reality and your debugger is not going to help. In fact it insisting on -O0 is actually the cause!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the case of switching JTAG off, just the act of single stepping will defeat the 4 cycle requirement...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

An atrocious idea.

Hmmm. Maybe. That's interesting, thanks for the warning. I can easily change the optimisation level later; setting it to zero makes it easier for novice debugger developers as well!

This debugger is software-only, so users won't have JTAG devices. And AFAIK there aren't any Arduino boards (the target here) that use the Xmega devices. 

I could also parse the code to look for calls to CCP (or similar, if there are also concerns with short-cycle operations on atMega328 or 256 devices) to warn the user if such methods are being used (by a library routine; it's unlikely that my target audience will be using such constructs themselves because mostly they're very new to 'C' development).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

quilkin wrote:

I'm building a  software debugger (for AtMega328p to start with, may extend later). 

 

Want to join forces? :D

 

http://www.avrfreaks.net/forum/software-debugging-extension-avrs

 

https://youtu.be/uuL8PTD9oLQ

https://youtu.be/p_YvdBPmX_E

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

razvanma wrote:

 

Want to join forces? :D

 

Thanks for the suggestion! I have had a quick look at your posts (and  a very quick peek at your code) and, prompted by your thread, another look at the CodeProject article you referred to (which I had seen before).

We all seem to be coming at this from slightly different angles; my target audience was for new Arduino users who would possibly be a bit freaked out by using an IDE as complex as Atmel Studio (it's also a very big app to install) or Eclipse.

 

However, also prompted by your thread I have done a bit more general research and found that a large chunk of what I've been writing is effectively re-inventing the wheel - I have been parsing a Dwarf file created from the .elf, to generate all the memory locations etc, when I should probably have just used avr-gdb (as the CodeProject article does). I'll have to modify my target stub to use that; if I decide to go that way I'll base it on the avr8-stub from CodeProject, but I am using a different debug interrupt. I'm also concerned by the size of that stub; my current one is much smaller; not much more than when 'Serial.print' is used for debugging, so effectively no real increase in the user's code size (since they won't need to use 'Serial.print' any more).

 

Because of the different approaches I doubt if there's much we can share, but if you like you can pm me, chris-at-[my username]-dot-co-dot-uk

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

quilkin wrote:

Because of the different approaches I doubt if there's much we can share

 

Somewhat true, if you decide to go with GDB not much we can share, if you are doing something custom, we can do a common protocol. My goal is to make the native code small / clean / configurable and use the minimum amount of RAM, I avoid push to stack as much as I can. I don't have too much time to invest so I went with "what can I do with the minimum amount of work". No need to contact you over the email, we can PM in this forum.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

quilkin wrote:
I have been parsing a Dwarf file created from the .elf, to generate all the memory locations etc, when I should probably have just used avr-gdb
Or at the very least just use libelf and libdwarf:

 

https://sourceforge.net/p/libdwa...