This is an archive of the discontinued LLVM Phabricator instance.

[ELF] - Fix removing of unused synthetic sections.
AbandonedPublic

Authored by grimar on Sep 6 2017, 8:26 AM.

Details

Reviewers
ruiu
rafael
Summary

Code that removes unused synthetic sections has a bug
that leaves unused section in output if its description is duplicated
in linkerscript. Testcase shows the issue, patch fixes it.

Diff Detail

Event Timeline

grimar created this revision.Sep 6 2017, 8:26 AM
ruiu added inline comments.Sep 6 2017, 10:12 AM
ELF/Writer.cpp
1182–1183

This loop needs a (brief) comment.

grimar updated this revision to Diff 114135.Sep 7 2017, 3:04 AM
  • Addressed review comments.
ruiu edited edge metadata.Sep 7 2017, 12:17 PM

Sorry, but I do not understand this function well. It seems too complicated compared to what it does (which doesn't sound too complicated). Can you refactor?

grimar updated this revision to Diff 114354.Sep 8 2017, 6:48 AM
  • Rebased and refactored.
grimar updated this revision to Diff 114357.Sep 8 2017, 6:54 AM
  • Rewrote comment.
ruiu added inline comments.Sep 8 2017, 3:56 PM
ELF/Writer.cpp
1

// SS is an unused synthetic section. Remove it from output section commands.

1

Please fix grammatical errors.

2–5

This code still looks odd. Can SS belong to more than one OS->Commands?

grimar updated this revision to Diff 114561.Sep 11 2017, 3:56 AM
  • Addressed review comments.
  • Changed inplementation to use Live flag for removing output section.
ELF/Writer.cpp
2–5

No. That was just short way to write the code. Rewrote this place to longer, but more straightforward way,
do you prefer new way more ?

grimar updated this revision to Diff 114565.EditedSep 11 2017, 4:23 AM
  • Previous revision deviated too far from original intention of patch and can be separate refactoring, reverted it to use original approach.
ruiu added inline comments.Sep 19 2017, 12:37 PM
ELF/Writer.cpp
1

// If SS was the only section content, remove the entire output section.

1185–1186

Do you actually have to scan over output sections? I mean, InputSection has getOutputSection function, and I wonder if you can just use to know the output section that an input section is contained.

grimar updated this revision to Diff 115989.Sep 20 2017, 5:52 AM
  • Addressed comments.
ELF/Writer.cpp
1185–1186

Here we scan not over output sections, but over commands it contains. OS is an OutputSection*.

It would be possible to avoid scanning if we would have pointer to InputSectionDescription command in each section (and InputSectionDescription can probably have poiner to OutputSection then), but we do not have it currently and I doubt we really need it.

ruiu added inline comments.Sep 22 2017, 11:35 AM
ELF/Writer.cpp
1195–1199

I think I do not understand this code. You don't need to use all_of, right? The only way in which OS becomes empty is (1) it contains only one InputSectionDescription and (2) that InputSectionDescription is empty, no?

grimar added inline comments.Sep 25 2017, 3:40 AM
ELF/Writer.cpp
1195–1199

No. If OS has 2 or more ISDs which are empty, it is also empty.
For example in script from testcase we have ".got : { *(.got) *(.got) }",
which means we have OS .got and 2 ISDs, one has input section and one is empty from start.
When we remove unused input section from first ISD it becomes empty, and we want to remove whole OS.
For that we have to check if all ISDs are empty.

ruiu added inline comments.Sep 25 2017, 10:11 AM
ELF/Writer.cpp
1195–1199

So you are saying that .got output section is not created for .got : { *(.got) } if .got synthetic input section is empty, that is what I expected.

But what about some output section .foo where it is created as .foo : { *(.bar) }, if no input file have .bar section? Do we still create .foo?

If yes, that behavior is inconsistent.

If no, there must be a place we remove empty output sections. Doing the same thing here doesn't make sense.

grimar added inline comments.Sep 26 2017, 3:55 AM
ELF/Writer.cpp
1195–1199

But what about some output section .foo where it is created as .foo : { *(.bar) }, if no input file have .bar section? Do we still create .foo?

No.

If no, there must be a place we remove empty output sections. Doing the same thing here doesn't make sense.

That place is adjustSectionsBeforeSorting(). There we mark output sections as Live even if they
have no output sections but still have other commands.
For .foo : { *(.bar) }, .foo will remain dead because Live flag is never set (it is normally set when we add any input section).

Alternative to removing of OS sections here is to set Live bit to false under some condition, which is I belive "if all ISDs are empty" and that is what this patch anyways already checks for removing the section.
FWIW I already did that in one of early diff of this patch (https://reviews.llvm.org/D37520?id=114561) and refused from that diff because it seems better to do as separate patch, as it touches other parts of code and unrelated to this patch because original code removes OS as well.

ruiu added inline comments.Sep 27 2017, 9:37 PM
ELF/Writer.cpp
1195–1199

I don't think I fully understand your justification. Are you saying that there is a reason that you have to have two pieces of code that basically does the same thing?

grimar added inline comments.Sep 28 2017, 9:12 AM
ELF/Writer.cpp
1195–1199

What we have now is code in removeEmptyCommands() which removes all output sections that are not live.

In current method we have live section which should be dead after removing synthetic, so either needs to be marked as dead so that removeEmptyCommands() can handle it or be removed.

For first case code here can probably be (after additional patch changing things outside this method, without that if will not work):

Approach #1:

OS->Live = !llvm::all_of(OS->Commands, [](BaseCommand *B) {
   auto *ISD = dyn_cast<InputSectionDescription>(B);
   return ISD && ISD->Sections.empty();
 });

for second - code from this patch, which is:

Approach #2:

bool IsEmpty = llvm::all_of(OS->Commands, [](BaseCommand *B) {
  auto *ISD = dyn_cast<InputSectionDescription>(B);
  return ISD && ISD->Sections.empty();
});
if (IsEmpty)
  llvm::erase_if(Script->Opt.Commands,
                 [&](BaseCommand *Cmd) { return Cmd == OS; });

Difference is in llvm::erase_if call, which also present in original code this patch fixes. Doing approach #1 is probably better.
But that is compoletely unrelative to what this patch fixes and should be (or should not be) done separatelly. That is my point.

grimar updated this revision to Diff 117320.Oct 2 2017, 3:51 AM
  • Rebased.
grimar abandoned this revision.Dec 1 2017, 4:13 AM