【问题标题】:how to prevent linker from discarding a function?如何防止链接器丢弃函数?
【发布时间】:2018-12-31 08:38:05
【问题描述】:

我的 C 代码中有一个函数被隐式调用,并被链接器转储。我怎样才能防止这种现象?

我正在使用 gcc 和链接器标志 -gc-sections 进行编译,我不想从标志中排除整个文件。我尝试使用属性:“used”和“externally_visible”,但都没有奏效。

void __attribute__((section(".mySec"), nomicromips, used)) func(){
...
}

在地图文件上,我可以看到该函数已编译但未链接。我用错了吗?还有其他方法吗?

【问题讨论】:

  • “被隐式调用”是什么意思?
  • 我对@9​​87654324@ 了解不多,但阅读gcc.gnu.org/onlinedocs/gcc-3.2/gcc/… 让我觉得你的语法是错误的。似乎应该如此。 void func() __attribute__ ((..)); void func() {…};。除此之外,不清楚您所说的“隐含”是什么意思。
  • 请详细说明您正在做什么、会发生什么以及您期望会发生什么。
  • 隐含的意思是我使用段地址来调用函数

标签: c gcc linker attributes


【解决方案1】:

你误会used attribute

二手

附加到函数的这个属性意味着必须为该函数发出代码,即使看起来该函数没有被引用...

编译器必须发出函数定义,即使函数出现 不被引用。编译器永远不会断定一个函数是未引用的 如果它有外部链接。所以在这个程序中:

ma​​in1.c

static void foo(void){}

int main(void)
{
    return 0;
}

编译:

$ gcc -c -O1 main1.c

根本没有发出foo 的定义:

$ nm main1.o
0000000000000000 T main

因为foo在翻译单元中没有被引用,不是外部的, 所以可能会被优化出来。

但是在这个程序中:

ma​​in2.c

static void __attribute__((used)) foo(void){}

int main(void)
{
    return 0;
}

__attribute__((used)) 强制编译器发出本地定义:

$ gcc -c -O1 main2.c
$ nm main2.o
0000000000000000 t foo
0000000000000001 T main

但这并不能阻止 链接器 丢弃一个部分 其中定义了foo,在存在-gc-sections 的情况下,即使foo 外部的,如果该部分未使用:

ma​​in3.c

void foo(void){}

int main(void)
{
    return 0;
}

使用功能部分编译:

$ gcc -c -ffunction-sections -O1 main3.c

foo 的全局定义在目标文件中:

$ nm main3.o
0000000000000000 T foo
0000000000000000 T main

但链接后:

$ gcc -Wl,-gc-sections,-Map=mapfile main3.o

foo 没有在程序中定义

$ nm a.out | grep foo; echo Done
Done

定义foo的函数部分被丢弃:

地图文件

...
...
Discarded input sections
 ...
 ...
 .text.foo      0x0000000000000000        0x1 main3.o
 ...
 ...

根据 Eric Postpischil 的评论,强制 链接器 保留 一个明显未使用的功能部分,您必须告诉它假设该程序 引用未使用的函数,带有链接器选项{-u|--undefined} foo:

ma​​in4.c

void __attribute__((section(".mySec"))) foo(void){}

int main(void)
{
    return 0;
}

如果你不告诉它:

$ gcc -c main4.c
$ gcc -Wl,-gc-sections main4.o
$ nm a.out | grep foo; echo Done
Done

foo 未在程序中定义。如果你告诉它:

$ gcc -c main4.c
$ gcc -Wl,-gc-sections,--undefined=foo main4.o
$ nm a.out | grep foo; echo Done
0000000000001191 T foo
Done

它被定义了。属性 used 没有用处。

【讨论】:

    【解决方案2】:

    除了这里已经提到的-u,还有另外两种使用 GCC 保留符号的方法。

    创建对它的引用而不调用它

    这种方法不需要弄乱链接描述文件,这意味着它适用于使用操作系统默认链接描述文件的托管程序和库。

    但是它会随着编译器优化设置而变化,并且可能不是很便携。

    例如,在带有 LD 2.31.1 的 GCC 7.3.1 中,您可以保留一个函数而不实际调用它,方法是在其地址上调用另一个函数,或在指向其地址的指针上跳转。

    bool function_exists(void *address) {
        return (address != NULL);
    }
    
    // Somewhere reachable from main
    assert(function_exists(foo));
    assert(foo != NULL);  // Won't work, GCC optimises out the constant expression
    assert(&foo != NULL);  // works on GCC 7.3.1 but not GCC 10.2.1
    

    另一种方法是创建一个包含函数指针的struct,然后您可以将它们全部组合在一起并检查struct 的地址。我经常将它用于中断处理程序。

    修改链接描述文件以保留该部分

    如果您正在开发托管程序或库,那么更改链接描述文件非常棘手。

    即使你这样做了,它也不是很便携,例如 OSX 上的 gcc 实际上并没有使用 GNU 链接器,因为 OSX 使用 Mach-O 格式而不是 ELF。

    您的代码已经显示了一个自定义部分,因此您可能正在使用嵌入式系统并且可以轻松修改链接器脚本。

    SECTIONS {
        // ...
        .mySec {
            KEEP(*(.mySec));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-18
      • 1970-01-01
      • 1970-01-01
      • 2013-10-22
      • 2022-08-22
      • 2011-06-10
      • 1970-01-01
      • 2017-02-23
      相关资源
      最近更新 更多