Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to prevent linker from discarding a function?

I have a function in my C code that is being called implicitly, and getting dumped by the linker. how can I prevent this phenomena?

I'm compiling using gcc and the linker flag -gc-sections, and I don't want to exclude the whole file from the flag. I tried using attributes: "used" and "externally_visible" and neither has worked.

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

on map file I can see that the function has compiled but didn't linked. am I using it wrong? is there any other way to do it?

like image 566
ron landau Avatar asked Dec 31 '18 08:12

ron landau


2 Answers

Apart from -u already mentioned here are two other ways to keep the symbol using GCC.

Create a reference to it without calling it

This approach does not require messing with linker scripts, which means it will work for hosted programs and libraries using the operating system's default linker script.

However it varies with compiler optimization settings and may not be very portable.

For example, in GCC 7.3.1 with LD 2.31.1, you can keep a function without actually calling it, by calling another function on its address, or branching on a pointer to its address.

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

Another way is to create a struct containing function pointers, then you can group them all together and just check the address of the struct. I use this a lot for interrupt handlers.

Modify the linker script to keep the section

If you are developing a hosted program or a library, then it's pretty tricky to change the linker script.

Even if you do, its not very portable, for example gcc on OSX does not actually use the GNU linker since OSX uses the Mach-O format instead of ELF.

Your code already shows a custom section though, so it's possible you are working on an embedded system and can easily modify the linker script.

SECTIONS {
    // ...
    .mySec {
        KEEP(*(.mySec));
    }
}
like image 25
szmoore Avatar answered Nov 15 '22 08:11

szmoore


You are misunderstanding the used attribute

used

This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced...

i.e the compiler must emit the function definition even the function appears to be unreferenced. The compiler will never conclude that a function is unreferenced if it has external linkage. So in this program:

main1.c

static void foo(void){}

int main(void)
{
    return 0;
}

compiled with:

$ gcc -c -O1 main1.c

No definition of foo is emitted at all:

$ nm main1.o
0000000000000000 T main

because foo is not referenced in the translation unit, is not external, and so may be optimised out.

But in this program:

main2.c

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

int main(void)
{
    return 0;
}

__attribute__((used)) compels the compiler to emit the local definition:

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

But this does nothing to inhibit the linker from discarding a section in which foo is defined, in the presence of -gc-sections, even if foo is external, if that section is unused:

main3.c

void foo(void){}

int main(void)
{
    return 0;
}

Compile with function-sections:

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

The global definition of foo is in the object file:

$ nm main3.o
0000000000000000 T foo
0000000000000000 T main

But after linking:

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

foo is not defined in the program:

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

And the function-section defining foo was discarded:

mapfile

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

As per Eric Postpischil's comment, to force the linker to retain an apparently unused function-section you must tell it to assume that the program references the unused function, with linker option {-u|--undefined} foo:

main4.c

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

int main(void)
{
    return 0;
}

If you don't tell it that:

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

foo is not defined in the program. If you do tell it that:

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

it is defined. There's no use for attribute used.

like image 70
Mike Kinghan Avatar answered Nov 15 '22 08:11

Mike Kinghan