首页 > 其他分享 >gdb如何比较core文件和image及buildid

gdb如何比较core文件和image及buildid

时间:2023-05-15 20:26:49浏览次数:40  
标签:__ core bfd buildid image GNU note build id

gdb

从git上看到的提交记录,关键的修改是在elf_core_file_matches_executable_p函数中添加的对于build_id的比较。

///@file: gdb-10.1\bfd\elfcore.h
bfd_boolean
elf_core_file_matches_executable_p (bfd *core_bfd, bfd *exec_bfd)
{
  char* corename;

  /* xvecs must match if both are ELF files for the same target.  */

  if (core_bfd->xvec != exec_bfd->xvec)
    {
      bfd_set_error (bfd_error_system_call);
      return FALSE;
    }

  /* If both BFDs have identical build-ids, then they match.  */
  if (core_bfd->build_id != NULL
      && exec_bfd->build_id != NULL
      && core_bfd->build_id->size == exec_bfd->build_id->size
      && memcmp (core_bfd->build_id->data, exec_bfd->build_id->data,
		 core_bfd->build_id->size) == 0)
    return TRUE;

  /* See if the name in the corefile matches the executable name.  */
  corename = elf_tdata (core_bfd)->core->program;
  if (corename != NULL)
    {
      const char* execname = strrchr (bfd_get_filename (exec_bfd), '/');

      execname = execname ? execname + 1 : bfd_get_filename (exec_bfd);

      if (strcmp (execname, corename) != 0)
	return FALSE;
    }

  return TRUE;
}

buildid

直观上看,这个信息应该是由内核在生成coredump文件的时候生成的,但是在内核的代码中并没有找到对于这个buildid的特殊处理(关键字是NT_GNU_BUILD_ID)。好在结合本次提交信息可以很快找到对应的实现:就是从coredump文件中包含的文件映射信息中读取的,读取的方法也是遍历文件header中的所有note信息,然后找到其中包含NT_GNU_BUILD_ID的节。由于这份数据是在生成coredump文件的时候从内存中生成的一个拷贝,所以可以具有记录信息。

static bfd_boolean
elfobj_grok_gnu_note (bfd *abfd, Elf_Internal_Note *note)
{
  switch (note->type)
    {
    default:
      return TRUE;

    case NT_GNU_PROPERTY_TYPE_0:
      return _bfd_elf_parse_gnu_properties (abfd, note);

    case NT_GNU_BUILD_ID:
      return elfobj_grok_gnu_build_id (abfd, note);
    }
}
static bfd_boolean
elf_parse_notes (bfd *abfd, char *buf, size_t size, file_ptr offset,
		 size_t align)
{
///...
	case bfd_core:
	  {
#define GROKER_ELEMENT(S,F) {S, sizeof (S) - 1, F}
	    struct
	    {
	      const char * string;
	      size_t len;
	      bfd_boolean (* func)(bfd *, Elf_Internal_Note *);
	    }
	    grokers[] =
	    {
	      GROKER_ELEMENT ("", elfcore_grok_note),
	      GROKER_ELEMENT ("FreeBSD", elfcore_grok_freebsd_note),
	      GROKER_ELEMENT ("NetBSD-CORE", elfcore_grok_netbsd_note),
	      GROKER_ELEMENT ( "OpenBSD", elfcore_grok_openbsd_note),
	      GROKER_ELEMENT ("QNX", elfcore_grok_nto_note),
	      GROKER_ELEMENT ("SPU/", elfcore_grok_spu_note),
	      GROKER_ELEMENT ("GNU", elfobj_grok_gnu_note)
	    };
#undef GROKER_ELEMENT
	    int i;

	    for (i = ARRAY_SIZE (grokers); i--;)
	      {
		if (in.namesz >= grokers[i].len
		    && strncmp (in.namedata, grokers[i].string,
				grokers[i].len) == 0)
		  {
		    if (! grokers[i].func (abfd, & in))
		      return FALSE;
		    break;
		  }
	      }
	    break;
	  }
///...
}

在linux下,可以通过readelf命令查看可执行文件的BUILD_ID,

tsecer@harry: readelf -n a.out  

Displaying notes found at file offset 0x00000274 with length 0x00000024:
  所有者             Data size  Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 296dc8433375f102ba8f0e3b766fec0cb12cc170

note

从内核填充note的代码可以看到,note首先是通过字符串表示属于哪个大的范围,之后还有一个数值表示属于哪个小类,最后通过长度+内容表示文件内容

static void fill_note(struct memelfnote *note, const char *name, int type, 
		unsigned int sz, void *data)
{
	note->name = name;
	note->type = type;
	note->datasz = sz;
	note->data = data;
	return;
}

结合gdb的代码可以看到,GNU扩展的note为一类,该类中当前包含了一些信息,其中就包含了我们关心的NT_GNU_BUILD_ID字段

/* Values for notes in non-core files using name "GNU".  */

