Linux 2.4.20 里 __setup 宏的学习记录

本文深入探讨了Linux内核如何处理启动参数,特别是通过__setup宏注册的函数。解释了el_addr_setup函数如何被调用,并展示了如何通过内核源代码跟踪这一过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

晚上有回过头来看MAC的问题,虽然试验证明是可以,但我还是不清楚:

/* need to get the ether addr from u-boot */

static int __init el_addr_setup(char *line)

{

    return 0;

}

__setup("mac=", el_addr_setup);

这里的el_addr_setup函数是如何被调用到的。

 

类似的问题其实很早就有人问过:

> I'm trying to track down why ide_setup() doesn't seem to be called on

> my system.  I'm using KGDB to put breakpoints in ide_setup(), but they

> never get hit.  I see that ide_setup is defined as:

>

> int __init ide_setup (char *s)

> {

> ...

>

> and there is also a line:

>

> __setup("", ide_setup);

>

> What do these macros do?  How can I get KGDB to break in ide_setup()?

 

take a look at the source (include/linux/init.h).  it places code in a

special section, then is used during init/main.c::checksetup() to find

matching setup strings from the kernel boot commandline.  it's not going

to be called if you don't have an "idex=xxx" or "hdx=" string on the

commandline.  it's also called if you compile ide modular and give it

options during insmod.

 

我们过一遍:

/*

 * Used for kernel command line parameter setup

 */

struct kernel_param {

    const char *str;

    int (*setup_func)(char *);

};

 

extern struct kernel_param __setup_start, __setup_end;

 

#define __setup(str, fn)                                /

    static char __setup_str_##fn[] __initdata = str;                /

    static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn }

 

 

 

__setup("mac=", el_addr_setup);就是说:

static char __setup_str_el_addr_setup[] __initdata = “mac=”;

static struct kernel_param __setup_el_addr_setup __attribute__((unused)) __initsetup = { __setup_str_el_addr_setup, el_addr_setup }

 

也就是定义了一个字符串,定义了一个结构体;

 

在看/2.4.20/init/main.c

int __init checksetup(char *line)

{

    struct kernel_param *p;

 

    if (line == NULL)

        return 0;

 

    p = &__setup_start;

    do {

        int n = strlen(p->str);

        if (!strncmp(line,p->str,n)) {

            if (p->setup_func(line+n))

                return 1;

        }

        p++;

    } while (p < &__setup_end);

    return 0;

}

其输入的参数就是cmdline

当我们拿到cmdline中的一个子命令段的时候,就回去和__setup_start__setup_end之间的这些已经注册了的,处理结构体进行比对的工作。

找到匹配项就掉其对应的处理函数予以执行。

System.map文件中找到:

802874d0 A __setup_start

802875b8 A __setup_end

这样你还可能不是很清楚,那你就要参考arch/i386/vmlinuz.lds这个关于ld 链接器的脚本文件有这样的一段

__setup_start = .;

.setup.init : { *(.setup.init) }

__setup_end = .;

这里的意思就是__setup_start是一个节的开始,__setup_end是一个节的结束,这个节的名称是.setup.init

 

通过readelf命令:

readelf -S vmlinux

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [10] .setup.init       PROGBITS        802874d0 1874d0 0000e8 00  WA  0   0  4

 

通过下面这个命令,我们就知道现在已经注册了的结构体列表。

[root@localhost 2.4.20_mtd]# hexdump -n 232 -s 1602768 ./vmlinux

01874d0 2880 9859 2780 8060 2880 a459 2780 0063

01874e0 2880 ac59 2780 2463 2880 b859 2780 c467

01874f0 2880 c459 2780 d467 2880 d459 2780 0468

0187500 2880 d859 2780 3068 2880 cc5c 2780 8469

0187510 2880 d45c 2780 5c6a 2880 e05c 2780 6c6a

0187520 2880 f05c 2780 c86f 2880 005d 2780 f86f

0187530 2880 205d 2780 cc94 2880 2c5d 2780 2cb3

0187540 2880 345d 2780 58b3 2880 405d 2780 94b6

0187550 2880 7c5d 2780 8ccf 2880 b85d 2780 a0e1

0187560 2880 9c65 2780 ace9 2880 a865 2780 70ed

0187570 2880 b865 2780 9ced 2880 e065 2880 180e

0187580 2880 ec65 2880 440e 2880 fc65 2880 600e

0187590 2880 5066 2880 c417 2880 5866 2880 c417

01875a0 2880 a067 2880 0c38 2880 a467 2880 d03a

01875b0 2880 c474 2880 e450

上面黑体的部分就和System.map里的802850e4 t el_addr_setup向对应(这个是一个MIPSkernel,用的是大端)。

那么我们可以得到他的str 的指针:802874c4

继续readelf

  [Nr] Name              Type            Addr     Off    Size

  [ 9] .data.init        PROGBITS        80285998 185998 001b34

 

0x802874c4 – 0x80285998 = 6956

185998 = 1595800

1595800+6956 = 1602756

我们假设字符串的长度不到10个字节。

[root@localhost 2.4.20_mtd]# hexdump -c -n 10 -s 1602756 ./vmlinux

01874c4   m   a   c   =  /0  /0  /0  /0  /0  /0                       

[root@localhost 2.4.20_mtd]# hexdump  -n 10 -s 1602756 ./vmlinux

01874c4 616d 3d63 0000 0000 0000              

这里就验证了我们看到的情况。

 

那为什么是.setup.init而不是别的段呢?

#define __initsetup __attribute__ ((unused,__section__ (".setup.init")))

 

这里有个技术点是如何在文件中找到我们用的数据,而不是在loaderELF加载到了内存之后。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值