This is an archive of the discontinued LLVM Phabricator instance.

[llvm-readelf/obj] - Handle out-of-order PT_LOADs better.
ClosedPublic

Authored by grimar on Dec 4 2020, 1:02 AM.

Details

Summary

This is https://bugs.llvm.org/show_bug.cgi?id=45698.

Specification says that
"Loadable segment entries in the program header table appear
in ascending order, sorted on the p_vaddr member."

Our toMappedAddr() relies on this condition. This patch
adds a warning when the sorting order of loadable segments is wrong.
In this case we force segments sorting and that allows
toMappedAddr() to work as expected.

Diff Detail

Event Timeline

grimar created this revision.Dec 4 2020, 1:02 AM
grimar requested review of this revision.Dec 4 2020, 1:02 AM
Herald added a project: Restricted Project. · View Herald TranscriptDec 4 2020, 1:02 AM

From the description:
"Out toMappedAddr()" -> "Our toMappedAddr()"

llvm/lib/Object/ELF.cpp
580–586

I know this is what the ELF specification says, but I wonder whether it would be almost as easy to sort the segments here (maybe in addition to a warning, specified via a callback probably, if they are unsorted), rather than just giving up? That would be somewhat more useful than just warning and not doing anything.

Also, this is library code - is this code used elsewhere? It might be more appropriate for the test case to be in the Object tests.

llvm/test/tools/llvm-readobj/ELF/dynamic-malformed.test
419
grimar updated this revision to Diff 310504.Dec 9 2020, 5:22 AM
grimar marked 2 inline comments as done.
grimar edited the summary of this revision. (Show Details)
  • Addressed review comments.
llvm/lib/Object/ELF.cpp
580–586

Done.

grimar added inline comments.Dec 9 2020, 5:26 AM
llvm/lib/Object/ELF.cpp
580–586

Also, this is library code - is this code used elsewhere? It might be more appropriate for the test case to be in the Object tests.

Yes, it is used in lib\InterfaceStub. I've added the unit test.

jhenderson added inline comments.Dec 10 2020, 1:08 AM
llvm/unittests/Object/ELFObjectFileTest.cpp
377 ↗(On Diff #310504)

I think if DataOrErr is false, we need to somehow handle the error, right? Actually maybe this should just be ASSERT_TRUE?

grimar marked an inline comment as done.Dec 10 2020, 1:50 AM
grimar added inline comments.
llvm/unittests/Object/ELFObjectFileTest.cpp
377 ↗(On Diff #310504)

I can't use ASSERT_TRUE here (or ASSERT_THAT_EXPECTED). Its intention is to return instantly,
and so it calls return internally I think. And so I can't use it in lambda:

Error	C2440	'return': cannot convert from 'void' to 'const uint8_t *'	ObjectTests	D:\Work3\LLVM\llvm-project\llvm\unittests\Object\ELFObjectFileTest.cpp	377

I think if DataOrErr is false, we need to somehow handle the error, right?

I don't think it is very important here? We expect toMappedAddr call to succeed here,
this is what test is wrapped around. When something in a unit test goes wrong,
we can probably just report and fail, no matter how.

grimar marked an inline comment as done.Dec 10 2020, 10:58 PM
grimar added inline comments.
llvm/unittests/Object/ELFObjectFileTest.cpp
377 ↗(On Diff #310504)

I can consume an error and return nullptr right after EXPECT_TRUE((bool)DataOrErr) probably.
It should be a bit cleaner. Should I?

jhenderson added inline comments.Dec 11 2020, 5:59 AM
llvm/unittests/Object/ELFObjectFileTest.cpp
377 ↗(On Diff #310504)

If you fail to consume the error in one form or other, this test will crash if the DataOrErr check fails, either from the unchecked error or the final dereference of DataOrErr at the end of the function, rather than producing a useful "test failed" type message. I don't think that's desirable. You could always try reporting the error via the << syntax if the check fails before returning. I can't remember the exact syntax, but I believe it's something like EXPECT_TRUE((bool)DataOrErr) << toString(DataOrErr.takeError()); That being said, shouldn't this be EXPECT_THAT_EXPECTED(DataOrErr, Succeeded());? I'm guessing that consumes the error safely (you'll still need to be careful about the dereference).

grimar updated this revision to Diff 311521.Dec 14 2020, 1:42 AM
grimar marked an inline comment as done.
  • Addressed review comment.
llvm/unittests/Object/ELFObjectFileTest.cpp
377 ↗(On Diff #310504)

I've used EXPECT_THAT_EXPECTED. Yes, it calls handleAllErrors internally.

grimar planned changes to this revision.Dec 14 2020, 4:43 AM
grimar updated this revision to Diff 311556.Dec 14 2020, 5:18 AM
  • Fixed an issue with the unit test.
llvm/unittests/Object/ELFObjectFileTest.cpp
377 ↗(On Diff #310504)

I've found that EXPECT_THAT_EXPECTED doesn't help much here.

The problem is in the line below:

return !DataOrErr ? nullptr : *DataOrErr;

EXPECT_THAT_EXPECTED takes the error from DataOrErr,
but the unchecked bit is then set again by the operator bool:

  explicit operator bool() {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
    Unchecked = HasError;
#endif
    return !HasError;
  }

I think we should handle an error in a straighforward way here.

jhenderson added inline comments.Dec 15 2020, 12:59 AM
llvm/unittests/Object/ELFObjectFileTest.cpp
391 ↗(On Diff #311556)

Perhaps you could add a single ASSERT_TRUE(Data) (or similar)? You could then make this and the equivalent line below something like EXPECT_EQ(Data[0], 0x11); which would give a nicer message.

grimar updated this revision to Diff 311842.Dec 15 2020, 1:47 AM
grimar marked an inline comment as done.
  • Addressed review comment.
llvm/unittests/Object/ELFObjectFileTest.cpp
391 ↗(On Diff #311556)

Done.

This revision is now accepted and ready to land.Dec 16 2020, 12:37 AM
MaskRay accepted this revision.Dec 16 2020, 12:48 AM
This revision was automatically updated to reflect the committed changes.