#define NT_GNU_ABI_TAG		1
#define NT_GNU_HWCAP		2	/* Used by ld.so and kernel vDSO.  */
#define NT_GNU_BUILD_ID		3	/* Generated by ld --build-id.  */
#define NT_GNU_GOLD_VERSION	4	/* Generated by gold.  */
#define NT_GNU_PROPERTY_TYPE_0  5	/* Generated by gcc.  */

#define NT_GNU_BUILD_ATTRIBUTE_OPEN	0x100
#define NT_GNU_BUILD_ATTRIBUTE_FUNC	0x101

perf

从perf的调用链可以看到,在perf的session结束之后,还是会记录所有buildid信息,并且还会缓存文件。

(gdb) bt
#0  write_buildid (fd=3, misc=1, pid=-1, build_id=0x7f1ddb "\026ٸ\351\264\325\062\223\330\001t谢k\341˾Ω", name_len=18, name=<optimized out>) at util/header.c:223
#1  __dsos__write_buildid_table (head=head@entry=0x7f1b18, pid=-1, misc=1, fd=fd@entry=3) at util/header.c:255
#2  0x00000000004738db in machine__write_buildid_table (fd=3, machine=0x7f1ac0) at util/header.c:275
#3  dsos__write_buildid_table (fd=3, header=0x7f19d0) at util/header.c:288
#4  write_build_id (fd=fd@entry=3, h=h@entry=0x7f19d0, evlist=evlist@entry=0x7f0d40) at util/header.c:497
#5  0x0000000000474612 in do_write_feat (evlist=0x7f0d40, p=<synthetic pointer>, type=2, h=0x7f19d0, fd=3) at util/header.c:2225
#6  perf_header__adds_write (fd=3, evlist=0x7f0d40, header=0x7f19d0) at util/header.c:2264
#7  perf_session__write_header (session=0x7f19d0, evlist=0x7f0d40, fd=3, at_exit=at_exit@entry=true) at util/header.c:2361
#8  0x000000000042cd8a in perf_record__exit (status=<optimized out>, arg=0x71ab00 <record>) at builtin-record.c:317
#9  0x00007ffff5b21a49 in __run_exit_handlers () from /lib64/libc.so.6
#10 0x00007ffff5b21ab5 in exit () from /lib64/libc.so.6
#11 0x0000000000416760 in handle_internal_command (argv=0x7fffffffe380, argc=4) at perf.c:376
#12 run_argv (argv=0x7fffffffe180, argcp=0x7fffffffe18c) at perf.c:420
#13 main (argc=4, argv=0x7fffffffe380) at perf.c:521
(gdb) 
static int perf_session__cache_build_ids(struct perf_session *session)
{
	struct rb_node *nd;
	int ret;
	char debugdir[PATH_MAX];

	snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);

	if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
		return -1;

	ret = machine__cache_build_ids(&session->machines.host, debugdir);

	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
		struct machine *pos = rb_entry(nd, struct machine, rb_node);
		ret |= machine__cache_build_ids(pos, debugdir);
	}
	return ret ? -1 : 0;
}

int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
			  const char *name, bool is_kallsyms, bool is_vdso)
{
///...
	len = scnprintf(filename, size, "%s%s%s",
		       debugdir, slash ? "/" : "",
		       is_vdso ? VDSO__MAP_NAME : realname);
	if (mkdir_p(filename, 0755))
		goto out_free;

	snprintf(filename + len, size - len, "/%s", sbuild_id);

	if (access(filename, F_OK)) {
		if (is_kallsyms) {
			 if (copyfile("/proc/kallsyms", filename))
				goto out_free;
		} else if (link(realname, filename) && copyfile(name, filename))
			goto out_free;
	}

	len = scnprintf(linkname, size, "%s/.build-id/%.2s",
		       debugdir, sbuild_id);
///...
}

perf为什么要保存这些二进制呢?在perf的一个wiki文档中的一个说明可能有一定意义:也就是为了在系统中保存多个同名文件的备份,这样文件被重新编译之后,还可以使用perf数据生成时使用的二进制文件。

Given that build-id are immutable, they uniquely identify a binary. If a binary is recompiled, a new build-id is generated and a new copy of the ELF images is saved in the cache. 

在查找符号的过程中,是按照这个顺序逐个尝试的,并且后面尝试成功将会覆盖前面尝试成功的内容,或者说:越靠后的位置读取到的文件优先级越高

static enum dso_binary_type binary_type_symtab[] = {
	DSO_BINARY_TYPE__KALLSYMS,
	DSO_BINARY_TYPE__GUEST_KALLSYMS,
	DSO_BINARY_TYPE__JAVA_JIT,
	DSO_BINARY_TYPE__DEBUGLINK,
	DSO_BINARY_TYPE__BUILD_ID_CACHE,
	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
	DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
	DSO_BINARY_TYPE__GUEST_KMODULE,
	DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
	DSO_BINARY_TYPE__NOT_FOUND,
};
int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
{
///...
	/* Iterate over candidate debug images.
	 * Keep track of "interesting" ones (those which have a symtab, dynsym,
	 * and/or opd section) for processing.
	 */
	for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) {
		struct symsrc *ss = &ss_[ss_pos];
		bool next_slot = false;

		enum dso_binary_type symtab_type = binary_type_symtab[i];

		if (dso__binary_type_file(dso, symtab_type,
					  root_dir, name, PATH_MAX))
			continue;
///...
}

