GDB中打印ART基础类

时间:2021-08-29 15:47:04

【art::mirror::String】

(gdb) p /x  *('art::mirror::String' *)0x71237d00
$89 = {
  ...
  count_ = 0x11, 
 hash_code_ = 0x6c3617af, 
  value_ = 0x71237d10, 
  ...
}

其中count_是字符串长度,value_是字符串buffer,buffeer里的内容是Unicode字符串:

(gdb) x /17hc 0x71237d10
0x71237d10:	97 'a'	110 'n'	100 'd'	114 'r'	111 'o'	105 'i'	100 'd'	46 '.'
0x71237d20:	118 'v'	105 'i'	101 'e'	119 'w'	46 '.'	86 'V'	105 'i'	101 'e'
0x71237d30:	119 'w'

因此,这个String类对象是"android.view.View"

对应脚本及用法如下:

define art_print_string
  set $local_string = ('art::mirror::String' *) $arg0
  set $string_length  = (int) $local_string->count_

  set $declaringstr = (char*)($local_string->value_)

  set $logcal_index = (int) 0
  set $string_length = (int) 2*$string_length

  while $logcal_index < $string_length
    printf "%c", *($declaringstr + $logcal_index)
    set $logcal_index = $logcal_index + 2
  end

end

(gdb) art_print_string 0x71237d00
android.view.View

 

【art::mirror::Class】

(gdb) p /x  *('art::mirror::Class' *)0x70eac5e0
$90 = {
  ...
  dex_cache_ = {
    <art::mirror::ObjectReference<false, art::mirror::DexCache>> = {
      reference_ = 0x70dac310
    }, <No data fields>}, 
  ...
name_ = { <art::mirror::ObjectReference<false, art::mirror::String>> = { reference_ = 0x71237d00 }, <No data fields>}, ... dex_type_idx_ = 0x61f, ... } }

这里分两种,如果类被Resolve过,则就可以直接通过art::mirror::String类的name_字段获取类名

否则就得从dex_file_中读取,而dex_file_的地址保存在dex_cache_中:

(gdb) p /x *('art::mirror::DexCache' *)0x70dac310
$97 = {
  ...
  dex_file_ = 0x7f9ba7b780, 
  resolved_fields_ = 0x71a80078, 
  resolved_methods_ = 0x719c8b78, 
  ...
}

 

dex_file_的结构如下:

(gdb) p /x  *('art::DexFile' *)0x7f9ba7b280
$98 = {
  begin_ = 0x72ffa3cc, 
  size_ = 0x7fdf88, 
  ...
  header_ = 0x72ffa3cc, 
  string_ids_ = 0x72ffa43c, 
  type_ids_ = 0x73046ab4, 
  field_ids_ = 0x7306afe0, 
  method_ids_ = 0x730b24c8, 
  proto_ids_ = 0x7304c7a0, 
  class_defs_ = 0x7311d350, 
  oat_dex_file_ = 0x7f9ba2f200, 
  ...
}

根据dex_file_中的type_ids_指针和class中的dex_type_idx_,找到该类的TypeId

(gdb) p /x *(('art::DexFile::TypeId' *)0x73046ab4+0x61f)
$101 = {
  descriptor_idx_ = 0x3741
}

这个descriptor_idx_就是类名字符串的StringId在string_ids_中的偏移:

(gdb) p *(('art::DexFile::StringId' *)0x72ffa43c+0x3741)
$102 = {
  string_data_off_ = 0x52f79d
}

最终得到的string_data_off_就是类名字符串真正存放的地址:dex_file_(base = 0x73ffa3cc)中的相对偏移。

因此,最终的字符串的地址是:

(gdb) p /x 0x72ffa3cc+0x52f79d
$103 = 0x73529b69

这个地址存放的是Leb128类型的字符串,对于长度小于128的字符串,它的长度放在buffer的第一个位置,后面存放的就是字符串的ASCII码:

(gdb) x /b 0x73529b69
0x73529b69:	0x13
(gdb) x /s 0x73529b69+1 0x73529b6a: "Landroid/view/View;"

对应脚本及用法如下:

define art_print_class
  set $myclass = ('art::mirror::Class' *)$arg0
  set $classstring = ('art::mirror::String' *)($myclass->name_.reference_)

  if $classstring != 0
    art_print_string $classstring
  else
    set $dexfile = ('art::DexFile' *) (('art::mirror::DexCache' *)$myclass->dex_cache_.reference_)->dex_file_
    set $dextypeidx = (int) $myclass->dex_type_idx_
    set $descriptoridx = (int) ($dexfile->type_ids_ + $dextypeidx)->descriptor_idx_
    set $classnameptr = (char*) ($dexfile->string_ids_[$descriptoridx].string_data_off_ + $dexfile->begin_ + 1)

    art_print_cstring $classnameptr
  end
end

define art_print_cstring
  set $buffer = (char*)$arg0
  if *$buffer == 'L'
    set $buffer = $buffer + 1
    while *$buffer != ';'
      if *$buffer == 0
        loop_break
      end

      if *$buffer == '\/'
        printf "."
      else
        printf "%c", *$buffer
      end
        set $buffer = $buffer + 1
    end
  else
    printf "%s", $buffer
  end
