Revision 1 - 10 Jan. 2005
This Application Note gives you information about the ELF/DWARF output generated by the Keil LA Linker/Locater for ARM. This information is intended for anyone who wants use the debugging information generated by the Keil Development Suite for ARM. It is assumed that you are familiar with ELF/DWARF Version 2 and 3 specifications.
Information in this file, the accompany manuals, and software
is
Copyright © Keil Software, Inc and Keil Elektronik GmbH.
All rights reserved.
Revision History:
Rev 1: 10. Jan. 2005: Initial Version
The Keil LA Linker/Locater for ARM (that is part of the Keil Development
Suite for ARM Version 2 or higher) generates an ELF/DWARF output file
based on the following specification (available at http://dwarf.freestandards.org):
DWARF Debugging Information Format Version 3 Draft 9 (November 12, 2003)
All example outputs are created with the DisELF.EXE utility which is part of the Keil
Development Suite for ARM. This Application Note explains only the
implementation specific issues.
A compile unit usually refers to a source module of the user application. The ELF output from the linker contains a special compilation unit, called the TypeContainer. It contains all type entities of all other compilation units. The TypeContainer uses the following abbreviations and debug info:
.debug_abbrev:
.debug_info:
DW_TAG_compile_unit
DW_AT_name DW_FORM_string
'<TypeContainer>'
DW_AT_producer DW_FORM_string 'Keil LA Linker'
No other attributes are specified for the TypeContainer. All type references from any other compilation unit uses references of
the form:
DW_AT_type DW_FORM_ref_addr
<offset into TypeContainer>
The type entities are generated on demand, therefore they do not necessarily
reflect the order of their declaration.
The debug information is contained in different ELF-sections. A list of the generated sections is shown in the table below:
| Elf-Section | Description |
|---|---|
| .symtab | symbol table, contains file level variables (global and local), function names and mode range symbols |
| .debug_loc | location lists, used to specify the frame pointer for functions |
| .debug_line | contains the line number information |
| .strtab | string table, contains identifiers |
| .shstrtab | a table containing the ELF-section names |
| .debug_abbrev | contains the debug abbreviations. Used as template to interpret the content of the .debug_info section |
| .debug_info | the debug information, the layout is defined by the debug abbreviations. |
| .debug_frame | Call frame information. Used to find the return address at any point within a function. |
The symbol table section contains the names of all file level global and static variables, functions and special mode range symbols in absolute format. The address and the size is also specified. Symbols which are part of functions such as parameters and automatic names are not listed in this section. They are listed in the .debug_info section with their location and type.
The mode range symbols are artificial, in that they are generated by the Linker. With the help of the mode range symbols, the code disassembly has the required information to always show the correct instructions (arm vs. thumb vs. data), regardless of the current cpu mode. The following range mode symbols are used:
| Symbol | Description |
|---|---|
| $a | specifies arm mode range (32-Bit code) |
| $t | specifies thumb mode range (16-Bit code) |
| $d | specifies a constant or data range (no code) |
Example:
...
5: '$a', val=0x010000D4, size=96, info=0x02, shndx=SHN_ABS(0xFFF1)
6: '$t', val=0x01000134, size=2, info=0x02, shndx=SHN_ABS(0xFFF1)
7: '$d', val=0x01000138, size=16, info=0x01, shndx=SHN_ABS(0xFFF1)
...
111: 'current', val=0x000004B0, size=20, info=0x11, shndx=SHN_ABS(0xFFF1)
112: 'measurement_interval', val=0x000004C4, size=4, info=0x11, shndx=SHN_ABS(0xFFF1)
113: 'mdisplay', val=0x000004C8, size=4, info=0x11, shndx=SHN_ABS(0xFFF1)
114: 'startflag', val=0x000004CC, size=4, info=0x11, shndx=SHN_ABS(0xFFF1)
115: 'save_record', val=0x000004D0, size=400, info=0x11, shndx=SHN_ABS(0xFFF1)
116: 'sindex', val=0x00000660, size=4, info=0x11, shndx=SHN_ABS(0xFFF1)
With the debug_loc entries, the current frame pointer (normally SP, alias R13) is specified. The frame base defines the start of the automatic variables on the stack. Additionally, there may be an offset in cases where the frame base register is modified (for example, due to PUSH instructions).
The generated info uses one of two formats:
| Format | Description |
|---|---|
| direct | DW_AT_frame_base DW_FORM_block1 framebase-reg (frame base register is given directly) |
| indirect | DW_AT_frame_base DW_FORM_data4 Ref to location-list in .debug_loc (frame base is specified via location list(s)) |
The generated output uses the indirect method for high level code, such as C functions, and the direct method for assembly language code.
Example:
void set_interval (char * buffer) {
struct interval itime; /* temporary interval record */
int min, sec, msec;
int args; /* number of arguments */
01000E6C B530 PUSH {R4-R5,LR}
01000E6E ---- Variable 'buffer' assigned to Register 'R0' ----
01000E6E B084 SUB R13,#0x10
<< local frame is allocated: SP -= 0x10 >>
<< local frame starts @SP+#0 >>
01000E70 ; SCOPE-START
01000E70 A903 ADD R1,R13,#0xC
01000E72 B402 PUSH {R1}
<< [1] PUSH: local frame starts @SP+#4 >>
01000E2C 492A LDR R1,[R15,#168] ; PoolRef @0x1000ED8 ; ??S_5
args = sscanf (buffer, "%d:%d.%d", /* scan input line for */
&min, /* minute, second and milliseconds */
&sec,
&msec);
/* check valid inputs */
01000E7A F000 BL sscanf?T ; T=0x10010B9 (1)
01000E7C F91D BL sscanf?T ; T=0x10010B9 (2)
01000E7E B001 ADD R13,#0x4
01000E80 ---- Variable 'args' assigned to Register 'R0' ----
<< [1] ADD SP,#0x04: local frame starts @SP+#0 >>
if (sec > 59 || msec > 999 || args < 2 || args == EOF) {
printf (ERROR, "INVALID INTERVAL FORMAT");
}
else { /* if inputs are valid then */
...
AIC_IECR = (1<<TC0_ID); /* enable interrupts again */
}
01000F1C B004 ADD R13,#0x10
01000F1E BD30 POP {R4-R5,PC}
01000F20 ; END 'set_interval?T'
}
The location list is specified by using the DW_AT_frame_base attribute value:
000b0e: Abbrev=13 DW_TAG_compile_unit(0x11) // the enclosing compile unit 000B0F: DW_AT_name 'Mcommand.c' 000B1A: DW_AT_producer 'Keil CA Compiler' 000B2B: DW_AT_stmt_list 0x000001A5 000B2F: DW_AT_low_pc 0x01000D70 // low_pc: adjust value for the location list 000B33: DW_AT_high_pc 0x01000F20 ...... 000c10: Abbrev=16 DW_TAG_subprogram(0x2E) 000C11: DW_AT_sibling DW_FORM_ref4 0x18B (0xC8E) 000C15: DW_AT_name 'set_interval' 000C22: DW_AT_external 0x01 000C23: DW_AT_low_pc 0x01000E6C 000C27: DW_AT_high_pc 0x01000F20 000C2B: DW_AT_frame_base 0x0000011B // Refers to .debug_loc [0x11B] 000C2F: DW_AT_type DW_FORM_ref_addr 0x332 ......
The corresponding debug_loc entries are as follows:
*** Section-Dump of '.debug_loc', pos=0x4F7A, size=1340 ... 00011B: beg=0x0000FC, end=0x0001B0, DW_OP_reg [R13] // local frame starts at @SP+#0 000126: beg=0x000104, end=0x000110, DW_OP_breg [R13+4] // local frame starts at @SP+#4 ...
Please note that the debug_loc ranges (beg ... end) are relative to the lowest address of the enclosing compilation unit. Therefore, the 'beg' and 'end' values need to be adjusted by simply adding the 'low_pc'. In our example, the compile unit 'Mcommand.C' contains the subprogram 'set_interval()' and the low_pc value of the compile unit is 0x1000D70. This value must be added to the 'beg' and 'end' values to give the absolute physical address ranges.
With the debug_frame entries, the current location of the function return address can be obtained. This feature is typically used in debuggers to perform a 'Step out of current function'. Depending on the current address within a function, the return address is either contained in the link register (R14 alias LR) or somewhere on the stack. The purpose is to retrieve the return address at any point within a function. In addition, the implementation also contains the start of local frame and the current stack depth.
The implementation uses two extra 'virtual' registers given to the DW_CFA_def_cfa attribute. The registers R257 and R256 specify the stack depth and start of local frame:
| Format | Description |
|---|---|
| Reg=R14, Offs=0x0000 | return address is @current frame base reg + 0x00 on Stack |
| Reg=R14, Offs=0x0010 | return address is @current frame base reg + 0x10 on Stack |
| Reg=R257, Offs=0x0014 | R257 used to give the current stack depth (0x14 in this example) |
| Reg=R256, Offs=0x0014 | R256 used to give the start of the local frame (@current frame base reg + 0x14 in this example) |
The current frame base register (normally 'SP' alias R13) is actually specified per function via '.debug_loc'.
The return address register is defined in a Common Information Entry, for
example:
CIE 0x000288: Len=16, CIE_id=0xFFFFFFFF, Vers=1
Augment: ''
CodeAlign: 1
DataAlign: -4
ReturnReg: R14 //
register holding the return address
If the return address register is saved on the stack, then this situation is
recorded with a frame description entry, for example:
FDE 0x000327: Len=19, CIE=0x288, InitLoc=0x1000C74, Range=0x54
// Range: 0x1000C74 ... 0x1000C74+0x54
DW_CFA_def_cfa Reg=R14, Offs=0x10
// return address can be found @frame-base-reg + 0x10 on stack.
Example of a frame description for 'start of the local frame':
DW_CFA_def_cfa Reg=R256, Offs=0x14 // Reg 256 denotes
'local frame offset', 0x14 bytes above the current value of the frame pointer
For example, the following frame description entry specifies the stack depth
to be 4:
DW_CFA_def_cfa Reg=R257, Offs=0x4 // Reg
257 denotes 'stack depth', Offset means 'stack depth'
Example: Code example, only the instructions modifying the SP value and
their successor are shown in the code. RA denotes the return address, LFS
denotes the start of the local frame (the automatics) relative to the current
frame pointer:
01000ADC main:
01000ADC B500 PUSH {LR} // RA := R14 (alias LR), LFS := <invalid>
01000ADE B084 SUB R13,#0x10 // RA := @SP+#0, LFS := <invalid>
01000AE0 497C LDR R1,[R15,#496] // RA := @SP+#16, LFS := @SP+#0
.....
01000C00 B085 SUB R13,#0x14 // RA := @SP+#16
01000C02 4669 MOV R1,R13 // RA := @SP+#36, LFS := @SP+#20
.....
01000C12 B005 ADD R13,#0x14 // RA := @SP+#36, LFS := @SP+#20
01000C14 484A LDR R0,[R15,#296] // RA := @SP+#16, LFS := @SP+#0
.....
01000C60 B085 SUB R13,#0x14 // RA := @SP+#16, LFS := @SP+#0
01000C62 4669 MOV R1,R13 // RA := @SP+#36, LFS := @SP+#20
.....
01000C72 B005 ADD R13,#0x14 // RA := @SP+#36, LFS := @SP+#20
01000C74 4830 LDR R0,[R15,#192] // RA := @SP+#16, LFS := @SP+#0
.....
01000CC6 B004 ADD R13,#0x10 // RA := @SP+#16, LFS := @SP+#0
01000CC8 BD00 POP {PC} // RA := @SP+#0, LFS := <invalid>
01000CCA ; END 'main'
This is the generated frame information:
CIE 0x000288: Len=16, CIE_id=0xFFFFFFFF, Vers=1 Augment: '' CodeAlign: 1 DataAlign: -4 ReturnReg: R14 DW_CFA_def_cfa Reg=R13, Offs=0x0 FDE 0x00029C: Len=12, CIE=0x288, InitLoc=0x1000ADC, Range=0x1EE FDE 0x0002AC: Len=19, CIE=0x288, InitLoc=0x1000ADE, Range=0x1EA DW_CFA_def_cfa Reg=R257, Offs=0x4 DW_CFA_def_cfa Reg=R14, Offs=0x0 FDE 0x0002C3: Len=19, CIE=0x288, InitLoc=0x1000AE0, Range=0x1E8 DW_CFA_def_cfa Reg=R257, Offs=0x14 DW_CFA_def_cfa Reg=R14, Offs=0x10 FDE 0x0002DA: Len=23, CIE=0x288, InitLoc=0x1000C02, Range=0x12 DW_CFA_def_cfa Reg=R257, Offs=0x28 DW_CFA_def_cfa Reg=R14, Offs=0x24 DW_CFA_def_cfa Reg=R256, Offs=0x14 FDE 0x0002F5: Len=19, CIE=0x288, InitLoc=0x1000C14, Range=0xB4 DW_CFA_def_cfa Reg=R257, Offs=0x14 DW_CFA_def_cfa Reg=R14, Offs=0x10 FDE 0x00030C: Len=23, CIE=0x288, InitLoc=0x1000C62, Range=0x12 DW_CFA_def_cfa Reg=R257, Offs=0x28 DW_CFA_def_cfa Reg=R14, Offs=0x24 DW_CFA_def_cfa Reg=R256, Offs=0x14 FDE 0x000327: Len=19, CIE=0x288, InitLoc=0x1000C74, Range=0x54 DW_CFA_def_cfa Reg=R257, Offs=0x14 DW_CFA_def_cfa Reg=R14, Offs=0x10 FDE 0x00033E: Len=19, CIE=0x288, InitLoc=0x1000CC8, Range=0x2 DW_CFA_def_cfa Reg=R257, Offs=0x4 DW_CFA_def_cfa Reg=R14, Offs=0x0
The same information can be represented in a more compact form and yields the following information:
Beg=0x1000ADC, End=0x1000CCA, SP=R13, Depth:=0 LR:=R14 // SP:=R13, RA in R14(LR) Beg=0x1000CC8, End=0x1000CCA, SP=R13, Depth:=4 LR:=[SP+0] // RA @SP+#0 Beg=0x1000C74, End=0x1000CC8, SP=R13, Depth:=20 LR:=[SP+16] // depth=20, RA @SP+#16 Beg=0x1000C62, End=0x1000C74, SP=R13, Depth:=40 LR:=[SP+36] Locals:=[SP+20] // depth=40, RA @SP+#36, LFS @SP+#20 Beg=0x1000C14, End=0x1000CC8, SP=R13, Depth:=20 LR:=[SP+16] // ... Beg=0x1000C02, End=0x1000C14, SP=R13, Depth:=40 LR:=[SP+36] Locals:=[SP+20] Beg=0x1000AE0, End=0x1000CC8, SP=R13, Depth:=20 LR:=[SP+16] Beg=0x1000ADE, End=0x1000CC8, SP=R13, Depth:=4 LR:=[SP+0]
The frame base register 'SP' is actually specified via '.debug_loc' per function. The start of the local frame can also be obtained via '.debug_loc'.
Note:
If you experience any problems or have any questions about this Application Note, contact one of our distributors or offices for assistance.
|
In the USA... |
In Europe... |
Copyright © Keil Software, Inc. and Keil Elektronik GmbH.
All rights reserved.
Visit our web site at www.keil.com.