补充

gdb比较buildid的修改在2019-10-30 Keith Seitz 提交,通过gdb的发布历史,可以看到在此次提交之后最近的一次发布是gdb的9.1版本。

Release Estimate Schedule Actual Slip (months) Comment
9.1 2019-12 2019-12 2020-02-08 1.5 Large number of issues to investigate
8.3.1 2019-08 2019-09 2019-09-20 0.5 None

标签:__,core,bfd,buildid,image,GNU,note,build,id
From: https://www.cnblogs.com/tsecer/p/17402951.html

相关文章

  • percentileofscore函数
    `percentileofscore`是一个用于计算给定数据在数组中的排名百分比的函数,通常用于统计分析中。具体来说,给定一个数组和一个分数,`percentileofscore`函数会计算出该分数在数组中的排名百分比,即有多少个数比该分数小。计算公式如下:percentileofscore=(numberofscores≤given......
  • net-core理解
    环境:netcore7.0第一行代码:varbuilder=WebApplication.CreateBuilder(args);//1忘记了Application,创建ApplicationBuilderMicrosoft.AspNetCore.Builder 第二行代码:builder.Services.AddControllers();privatestaticIMvcCoreBuilderAddControllersCore(ISer......
  • Ubuntu 升级 git,如何应对sudo add-apt-repository ppa:git-core/ppa卡死的情况
    绕开代理https://blog.csdn.net/m0_68734901/article/details/128411072sudo-Eadd-apt-repositoryppa:git-core/ppasudoapt-getupdatesudoapt-getinstallgit......
  • Meta发布ImageBind新模型,对齐文本/图像/音频/3D深度/热量/运动等6种模态
    出品|CSDN(ID:CSDNnews)据外媒报道,上周四,Google、微软、OpenAI几家公司的CEO受邀去白宫,共论关于人工智能发展的一些重要问题。然而,让人有些想不通的是,深耕AI多年的Meta公司(前身为Facebook)却没有在受邀之列。没多久,更让MetaCEO扎克伯格扎心的是,一位官员对此解释称,本次......
  • Asp.Net Core使用Exceptionless日志服务2-集成Exceptionless.NLog
    Asp.NetCore使用Exceptionless日志服务2-集成Exceptionless.NLog Exceptionless有自己一套完整的Api用于提交日志,我的项目已经写好了,使用Asp.NetCore标准接口ILogger记录日志,然后通过NLog输出到文件和控制台,我不想修改标准的日志接口,幸好有Exceptionless.NLog这样强大的组件,......
  • 【愚公系列】2023年05月 .NET CORE工具案例-Workflow-Core轻量级工作流引擎(随机流程)
    (文章目录)前言1.什么是工作流工作流是OA系统比较重要的功能之一,主要在于企业流程协同审批,有效进行流程管理。流程管理起源于生产组织和办公自动化领域,是针对日常工作中具有固定程序的活动提出的一个概念。目的是通过将工作分解成定义良好的任务、角色,按照一定的规则和过程来......
  • 在 ASP.NET Core Web API 中处理 Patch 请求
    一、概述PUT和PATCH方法用于更新现有资源。它们之间的区别是,PUT会替换整个资源,而PATCH仅指定更改。在ASP.NETCoreWebAPI中,由于C#是一种静态语言(dynamic在此不表),当我们定义了一个类型用于接收HTTPPatch请求参数的时候,在Action中无法直接从实例中得知客户......
  • CF1777D Score of a Tree 题解
    题目简述给你一个\(n\)个结点根为\(1\)的树。在\(t=0\)时,每个结点都有一个值,为\(0\)或\(1\)。在每一个\(t>0\)时,每个结点的值都会变成其子结点在\(t-1\)时的值的异或和。定义\(S(t)\)为\(t\)时所有结点值的和。定义\(F(A)\)为树在\(0\let\le10^......
  • NET Core 事件总线,分布式事务解决方案:CAP
    转载:https://blog.csdn.net/WuLex/article/details/1245648311.事件总线概念事件总线是一种事件发布/订阅结构,通过发布订阅模式可以解耦不同架构层级,同样它也可以来解决业务之间的耦合,它有以下优点松耦合横切关注点可测试性事件驱动事件总线类型进程内事件(Event):本地事件,它......
  • 【五期邹昱夫】CCF-A(NeurIPS'21)Gradient inversion with generative image prior
    "JeonJ,LeeK,OhS,etal.Gradientinversionwithgenerativeimageprior[J].Advancesinneuralinformationprocessingsystems,2021,34:29898-29908."  本文提出了一种基于预训练模型的梯度反演方法。该方法通过使用潜在空间搜索优化维度较低的特征向量,减少......