end

(gdb) art_print_class 0x70eac5e0
android.view.View

 

【art::mirror::Object】

(gdb) p /x *('art::mirror::Object' *)0x1354a5e0
$114 = {
  static kVTableLength = 0xb, 
  static hash_code_seed = {
    <std::__1::atomic<unsigned int>> = {
      <std::__1::__atomic_base<unsigned int, true>> = {
        <std::__1::__atomic_base<unsigned int, false>> = {
          __a_ = 0xf2e119b1
        }, <No data fields>}, <No data fields>}, <No data fields>}, 
  klass_ = {
    <art::mirror::ObjectReference<false, art::mirror::Class>> = {
      reference_ = 0x70eac5e0
    }, <No data fields>}, 
  monitor_ = 0x0
}

对于Object来说,只需要打印所属类名就可以了。

对应脚本及用法如下:

define art_print_object
   set $curclass = ('art::mirror::Class' *)((('art::mirror::Object' *)$arg0)->klass_.reference_)
   art_printn_class $curclass
end

(gdb) art_print_object 0x1354a5e0
android.view.View

 

【art::ArtMethod】

(gdb) p /x *('art::ArtMethod' *)0x7144b280
$115 = {
  declaring_class_ = {
    root_ = {
      <art::mirror::ObjectReference<false, art::mirror::Object>> = {
        reference_ = 0x70eac5e0
      }, <No data fields>}
  }, 
  access_flags_ = 0x90001, 
  dex_code_item_offset_ = 0x1d5968, 
  dex_method_index_ = 0x307a, 
  method_index_ = 0xa, 
  hotness_count_ = 0x0, 
  ptr_sized_fields_ = {
    dex_cache_resolved_methods_ = 0x719c8b7800000000, 
    dex_cache_resolved_types_ = 0x719c2e8800000000, 
    entry_point_from_jni_ = 0x0, 
    entry_point_from_quick_compiled_code_ = 0x75f568b400000000
  }
}

打印方法名分三部分:

1、方法所属类

2、方法名

3、参数及返回值

 

方法所属类这个可以简单的打印ArtMethod的declaring_class_即可

(gdb) art_print_class 0x70eac5e0
android.view.View

可以通过declaring_class_找到DexCache,进而找到DexFile。

通过ArtMethod中的dex_method_index和DexFile中的method_ids_,找到该方法对应的MethodId:

(gdb) p /x  *('art::DexFile' *)0x7f9ba7b280
$98 = {
  begin_ = 0x72ffa3cc, 
  size_ = 0x7fdf88, 
  ...
  header_ = 0x72ffa3cc, 
  string_ids_ = 0x72ffa43c, 
  type_ids_ = 0x73046ab4, 
  field_ids_ = 0x7306afe0, 
  method_ids_ = 0x730b24c8, 
  proto_ids_ = 0x7304c7a0, 
  class_defs_ = 0x7311d350, 
  oat_dex_file_ = 0x7f9ba2f200, 
  ...
}

(gdb) p /x *(('art::DexFile::MethodId' *)0x730b24c8+0x307a)
$117 = {
  class_idx_ = 0x61f, 
  proto_idx_ = 0x155e, 
  name_idx_ = 0x9f2
}

其中MethodId中的

class_idx_是方法所属类的TypeId的索引值,通过前面的分析可知,0x61f对应的就是android.view.View。

proto_idx_是方法原型的索引,通过这个索引可在DexFile中的proto_ids_中查找该方法对应的原型。

name_idx_是方法名在字符串池中的偏移。

 

先看看方法名:

(gdb) x /8c (('art::DexFile::StringId' *)0x72ffa43c+0x9f2).string_data_off_ + 0x72ffa3cc
0x734e212c:	6 '\006'	60 '<'	105 'i'	110 'n'	105 'i'	116 't'	62 '>'	0 '\000'

方法名是长度为6的字符串"<init>",也就是android.view.View类的构造函数。

 

再看看方法的原型:

(gdb) p /x *(('art::DexFile::ProtoId' *)0x7304c7a0+0x155e)
$120 = {
  shorty_idx_ = 0x764b, 
  return_type_idx_ = 0x1588, 
  pad_ = 0x0, 
  parameters_off_ = 0x4c51b4
}

return_type_idx_是方法的返回值在DexFile的TypeId(0x73046ab4)数组里的偏移。

(gdb)  p /x *(('art::DexFile::TypeId' *)0x73046ab4+0x1588)
$121 = {
  descriptor_idx_ = 0x745a
}

(gdb) x /8c (('art::DexFile::StringId' *)0x72ffa43c+0x745a).string_data_off_ + 0x72ffa3cc
0x735a4fb9:	1 '\001'	86 'V'	0 '\000'	16 '\020'	86 'V'	49 '1'	95 '_'	80 'P'

返回值类型是长度为1的字符串"V"

 

parameters_off_(0x4c51b4)是参数列表距dexfile起始位置(0x72ffa3cc)的的偏移:

