首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ld:这个ld脚本是如何工作的?

ld:这个ld脚本是如何工作的?
EN

Stack Overflow用户
提问于 2018-03-03 14:36:41
回答 1查看 112关注 0票数 1

在他关于理解Linux内核Initcall机制的文章中,特雷弗创建了一个用户空间程序,它模拟了调用init_module()驱动程序的机制。

代码语言:javascript
运行
复制
#include <stdio.h>

typedef int (*initcall_t)(void);
extern initcall_t __initcall_start, __initcall_end;

#define __initcall(fn) \
        static initcall_t __initcall_##fn __init_call = fn
#define __init_call     __attribute__ ((unused,__section__ ("function_ptrs")))
#define module_init(x)  __initcall(x);

#define __init __attribute__ ((__section__ ("code_segment")))

static int __init
my_init1 (void)
{
        printf ("my_init () #1\n");
        return 0;
}

static int __init
my_init2 (void)
{
        printf ("my_init () #2\n");
        return 0;
}

module_init (my_init1);
module_init (my_init2);

void
do_initcalls (void)
{
        initcall_t *call_p;

        call_p = &__initcall_start;
        do {
                fprintf (stderr, "call_p: %p\n", call_p);
                (*call_p)();
                ++call_p;
        } while (call_p < &__initcall_end);
}

int
main (void)
{
        fprintf (stderr, "in main()\n");
        do_initcalls ();
        return 0;
}

如您所见,没有定义__initcall_start和__initcall_end,因此链接器会发出抱怨,并且不会生成可执行文件。解决方案是通过在文本部分之前添加以下行来自定义默认链接器脚本(由ld --详细生成):

代码语言:javascript
运行
复制
__initcall_start = .;
function_ptrs : { *(function_ptrs) }
__initcall_end   = .;
code_segment    : { *(code_segment) }

下面是objdump -t输出的一个片段:

代码语言:javascript
运行
复制
0000000000000618 g function_ptrs        0000000000000000         __initcall_end<br>
0000000000000608 g .plt.got             0000000000000000         __initcall_start<br>
0000000000000608 l O function_ptrs      0000000000000008      __initcall_my_init1<br>
0000000000000610   O function_ptrs      0000000000000008      __initcall_my_init2<br>
0000000000000618 l F code_segment       0000000000000017          my_init1<br>

我理解这个机制,我只是不明白链接器如何理解__initcall_start应该指向function_ptrs部分,或者__initcall_end如何指向code_segment部分。

在我看来,__initcall_start被分配了当前输出位置的值,然后定义了一个节function_ptrs,它将指向输入文件中的function_ptrs部分,但是我看不到__initcall_start和funtction_ptrs部分之间的链接。

我的问题是:链接器如何能够理解__initcall_start应该指向funtion_ptrs ??

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-03-04 17:08:12

代码语言:javascript
运行
复制
__initcall_start = .;
function_ptrs : { *(function_ptrs) }
__initcall_end   = .;
code_segment    : { *(code_segment) }

这个链接器脚本指示链接器如何组成输出文件的某一部分。意思是:-

  • 发出寻址位置计数器的符号__initcall_start (即.)。
  • 然后发出一个名为function_ptrs的节,该节由名为function_ptrs的所有输入部分的级联组成(即来自所有输入文件的function_ptrs段)。
  • 然后发出再次寻址位置计数器的符号__initcall_end
  • 然后发出一个名为code_segment的节,该节由称为code_seqment的所有输入部分的级联组成)

function_ptrs部分是部署在__initcall_start地址的第一个存储区。因此,__initcall_start是链接器启动function_ptrs段的地址。__initcall_end位于function_ptrs段之后的位置。出于同样的原因,它是链接器启动code_segment段的地址。

在我看来,__initcall_start被分配给当前输出位置的值,

你在想:

代码语言:javascript
运行
复制
    __initcall_start = .;

使链接器创建某种意义上是指针的符号,并将当前位置指定为该指针的值。有点像这个C代码:

代码语言:javascript
运行
复制
void * ptr = &ptr;

这里也有同样的想法(强调我的观点):

我只是不明白链接器是如何理解__initcall_start应该将指向function_ptrs部分的,或者__initcall_end如何将指向code_segment部分。

链接器没有指针的概念。它处理象征地址的符号。

在链接器手册中,可以看到:赋值:定义符号

您可以使用任何C赋值运算符创建全局符号,并向全局符号分配值(地址): 符号=表达; ..。

这仅仅意味着symbol被定义为expression计算的地址的符号。同样:

代码语言:javascript
运行
复制
__initcall_start = .;

意味着__initcall_start被定义为当前位置计数器上地址的符号。它不意味着任何类型的符号--甚至不是数据符号或函数符号。符号S的类型是一种编程语言概念,它表示程序如何使用该语言的字节序列,其地址由S符号表示。

C程序可以随意声明它所使用的外部符号S的任何类型,只要链接提供该符号。无论是哪种类型,程序都将获得以S表示的地址,该地址的表达式为&S

C程序选择将__initcall_start__initcall_end声明为类型:

代码语言:javascript
运行
复制
int (*initcall_t)(void);

这在程序告诉链接器要做的内容中是很有意义的。它告诉链接器在以__initcall_start__initcall_end符号表示的地址之间布局__initcall_start部分。本节包含int ()(void)类型的函数数组。因此,类型int (*initcall_t)(void)非常适合遍历该数组,如下所示:

代码语言:javascript
运行
复制
call_p = &__initcall_start;
do {
        fprintf (stderr, "call_p: %p\n", call_p);
        (*call_p)();
        ++call_p;
} while (call_p < &__initcall_end)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49085273

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档