用于模拟Linux内核Initcall机制的自定义ld脚本工作原理是什么?
Hey everyone, let me break down a common issue when trying to replicate the Linux kernel's Initcall mechanism in user space—something Trevor ran into recently while writing a test program to simulate init_module() calls.
The Problem
When Trevor tried compiling his user-space program, the linker threw an error complaining that __initcall_start and __initcall_end were undefined. No executable could be generated at all.
Why This Happens
The Linux kernel's Initcall system relies on two special symbols—__initcall_start and __initcall_end—that are defined in the kernel's custom link script. These symbols mark the start and end of the section where initialization functions are stored. But user-space programs use the default system linker script, which doesn't include these symbols. So the linker has no idea what they're supposed to point to.
The Fix
We need to create a custom linker script that adds these missing symbols. Here's how to do it step by step:
- Export the default linker script: Run
ld --verbosein your terminal, and save the entire output to a file (let's call itmy_initcall.ld). This gives you a base to modify. - Add the initcall symbols: Open
my_initcall.ldand find the section where the.textsegment is defined. Right before that, add code to define our initcall symbols and map the relevant sections. For example:
This tells the linker to set__initcall_start = .; *(SORT(.initcall*.init)) __initcall_end = .;__initcall_startat the current address, include all sections matching.initcall*.init(matching the kernel's initcall naming convention), then mark__initcall_endat the end of those sections. - Compile with the custom script: When compiling your program, add the
-T my_initcall.ldflag to tellgcc(orld) to use your modified linker script instead of the default one.
Proof It Works
After making these changes, the linker no longer throws errors. Here's a snippet from objdump -d on the working executable, showing the symbols are correctly placed:
0000000000401000 <__initcall_start>:
401000: 55 push %rbp
401001: 48 89 e5 mov %rsp,%rbp
401004: b8 00 00 00 00 mov $0x0,%eax
401009: 5d pop %rbp
40100a: c3 retq000000000040100b <__initcall_end>:
40100b: e9 f0 ff ff ff jmpq 400ff0 <_start>
内容的提问来源于stack exchange,提问作者Baroudi Safwen

