DragonMinded / libdragon

Open source library for N64 development.
https://libdragon.dev
The Unlicense
746 stars 108 forks source link

Don't garbage collect `keep.{text,data,rodata}.*` sections #583

Open Dragorn421 opened 4 months ago

Dragorn421 commented 4 months ago

This allows manually preventing specific sections (that is, specific symbols, since -ffunction-sections -fdata-sections means one section per symbol) from being garbage collected by the linker ld in the case they are not referenced.


For example:

#include <libdragon.h>

char string[] = "hey";

int main()
{
    return 0;
}

The -fdata-sections flag passed to gcc makes the string symbol be put into its own .rodata.string section. Then on linking since that symbol is unreferenced the section is dropped.

This PR changes the linker script n64.ld such that sections named like keep.{text,data,rodata}.* are always linked in regardless of garbage collection. This allows using the gcc section attribute to put a symbol in a so-named section that would be kept:

__attribute__((section("keep.rodata.string")))
char string[] = "hey";

The section suffix ("string" here) doesn't matter but I'd recommend using the symbol name like -fdata-sections does for simplicity, consistency and avoiding name collisions (EDIT: section names don't collide. that's the point of the linker after all... should we just not have suffixes at all?).

I also made this possible with text and data. Not that it matters where a symbol ends up, afaik having put this string in keep.text.string and having it ending up in the rom elf's .text section would work just fine. (likewise with putting text in .data/.rodata) Still it seems preferable to put things where they're expected.

I did not add this for .bss because that looks like a footgun, for example picture doing __attribute__((section("keep.bss.mydata"))) int mydata;, and later adding an initializer to mydata but not changing the section from keep.bss to keep.data: then the C source makes it look like the variable has that initial value, but it will be linked in the NOLOAD bss section which is 0-initialized regardless. Note that (afaik) linking a "bss" uninitialized data symbol into a LOAD section such as .data (__attribute__((section("keep.data.mydata"))) int mydata;) will just work, the linker will store 0s in the data section for those bytes.


Other alternatives:

Dragorn421 commented 4 months ago

I just realized that there is no need for section names to be unique for __attribute__((section)) or in general, neither inside one file or across TUs. So this could also just be only keeping sections keep.{text,data,rodata} and putting symbols in those, no need to have a .symbolthing suffix. Would that be better?

asiekierka commented 4 months ago

Modern versions of binutils/GCC (GCC 11+, and matching binutils) have __attribute__((retain)). This should do the trick?

rasky commented 3 months ago

@Dragorn421 does __attribute__((retain)) work for you?

Dragorn421 commented 3 months ago

It unfortunately does not work

my gcc / ld(binutils) versions:

$ $N64_GCCPREFIX/bin/mips64-elf-gcc --version
mips64-elf-gcc (GCC) 13.2.0
$ $N64_GCCPREFIX/bin/mips64-elf-ld --version
GNU ld (GNU Binutils) 2.41

on compilation:

src/main.c:8:1: error: 'retain' attribute ignored [-Werror=attributes]
    8 | __attribute__((retain)) char str_main_abcdef[] = "str_main_abcdef contents";
      | ^~~~~~~~~~~~~

the docs for the attribute (which I didn't know about btw, thanks for sharing) https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-retain-variable-attribute