(gdb) p /x * ('art::DexFile::TypeList' *)(0x72ffa3cc+0x4c51b4)
$122 = {
  size_ = 0x3, 
  list_ = {{
      type_idx_ = 0x9b
    }}
}

该函数有3个参数,每个参数的在TypeId中的偏移为:

(gdb) p /x  (('art::DexFile::TypeList' *)(0x72ffa3cc+0x4c51b4))->list_[0]
$124 = {
  type_idx_ = 0x9b
}
(gdb) p /x  (('art::DexFile::TypeList' *)(0x72ffa3cc+0x4c51b4))->list_[1]
$125 = {
  type_idx_ = 0x48a
}
(gdb) p /x  (('art::DexFile::TypeList' *)(0x72ffa3cc+0x4c51b4))->list_[2]
$126 = {
  type_idx_ = 0x4
}

三个参数descriptor_idx_及字符串为:

(gdb)  p /x (('art::DexFile::TypeId' *)0x73046ab4+0x9b).descriptor_idx_
$130 = 0x3149

(gdb)  p /x (('art::DexFile::TypeId' *)0x73046ab4+0x48a).descriptor_idx_
$131 = 0x356d

(gdb)  p /x (('art::DexFile::TypeId' *)0x73046ab4+0x4).descriptor_idx_
$132 = 0x27cc

(gdb) x /26c (('art::DexFile::StringId' *)0x72ffa43c+0x3149).string_data_off_ + 0x72ffa3cc
0x7351ac73:	25 '\031'	76 'L'	97 'a'	110 'n'	100 'd'	114 'r'	111 'o'	105 'i'
0x7351ac7b:	100 'd'	47 '/'	99 'c'	111 'o'	110 'n'	116 't'	101 'e'	110 'n'
0x7351ac83:	116 't'	47 '/'	67 'C'	111 'o'	110 'n'	116 't'	101 'e'	120 'x'
0x7351ac8b:	116 't'	59 ';'

(gdb)  x /28c (('art::DexFile::StringId' *)0x72ffa43c+0x356d).string_data_off_ + 0x72ffa3cc
0x735253f4:	27 '\033'	76 'L'	97 'a'	110 'n'	100 'd'	114 'r'	111 'o'	105 'i'
0x735253fc:	100 'd'	47 '/'	117 'u'	116 't'	105 'i'	108 'l'	47 '/'	65 'A'
0x73525404:	116 't'	116 't'	114 'r'	105 'i'	98 'b'	117 'u'	116 't'	101 'e'
0x7352540c:	83 'S'	101 'e'	116 't'	59 ';'

(gdb) x /2c (('art::DexFile::StringId' *)0x72ffa43c+0x27cc).string_data_off_ + 0x72ffa3cc
0x7350ed6e:	1 '\001'	73 'I'

 

分别是

长度为25的"Landroid/context/Context;"

长度为27的"Landroid/util/AttributeSet;"

长度为1的"I"

 

将上诉内容用脚本实现:

define art_get_method_name_by_method_id
  set $methodid = ('art::ArtMethod' *)$arg0
  set $declaringclass = ('art::mirror::Class' *) $methodid->declaring_class_.root_.reference_

  set $dexfile = ('art::DexFile' *) (('art::mirror::DexCache' *) $declaringclass->dex_cache_.reference_)->dex_file_

  art_print_class $declaringclass

  set $methodidx = (int) $methodid->dex_method_index_

  set $methodnameidx = (int) $dexfile->method_ids_[$methodidx]->name_idx_
  set $methodstr = (char*) ($dexfile->string_ids_[$methodnameidx].string_data_off_ + $dexfile->begin_ + 1)
  printf ".%s ", $methodstr

  set $protoid = ('art::DexFile::ProtoId' *)$dexfile->proto_ids_ + $dexfile->method_ids_[$methodidx]->proto_idx_

  printf "\"("

  if $protoid->parameters_off_ != 0
    set $typelist = ('art::DexFile::TypeList' *)($dexfile->begin_ + $protoid->parameters_off_)
    set $typelistsize = (int)$typelist->size_
    set $typelistitems = (unsigned short *) ($typelist->list_)

    set $logcal_index = (int) 0

    while $logcal_index < $typelistsize
      set $descriptoridx = (int) ($dexfile->type_ids_ + *($typelistitems +$logcal_index))->descriptor_idx_
      set $paramstr = (char*) ($dexfile->string_ids_[$descriptoridx].string_data_off_ + $dexfile->begin_ + 1)
      printf "%s", $paramstr
      set $logcal_index = $logcal_index + 1
    end
  end

  printf ")"

  set $descriptoridx = (int) ($dexfile->type_ids_ + $protoid->return_type_idx_)->descriptor_idx_
  set $returnstr = (char*) ($dexfile->string_ids_[$descriptoridx].string_data_off_ + $dexfile->begin_ + 1)
  printf "%s\"\n", $returnstr

end

用法如下:

(gdb) art_get_method_name_by_method_id 0x7144b280
android.view.View.<init> "(Landroid/content/Context;Landroid/util/AttributeSet;I)V"