feat: Support PROVIDE and PROVIDE_HIDDEN linker script directives - #1525
Conversation
| extern char __data_start __attribute__((weak)); | ||
| extern char __data_end __attribute__((weak)); | ||
|
|
||
| int test_data = 42; |
There was a problem hiding this comment.
This is the reason I'm keeping this PR in draft. If you comment out this line, the .data section becomes empty, and wild will fail to link. This is a clear issue that needs to be addressed.
---- integration_test::program_name_055___linker_script_provide_c__ stdout ----
Error: Test failed: `linker-script-provide.c` with linker `wild` config `default`
Caused by:
libwild reported error. Rerun command(s):
cargo run --bin wild -- -dynamic-linker /lib64/ld-linux-x86-64.so.2 --gc-sections -shared -T /home/lapla/repos/wild/wild/tests/sources/linker-script-provide.ld -o /home/lapla/repos/wild/wild/tests/build/linker-script-provide.c/default-host/linker-script-provide.c.wild /home/lapla/repos/wild/wild/tests/build/linker-script-provide.c/default-host/linker-script-provide.aeb0bf64dfeb9435.o --validate-output --write-layout --write-trace --dependency-file=/home/lapla/repos/wild/wild/tests/build/linker-script-provide.c/default-host/linker-script-provide.c.wild.deps
Failed copying from /home/lapla/repos/wild/wild/tests/sources/linker-script-provide.ld to output file
symbol '__data_start' in section '`.data`' that we're not going to output Resolution { raw_value: 0, dynamic_symbol_index: Some(5), got_address: None, plt_address: None, flags: ValueFlags(NON_INTERPOSABLE | DIRECT | EXPORT_DYNAMIC | PROVIDE) }
After checking how other linkers handle this situation, I found that GNU ld appears to resolve symbols referenced by PROVIDE to point to the next section (=.dynamic in here) when the .data section is empty:
(▰╹◡╹)❯ gcc -c wild/tests/sources/linker-script-provide.c -o /tmp/ld-no-data.o && ld -shared -T wild/tests/sources/linker-script-provide.ld /tmp/no-data-test.o -o /tmp/ld-no-data.so && readelf -sSW /tmp/ld-no-data.so|grep -E "data_start|data_end|.dynamic"
/usr/bin/x86_64-linux-gnu-ld.bfd: warning: /tmp/ld-no-data.so has a LOAD segment with RWX permissions
[ 8] .dynamic DYNAMIC 0000000000000168 001168 0000e0 10 WA 4 0 8
2: 0000000000000168 0 NOTYPE GLOBAL DEFAULT 8 __data_start
3: 0000000000000168 0 NOTYPE GLOBAL DEFAULT 8 __data_end
6: 0000000000000168 0 NOTYPE GLOBAL DEFAULT 8 __data_end
7: 0000000000000168 0 NOTYPE GLOBAL DEFAULT 8 __data_start
However, this seems very strange to me - I don't see any positive motivation for doing it this way.
In fact, lld appears to output sections referenced via linker scripts even when they are empty. This approach seems natural to me.
(▰╹◡╹)❯ gcc -c wild/tests/sources/linker-script-provide.c -o /tmp/lld-no-data.o && ld.lld -shared -T /tmp/test-location.ld /tmp/lld-no-data.o -o /tmp/no-data-test-lld.so && readelf -sSW /tmp/no-data-test-lld.so | grep -E "data_start|data_end|.data"
[11] .data PROGBITS 00000000000004d0 0014d0 000000 00 WA 0 0 1
7: 00000000000003d6 36 FUNC GLOBAL DEFAULT 8 get_data_size
8: 00000000000004d0 0 NOTYPE GLOBAL DEFAULT 11 __data_end
9: 00000000000004d0 0 NOTYPE GLOBAL DEFAULT 11 __data_start
10: 00000000000003fa 0 NOTYPE GLOBAL DEFAULT 8 __before_data
11: 00000000000004d0 0 NOTYPE GLOBAL DEFAULT 11 __after_data
10: 00000000000003d6 36 FUNC GLOBAL DEFAULT 8 get_data_size
11: 00000000000004d0 0 NOTYPE GLOBAL DEFAULT 11 __data_end
12: 00000000000004d0 0 NOTYPE GLOBAL DEFAULT 11 __data_start
13: 00000000000003fa 0 NOTYPE GLOBAL DEFAULT 8 __before_data
14: 00000000000004d0 0 NOTYPE GLOBAL DEFAULT 11 __after_data
There was a problem hiding this comment.
I agree that keeping sections associated with a PROVIDE symbol that is referenced makes sense. We have code that does that for regular symbols - https://github.com/davidlattimore/wild/blob/6a7660ba30b24b6214ab7fa301bde38ce52ac137/libwild/src/layout.rs#L3876
There was a problem hiding this comment.
All right, thanks for double-checking: ada108f
davidlattimore
left a comment
There was a problem hiding this comment.
Great to have this implemented!
|
|
||
| /// This symbol is from a PROVIDE directive in a linker script. It should only be used if | ||
| /// the symbol is referenced but not defined elsewhere. | ||
| const PROVIDE = 1 << 15; |
There was a problem hiding this comment.
Our last available bit. I guess if we need another bit we'll need to either expand to 32 bits and incur the associated performance penalty (about a 1.3% slowdown on the zed benchmark) or refactor to free a bit. Probably PROVIDE could be done without using a bit, since it looks like the only place the bit is used is when selecting alternatives.
There was a problem hiding this comment.
One way to achieve this without adding new bits to ValueFlags would be to add a method to SymbolDb to determine whether a symbol is a PROVIDE symbol. However, this would mean the method would be called every time select_symbol() is invoked, which raises concerns about potential performance impact. However indeed the current implementation consumes the last bit of ValueFlags, let me think a bit more.
There was a problem hiding this comment.
Another way would be the collect up a list of all PROVIDE symbols, then before alternative symbols are selected, look up each of the PROVIDEd symbols to see if there is a definition. If there is, do nothing, if there isn't, then add the symbol.
There was a problem hiding this comment.
I realized that symbol_strength() returns SymbolStrength::Undefined for linker script symbols, making it possible to implement this much more simply. e0894b7
| extern char __data_start __attribute__((weak)); | ||
| extern char __data_end __attribute__((weak)); | ||
|
|
||
| int test_data = 42; |
There was a problem hiding this comment.
I agree that keeping sections associated with a PROVIDE symbol that is referenced makes sense. We have code that does that for regular symbols - https://github.com/davidlattimore/wild/blob/6a7660ba30b24b6214ab7fa301bde38ce52ac137/libwild/src/layout.rs#L3876
594be36 to
749eb2c
Compare
749eb2c to
ada108f
Compare
The
PROVIDEdirective defines a symbol with lower priority than even weak definitions. The symbol is only used if no other definition (strong or weak) exists.PROVIDE_HIDDENadditionally prevents the symbol from being exported.close #1097