【问题标题】:Access build-id at runtime在运行时访问 build-id
【发布时间】:2022-01-19 05:13:43
【问题描述】:

我正在尝试弄清楚如何访问链接器在运行时生成的 build-id。

从此页面,https://linux.die.net/man/1/ld

当我构建一个测试程序时:

% gcc test.c -o test -Wl,--build-id=sha1

我可以看到构建 ID 存在于二进制文件中:

% readelf -n test

Displaying notes found in: .note.gnu.build-id
  Owner                 Data size   Description
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 85aa97bd52ddc4dc2a704949c2545a3a9c69c6db

我想在运行时打印它。

编辑:假设您无法访问加载运行进程的 elf 文件(权限、嵌入式/无文件系统等)。

编辑:接受的答案有效,但链接器不一定必须将变量放在该部分的末尾。如果有办法获得指向节开头的指针,那就更可靠了。

【问题讨论】:

  • 你试过内联汇编吗?
  • 不,但现在我很感兴趣
  • 您的回答完全不能保证有效。 “链接器将其放置在...之后立即”或者它可以将其放置在之前。此外,--build-id 可以是 SHA1,或 MD5,或任意十六进制值。
  • 我在所有示例中都使用了 -Wl,--build-id=sha1
  • 请回答您自己的问题并接受它,而不是回答问题中的问题。

标签: c gcc linker elf linker-scripts


【解决方案1】:

想通了。这是一个工作示例,

#include <stdio.h>

//
// variable must have an initializer
//  https://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Variable-Attributes.html
//
// the linker places this immediately after the section data
// 
char build_id __attribute__((section(".note.gnu.build-id"))) = '!';

int main(int argc, char ** argv)
{
  const char * s;

  s = &build_id;

  // section data is 20 bytes in size
  s -= 20;

  // sha1 data continues for 20 bytes
  printf("  > Build ID: ");
  int x;
  for(x = 0; x < 20; x++) {
    printf("%02hhx", s[x]);
  }

  printf(" <\n");

  return 0;
}

当我运行它时,我得到与 readelf 匹配的输出,

0 % gcc -g main.c -o test -Wl,--build-id=sha1 && readelf -n test | tail -n 5 && ./test
Displaying notes found in: .note.gnu.build-id
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: c5eca2cb08f4f5a31bb695955c7ebd2722ca10e9
  > Build ID: c5eca2cb08f4f5a31bb695955c7ebd2722ca10e9 <

【讨论】:

  • 这个解决方案硬编码了.note.gnu.build-id的长度,不太理想。
  • 此解决方案不适用于 Android。 Adroid 的崩溃转储程序将停止打印构建 ID。
  • 这似乎对我有用,但我收到了一个令人担忧的警告:为 .note.gnu.build-id 设置不正确的部分属性
【解决方案2】:

一种可能性是使用链接器脚本来获取.note.gnu.build-id 部分的地址:

#include <stdio.h>

// These will be set by the linker script
extern char build_id_start;
extern char build_id_end;

int main(int argc, char **argv) {
  const char *s;

  s = &build_id_start;

  // skip over header (16 bytes)
  s += 16;

  printf("  > Build ID: ");
  for (; s < &build_id_end; s++) {
    printf("%02hhx", *s);
  }

  printf(" <\n");

  return 0;
}

在链接描述文件中,定义了符号build_id_startbuild_id_end

build_id_start = ADDR(.note.gnu.build-id);
build_id_end = ADDR(.note.gnu.build-id) + SIZEOF(.note.gnu.build-id);

然后可以编译并运行代码:

gcc build-id.c linker-script.ld -o test && readelf -n test | grep NT_GNU_BUILD_ID -A1 && ./test
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 7e87ff227443c8f2d5c8e2340638a2ec77d008a1
  > Build ID: 7e87ff227443c8f2d5c8e2340638a2ec77d008a1 <

【讨论】:

    【解决方案3】:

    我发现build-id 库可以在运行时获取 BuildId。

      auto path="/path/to/executable";
      const struct build_id_note *note = build_id_find_nhdr_by_name(path);
      if (!note) {
          return std::nullopt;
      }
    
      ElfW(Word) len = build_id_length(note);
    
      const uint8_t *build_id = build_id_data(note);
    
      std::vector<byte> result(build_id,build_id+len);
    

    【讨论】:

    • 很高兴它对你有用:)
    猜你喜欢
    • 2023-02-07
    • 2021-05-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 2021-02-11
    相关资源
    最近更新 更多