This is an archive of the discontinued LLVM Phabricator instance.

[ELF] Ensure that .init/.fini sections are padded with nops instead of traps
AbandonedPublic

Authored by arichardson on Sep 22 2017, 2:03 AM.

Details

Summary

The .init/.fini section is executed from start to finish so inserting traps
between the individual sections will cause programs to crash if the
.init/.fini sections are not filled up to the requested alignment.

I discovered this issue linking FreeBSD for MIPS n64 with LLD. When linking
with BFD 2.17 all binaries ran as expected but LLD was inserting 0xefefefef
instructions into the .fini section so every binary would crash on exit.

Event Timeline

arichardson created this revision.Sep 22 2017, 2:03 AM
ruiu edited edge metadata.Sep 22 2017, 11:14 AM

I wonder whether this is a new issue or not. lld used to leave the gap in .init/.fini (or any other sections) as null bytes, so unless your processor handles null bytes as nop instructions, it didn't work before. So, this issue existed before lld started filling gaps with trap instructions. Is my understanding correct?

In D38167#879015, @ruiu wrote:

I wonder whether this is a new issue or not. lld used to leave the gap in .init/.fini (or any other sections) as null bytes, so unless your processor handles null bytes as nop instructions, it didn't work before. So, this issue existed before lld started filling gaps with trap instructions. Is my understanding correct?

I guess the issue existed but it was not causing problems because null bytes are nops on MIPS (and according to https://en.wikipedia.org/wiki/NOP also x86 and ARM and possibly others).

ruiu added a comment.Sep 22 2017, 11:41 AM

At least for x86, null bytes are not nop instructions, so it feels like it just happened to work for MIPS but it wasn't really guaranteed. How did you get a gap in .fini section?

In D38167#879065, @ruiu wrote:

At least for x86, null bytes are not nop instructions, so it feels like it just happened to work for MIPS but it wasn't really guaranteed. How did you get a gap in .fini section?

Ah yes I was looking at the wrong column in the table, so I guess for x86 I should set the NopInstr to 0x90909090. The problem was that the FreeBSD startup assembly used a .align 4 directive which on MIPS is 2^4 instead of 4 bytes. I fixed this in https://github.com/CTSRD-CHERI/cheribsd/commit/d15dd021ce59f54996ce9c823b2b0d47f8d7039b and will submit it upstream as well. However, if we try to link older versions it will still break and I think this is a very simple fix.

ruiu added a comment.Sep 22 2017, 12:06 PM

Well, this is not a bug, so this patch is not a fix but a workaround for existing FreeBSD/MIPS kernel's issue.

So the question is whether or not the issue is so significant and persistent that we need to extend our ABI to make a promise that lld fills gaps in .init/.fini sections with NOP bytes. I think the answer is no in order to make it simple. We shouldn't make any assumption on gap bytes (we shouldn't even assume that they are null or trap instructions) just like we shouldn't use uninitialized variables in C++.

In D38167#879089, @ruiu wrote:

Well, this is not a bug, so this patch is not a fix but a workaround for existing FreeBSD/MIPS kernel's issue.

So the question is whether or not the issue is so significant and persistent that we need to extend our ABI to make a promise that lld fills gaps in .init/.fini sections with NOP bytes. I think the answer is no in order to make it simple. We shouldn't make any assumption on gap bytes (we shouldn't even assume that they are null or trap instructions) just like we shouldn't use uninitialized variables in C++.

I agree it is a bug in the CRT startup assembly code. However, it can be quite hard to track down where the trap instruction originated from (especially if /sbin/init faults). Would adding a warning or error if .init/.fini requires padding be an acceptable patch?

arichardson abandoned this revision.Nov 13 2018, 3:02 AM

LLD should not attempt to workaround bugs in crt*.o files