C 运行时库中的 R_386_32 类型符号是什么?

What is R_386_32 type symbols in C runtime library?

我读了Here
据我了解,R_386_32 用于静态数据,R_386_PC32 用于函数。对吧?

但是我仍然对 R_386_32 类型符号的用法感到困惑。
请参见下面的示例。

示例 1

readelf -a --wide /usr/lib/i386-linux-gnu/crt1.o | grep R_386_32
0000000c  00000901 R_386_32               00000000   __libc_csu_fini
00000011  00000b01 R_386_32               00000000   __libc_csu_init
00000018  00000c01 R_386_32               00000000   main

例2

readelf -a --wide /usr/local/lib/gcc/i686-pc-linux-gnu/5.5.0/crtbegin.o 
00000001  00001501 R_386_32               00000000   __TMC_END__
00000010  00001601 R_386_32               00000000   _ITM_deregisterTMCloneTable
00000031  00001501 R_386_32               00000000   __TMC_END__
00000049  00001701 R_386_32               00000000   _ITM_registerTMCloneTable
000000a1  00001901 R_386_32               00000000   _Jv_RegisterClasses


问题

  1. 示例 2 中,R_386_32 键入的数据会自动添加到 Application
    在编译时?
  2. 如果是,我可以在我的代码中引用这些数据吗?
    例如,我可以使 printf 的 Application 成为 _Jv_RegisterClasses 的值吗?
  3. 示例1中,为什么mainR_386_32类型?
    我觉得应该是R_386_PC32,因为不是静态数据,是函数。

您正在查看 重定位, 而不是 符号。 重定位正是汇编程序在要引用其符号时生成的内容价值未知;这是对 link 人员的指示,要求他们在 link 时填写正确的值。重定位不是符号类型;每个符号都可以通过任意数量的任意类型的重定位来引用。另请注意,符号 table 不知道符号引用的数据类型(如果有的话)。一个符号只是一个地址和一个名字。

重定位类型R_386_32只是表示“将符号的值粘贴为32位”。无法说明所使用的符号是用于数据还是文本。这用于例如,如果您加载符号的地址或执行绝对内存访问。这两条指令都会生成 R_386_32 重定位:

mov $foo, %eax       # move value of symbol to register
mov foo, %eax        # perform absolute memory access

另一方面,重定位类型R_386_PC32减去指令指针的值(program counter ) 从符号中粘贴。这种重定位类型主要用于直接跳转和调用指令:

jmp foo              # jump to foo
call foo             # call foo

通常,无法通过查看重定位来猜测符号定义在哪个部分。实际上,重定位根本不提供任何相关信息,并且目标文件不能要求外部符号引用数据或文本。对于已定义的符号,您可以通过 运行 和 nm 实用程序找出它们所在的部分。标记为tT的符号是文本,dD是数据,rR是只读数据,bB 是 BSS。

对于你的第二个问题:是的,你可以。使用这样的 C 代码打印 _Jv_RegisterClasses 的值。注意一个符号的值是它所引用的变量的地址。

extern const void _Jv_RegisterClasses;  /* or any other type */

printf("%p\n", &_Jv_RegisterClasses);   /* print value of symbol */