https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis/ 说
类型字段告诉我们该文件的用途是什么。有几种常见的文件类型。
CORE (value 4) DYN (Shared object file), for libraries (value 3) EXEC (Executable file), for binaries (value 2) REL (Relocatable file), before linked into an executable file (value 1)
https://unix.stackexchange.com/a/476157/674表明内核模块是 REL。为什么是 REL 而不是 DYN?
DYN 和 REL 有什么区别?
谢谢。
答案1
看系统 V ABI,其中包含 ELF 格式的规范。它说
不同目标文件的重定位条目对成员的解释略有不同
r_offset
。
在可重定位文件中,
r_offset
保存节偏移量。也就是说,重定位节本身描述了如何修改文件中的另一个节;重定位偏移指定第二部分内的存储单元。在可执行文件和共享目标文件中,
r_offset
保存虚拟地址。为了使这些文件的重定位条目对动态链接器更有用,节偏移(文件解释)让位于虚拟地址(内存解释)。
可重定位文件仍然是完全可重定位的,而共享对象则在链接过程中更进一步,并且已大部分被重定位。仅当共享对象的代码与位置无关时,共享对象才可重定位(例如它是用 GCC 的-fPIC
选项构建的)。
内核模块需要可重定位而不与位置无关,因此它们作为可重定位文件提供。
答案2
Linux 不使用 ELF 方法来处理内核中的动态对象,相反,Linux 仍然使用 20 世纪 80 年代中期的基本方法来加载驱动程序,并且该方法已经适用于该a.out
格式。有可重定位文件(类似于.o
文件),它们链接到内核然后加载。
20 世纪 80 年代中期引入的方法就是这样工作的,要么通过调用执行以下操作的程序,要么通过让用户空间守护进程执行以下操作:
获取驱动程序文件或通过多个文件
.o
链接的文件,并执行最终链接步骤(使用),将该驱动程序链接到加载地址 0。这是必需的,因为 COMMON 变量不会显示在输出中。.o
ld -o driver -r *.o
ld
size
现在调用
size
生成的文件来获取驱动程序所需的大小。打开模块加载驱动程序并使用
ioctl
告诉该驱动程序模块的大小。加载驱动程序的模块
kmem_alloc()
在内核中调用文本、数据和 bss 段,并返回.kmem_alloc()
ioctl
再次调用链接器 (
ld
),但现在将驱动程序链接到模块加载驱动程序返回的地址。使用对模块加载驱动程序的另一个调用,告诉该驱动程序装入已链接到内核分配的地址的驱动程序变体,并将驱动程序内容放入分配的空间。
加载的驱动程序现在可以使用了
如果您想查看使用 ELF 方法的内核,我建议查看 Solaris 内核。
为Solaris 加载的第一个文件是eg /platform/i86pc/kernel/amd64/unix
,该文件被标记为excutable
依赖于两个共享“库”的文件。您可以使用标准 ELF 工具列出它dump
:
dump -Lv /platform/i86pc/kernel/amd64/unix
/platform/i86pc/kernel/amd64/unix:
**** DYNAMIC SECTION INFORMATION ****
.dynamic:
[INDEX] Tag Value
[1] NEEDED genunix
[2] NEEDED dtracestubs
[3] HASH 0xfffffffffb8c1040
[4] STRTAB 0xfffffffffb8e4e10
[5] STRSZ 0xf584
[6] SYMTAB 0xfffffffffb8c9fc0
[7] SYMENT 0x18
[8] CHECKSUM 0x4445
[9] TEXTREL 0
[10] RELA 0xfffffffffb8f4398
[11] RELASZ 0x16470
[12] RELAENT 0x18
[13] FEATURE_1 PARINIT
[14] SUNW_CAP 0xfffffffffb8a37a8
[15] FLAGS TEXTREL
[16] FLAGS_1 [ NOHDR ]
[17] SUNW_STRPAD 0x200
[18] SUNW_LDMACH EM_AMD64
file /platform/i86pc/kernel/unix
/platform/i86pc/kernel/unix: ELF 32-bit LSB executable 80386 Version 1, dynamically linked, not stripped, no debugging information available
正如您在这里看到的,基本内核依赖的共享库是:genunix
和dtracestubs
。
因此,如果您想引导 Solaris 内核,则需要有一个了解 ELF 并能够加载和链接内核所依赖的共享对象的引导加载程序。
顺便说一句:Solaris 有一个内核动态链接器,因此动态加载驱动程序需要更少的步骤。