return 0
}
+# Return a list of expressions about function FUNC's address and length.
+# The first expression is the address of function FUNC, and the second
+# one is FUNC's length. SRC is the source file having function FUNC.
+# An internal label ${func}_label must be defined inside FUNC:
+#
+# int main (void)
+# {
+# asm ("main_label: .globl main_label");
+# return 0;
+# }
+#
+# This label is needed to compute the start address of function FUNC.
+# If the compiler is gcc, we can do the following to get function start
+# and end address too:
+#
+# asm ("func_start: .globl func_start");
+# static void func (void) {}
+# asm ("func_end: .globl func_end");
+#
+# however, this isn't portable, because other compilers, such as clang,
+# may not guarantee the order of global asms and function. The code
+# becomes:
+#
+# asm ("func_start: .globl func_start");
+# asm ("func_end: .globl func_end");
+# static void func (void) {}
+#
+
+proc function_range { func src } {
+ global decimal gdb_prompt
+
+ set exe [standard_temp_file func_addr[pid].x]
+
+ gdb_compile $src $exe executable {debug}
+
+ gdb_exit
+ gdb_start
+ gdb_load "$exe"
+
+ # Compute the label offset, and we can get the function start address
+ # by "${func}_label - $func_label_offset".
+ set func_label_offset ""
+ set test "p ${func}_label - ${func}"
+ gdb_test_multiple $test $test {
+ -re ".* = ($decimal)\r\n$gdb_prompt $" {
+ set func_label_offset $expect_out(1,string)
+ }
+ }
+
+ # Compute the function length.
+ global hex
+ set func_length ""
+ set test "disassemble $func"
+ gdb_test_multiple $test $test {
+ -re ".*$hex <\\+($decimal)>:\[^\r\n\]+\r\nEnd of assembler dump\.\r\n$gdb_prompt $" {
+ set func_length $expect_out(1,string)
+ }
+ }
+
+ # Compute the size of the last instruction.
+ set test "x/2i $func+$func_length"
+ gdb_test_multiple $test $test {
+ -re ".*($hex) <$func\\+$func_length>:\[^\r\n\]+\r\n\[ \]+($hex).*\.\r\n$gdb_prompt $" {
+ set start $expect_out(1,string)
+ set end $expect_out(2,string)
+
+ set func_length [expr $func_length + $end - $start]
+ }
+ }
+
+ return [list "${func}_label - $func_label_offset" $func_length]
+}
+
# A DWARF assembler.
#
# All the variables in this namespace are private to the
# This can either be the full name, like 'DW_AT_name', or a shortened
# name, like 'name'. These are fully equivalent.
#
+# Besides DWARF standard attributes, assembler supports 'macro' attribute
+# which will be substituted by one or more standard or macro attributes.
+# supported macro attributes are:
+#
+# - MACRO_AT_range { FUNC FILE }
+# It is substituted by DW_AT_low_pc and DW_AT_high_pc with the start and
+# end address of function FUNC in file FILE.
+#
+# - MACRO_AT_func { FUNC FILE }
+# It is substituted by DW_AT_name with FUNC and MACRO_AT_range.
+#
# If FORM is given, it should name a DW_FORM_ constant.
# This can either be the short form, like 'DW_FORM_addr', or a
# shortened version, like 'addr'. If the form is given, VALUE
}
}
+ # Handle macro attribute MACRO_AT_range.
+
+ proc _handle_macro_at_range { attr_value } {
+ if {[llength $attr_value] != 2} {
+ error "usage: MACRO_AT_range { func file }"
+ }
+
+ set func [lindex $attr_value 0]
+ set src [lindex $attr_value 1]
+ set result [function_range $func $src]
+
+ _handle_attribute DW_AT_low_pc [lindex $result 0] \
+ DW_FORM_addr
+ _handle_attribute DW_AT_high_pc \
+ "[lindex $result 0] + [lindex $result 1]" DW_FORM_addr
+ }
+
+ # Handle macro attribute MACRO_AT_func.
+
+ proc _handle_macro_at_func { attr_value } {
+ if {[llength $attr_value] != 2} {
+ error "usage: MACRO_AT_func { func file }"
+ }
+ _handle_attribute DW_AT_name [lindex $attr_value 0] DW_FORM_string
+ _handle_macro_at_range $attr_value
+ }
+
proc _handle_DW_TAG {tag_name {attrs {}} {children {}}} {
variable _abbrev_section
variable _abbrev_num
foreach attr $attrs {
set attr_name [_map_name [lindex $attr 0] _AT]
set attr_value [uplevel 2 [list subst [lindex $attr 1]]]
- if {[llength $attr] > 2} {
- set attr_form [lindex $attr 2]
+
+ if { [string equal "MACRO_AT_func" $attr_name] } {
+ _handle_macro_at_func $attr_value
+ } elseif { [string equal "MACRO_AT_range" $attr_name] } {
+ _handle_macro_at_range $attr_value
} else {
- set attr_form [_guess_form $attr_value attr_value]
- }
- set attr_form [_map_name $attr_form _FORM]
+ if {[llength $attr] > 2} {
+ set attr_form [lindex $attr 2]
+ } else {
+ set attr_form [_guess_form $attr_value attr_value]
+ }
+ set attr_form [_map_name $attr_form _FORM]
- _handle_attribute $attr_name $attr_value $attr_form
+ _handle_attribute $attr_name $attr_value $attr_form
+ }
}
_defer_output $_abbrev_section {