天天看點

gdbserver調試共享庫(終結版)

我 已經寫過兩篇關于gdbserver調試共享庫的BLOG了:第一篇解決了調試共享庫的難題,讓調試共享庫成為可能,但是使用起來很麻煩。第二篇做了點改 進,通過一個腳本檔案計算偏移量,使用起來稍微友善一點。幾年過去了,gdbserver還是不支援調試共享庫,我也受夠了,最終決定去修改了gdb的代 碼。其實我們要做的就是計算共享庫加載符号表的位址,算法很簡單:

共享庫加載符号表的位址 = 共享庫在記憶體中的加載位址+代碼段的偏移量。

實作原理如下:

o 通過gdbserver讀/proc/$pid/maps檔案,以擷取共享庫在記憶體中的加載位址。

o 通過bfd查詢共享庫的代碼段的偏移量。

o 計算共享庫加載符号表的位址。

o 調用add-symbol-file加載符号表。

具體實作如下:

o gdbserver增加qMaps請求(gdb/gdbserver/server.c)

void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
{
…
  if (strncmp (own_buf, "qMaps;", 5) == 0)
    {
      handle_q_maps (own_buf);
      return;
    }
…
}      

o gdbserver處理qMaps請求

handle_q_maps (char *own_buf)
{
    FILE* fp = NULL;
    char file_name[256] = {0};

    snprintf(file_name, sizeof(file_name), "/proc/%ld/maps", signal_pid);
    if((fp = fopen(file_name, "rb")) != NULL)
    {
        char line[512] = {0};
        while(fgets(line, sizeof(line), fp) != NULL)
        {
            putpkt(line);
        }
        putpkt("<end>");
        fclose(fp);
    }      

包的大小有限制(2000),是以我們隻傳一行過去,最後再發”“表示傳輸結束。

o修改gdb/remote.c解析maps檔案。

static Maps* maps_parse(Maps* maps, char* maps_str)
{
  char* line = maps_str;
  char* next_line = NULL;
  while(line != NULL)
  {
    next_line = strchr(line, '/n');
    if(next_line != NULL)
    {
      *next_line = '/0';
      next_line++;
    }

    if(strstr(line, "r-xp") != NULL && strstr(line, ".so") != NULL)
    {
      int unused = 0;
      MapsItem item;
      memset(&item, 0x00, sizeof(item));

      sscanf(line, "%08x-%08x r-xp %08x %02x:%02x %d %s",
        &(item.start), &(item.end), &unused, &unused, &unused, &unused, item.file_name);
      maps_add(maps, &item);
}
    line = next_line;
  }

  return maps;
}      

o修改gdb/remote.c擷取代碼段的偏移量。

int remote_get_so_vma(const char* file_name, size_t* vma)
{
  char **matching = NULL;
  bfd* b = bfd_openr(file_name, NULL);

  if(b != NULL && bfd_check_format_matches (b, bfd_object, &matching))
  {
    asection* s = bfd_get_section_by_name(b, ".text");
    if(s != NULL)
    {
      *vma = s->vma;

      return 1;
    }
  }

  return 0;
}      

o 修改gdb/symfile.c實作add_shared_symbol_files_command,這個函數在linux下沒有實作。

static void
add_shared_symbol_files_command (char *args, int from_tty)
{
#ifdef ADD_SHARED_SYMBOL_FILES
  ADD_SHARED_SYMBOL_FILES (args, from_tty);
#else
/*support sharelib: {*/
  extern void   remote_list_so(void);
  extern int    remote_get_so_nr(void);
  extern int    remote_get_so_vma(const char* file_name, size_t* vma);
  extern int    remote_get_so_text_start_addr(const char* file_name, size_t* addr);
  extern int    remote_get_so_name_text_start_addr(int index, const char** file_name, size_
t* addr);

  if (current_target.to_shortname &&
      (strcmp (current_target.to_shortname, "remote") == 0
       || strcmp (current_target.to_shortname, "extended-remote") == 0))
  {
    size_t addr = 0;
char cmd[2048] = {0};
if(args == NULL || args[0] == '/0')
    {
      printf_unfiltered("available so:/n");
      remote_list_so();
      printf_unfiltered("usage: add-shared-symbol-files file/n");
      printf_unfiltered("usage: add-shared-symbol-files all remote_path:local_path/n");

      return;
    }
    else if(strncmp(args, "all ", 4) == 0)
    {
      size_t i = 0;
      size_t n = 0;
      int rlen = 0;
      char rpath[260] = {0};
      char lpath[260] = {0};
      char* p = strchr(args, ':');
      const char* file_name = NULL;
      if(p == NULL) return;

      *p = '/0';
      strncpy(lpath, p + 1, sizeof(lpath));
strncpy(rpath, args + 4, sizeof(rpath));

      rlen = strlen(rpath);
      n = remote_get_so_nr();
      for(i = 0; i < n; i++)
      {
        remote_get_so_name_text_start_addr(i, &file_name, &addr);
        if(file_name != NULL && strncmp(file_name, rpath, rlen) == 0)
        {
          size_t vma = 0;
          char lfile_name[512] = {0};

          snprintf(lfile_name, sizeof(lfile_name),"%s/%s", lpath, file_name+rlen);
          if(remote_get_so_vma(lfile_name, &vma))
          {
            addr += vma;
          }
          snprintf(cmd, sizeof(cmd),"%s/%s %p", lpath, file_name+rlen, (void*)addr);
          add_symbol_file_command(cmd, 0);
        }
      }
    }
    else
    {
      remote_get_so_text_start_addr(args, &addr);

      if(addr > 0)
      {
        size_t vma = 0;
        if(remote_get_so_vma(args, &vma))
        {
          addr += vma;
        }
        snprintf(cmd, sizeof(cmd), "%s %p", args, (void*)addr);
        add_symbol_file_command(cmd, from_tty);
      }
      else
      {
        printf_unfiltered("%s is not found./n", args);
      }
    }
    return;
  }
/*support sharelib: }*/
  error (_("This command is not available in this configuration of GDB."));
#endif
}      

使用方法:

o 加載單個共享庫的符号表。add-shared-symbol-files 共享庫檔案名(本地絕對路徑).

o 加載多個共享庫的符号表。add-shared-symbol-files all 遠端路徑:本地路徑

因為加載符号表時是加載本地檔案,是以加載多個檔案時,要把遠端路徑替換成本地路徑。