This is an archive of the discontinued LLVM Phabricator instance.

[ELF2] PPC64 needs to delay local-call relocations until after the function-descriptor values are known
ClosedPublic

Authored by hfinkel on Oct 8 2015, 10:35 AM.

Details

Summary

I've separated this into a separate patch because it brings up a design question (which we might wish to solve as I've done here, or we might wish to solve in a completely different way).

Under PPC64 ELF v1 ABI, the symbols associated with each function name don't point directly to the code in the .text section (or similar), but rather to a function descriptor structure in a special data section named .opd. The elements in the .opd structure include a pointer to the actual code, and a the relevant TOC base value. Both of these are themselves set by relocations.

When we have a local call, we need the relevant relocation to refer directly to the target code, not to the function-descriptor in the .opd section. Only when we have a .plt stub do we care about the address of the .opd function descriptor itself.

We don't process relocations for the .opd section until after we process the code sections, and so when we're processing the relocation for a local call, we don't yet know the address of that call. We know the address of its function descriptor, but won't know the value in the descriptor until after we process relocations for the .opd section.

Here we store a vector of lambda function for each function descriptor, allowing us to go back and process the call relocations for local functions once the function-descriptor values are known.

Obviously still missing test cases, but feedback is certainly appreciated.

With this patch (and the others posted today), I can link, and run, a dynamically-compiled "hello world" application on big-Endian PPC64/Linux (ELF v1 ABI) using lld.

Diff Detail

Event Timeline

hfinkel updated this revision to Diff 36875.Oct 8 2015, 10:35 AM
hfinkel retitled this revision from to [ELF2] PPC64 needs to delay local-call relocations until after the function-descriptor values are known.
hfinkel updated this object.
hfinkel added reviewers: ruiu, rafael.
hfinkel added a project: lld.
hfinkel added a subscriber: llvm-commits.
ruiu edited edge metadata.Oct 8 2015, 1:45 PM

If my understanding is correct, the problem is that when we apply relocations, relocations in .opd may not have been applied yes, so you have to backfill relocations that refer function descriptors in .opd.

If so, it seems to me that you can solve the problem by applying all relocations in .opd first and then rest,. Doesn't this work?

In D13566#263240, @ruiu wrote:

If my understanding is correct, the problem is that when we apply relocations, relocations in .opd may not have been applied yes, so you have to backfill relocations that refer function descriptors in .opd.

If so, it seems to me that you can solve the problem by applying all relocations in .opd first and then rest,. Doesn't this work?

Yes, if we always applied relocations in .opd first, then we'd be able to save and use that information later for the relocations in the text sections.

Should we add some kind of target hook that allows it to somehow adjust the order in which the relocations are applied? Does this require separating that process from the order in which the sections are written? In short, how would you like this to work?

hfinkel updated this revision to Diff 37132.Oct 12 2015, 10:14 AM
hfinkel edited edge metadata.

Updated based on reviewer comments (and some IRC discussion), and added a test case.

ruiu added a comment.Oct 12 2015, 10:53 AM

Writer<ELFT>::writeSections() {

ELF/Target.cpp
399–405

You may be able to eliminate the hash table by reading back previous relocatino results for R_PPC64_ADDR64 from Buf.

if (!ForPltEntry)
  R = read64be(Buf + R - BaseAddr);
ELF/Target.h
93

Mutable memebers are not generally good, so I'd avoid using that.

ELF/Writer.cpp
652–662

Changing the order of the vector could have significant impact because that's not supposed to happen. Please do like this

for (OutputSectionBase<ELFT::Is64Bits> *Sec : OutputSections)
  if (Sec->getName() == ".opd")
    Sec->writeTo(Buf + Sec->getFileOff();
for (OutputSectionBase<ELFT::Is64Bits> *Sec : OutputSections)
  if (Sec->getName() != ".opd")
    Sec->writeTo(Buf + Sec->getFileOff();
hfinkel added inline comments.Oct 12 2015, 11:13 AM
ELF/Target.cpp
399–405

Two problems:

  1. The relocated addresses are in a different section (and, thus, I assume, have a different base buffer pointer)
  2. If the relocation is not pointing to a function descriptor (which it might not), then we need to keep the original symbol address.

An alternative would be to be able to explicitly get the .opd section, do a bounds check (just as I do for the .plt section), and then read from its buffer. Is there a good way to do that?

ELF/Target.h
93

The alternative is to make relocateOne non-const. Is that okay? [no need to answer this question if we don't need the map at all, as per the thread below].

ruiu added inline comments.Oct 12 2015, 11:22 AM
ELF/Target.cpp
399–405

Ah, you are right. But how about keeping a pointer to .opd section? If you have a pointer to .opd section, can you get the value you want from the section contents?

hfinkel updated this revision to Diff 37141.Oct 12 2015, 12:06 PM

Updated to reflect reviewer comments (and some discussion on IRC).

ruiu added inline comments.Oct 12 2015, 12:25 PM
ELF/OutputSections.cpp
43–50 ↗(On Diff #37141)

Please rebase to head.

ELF/OutputSections.h
290–291 ↗(On Diff #37141)

Alright, I found that not look very good. How about adding these two fields to PPC64TargetInfo?

hfinkel added inline comments.Oct 12 2015, 2:34 PM
ELF/OutputSections.h
290–291 ↗(On Diff #37141)

I'll try that.

FYI: PPC64, Itanium and HPPA seem to use the .opd section. Thus, while theoretically generic, I suspect we can consider this to be PPC64-specific in practice.

hfinkel added inline comments.Oct 12 2015, 2:50 PM
ELF/OutputSections.h
290–291 ↗(On Diff #37141)

Alright, this can certainly be done. The largest issue is that the type of the output section depends on the target's object type, and we cannot universally stash the pointer to the section away in PPC64 target info without some form of type erasure and/or dispatch to the target.

I'll upload a pure rebase of this patch, and then a version where I've moved the data members into PPC64TargetInfo (but using a void* to get around the aforementioned issue).

hfinkel updated this revision to Diff 37179.Oct 12 2015, 2:51 PM

Rebased to trunk.

hfinkel updated this revision to Diff 37181.Oct 12 2015, 2:52 PM

Uploading the full-context patch.

hfinkel updated this revision to Diff 37182.Oct 12 2015, 2:55 PM

Move the .opd section and buffer pointers into PPC64TargetInfo. Because the type of the section object depends on ELFT, I've made the type of pointer in PPC64TargetInfo a void* so that we can universally stash the pointer value there.

ruiu added inline comments.Oct 12 2015, 3:00 PM
ELF/Target.cpp
412
read64be(OpdBuf + S + A - OpdStart);
ELF/Target.h
90–91

No need to make them static. They can be non-static members.

Because PPC64 is ELF64BE, you can use the strict type.

OutputSection<ELF64BE> *OpdPtr = nullptr;
static uint8_t *OpdBuf = nullptr;
ELF/Writer.cpp
479–480
if (OutputSection<ELFT> *Sec = Map.lookup({".opd", ...))
  Target->Opd = reinterpret_cast<OutputSection<ELF64BE> *>(Sec);
hfinkel added inline comments.Oct 12 2015, 3:14 PM
ELF/Writer.cpp
479–480

Do you want me to move Opd and OpdBuf into TargetInfo to make this work?

ruiu added inline comments.Oct 12 2015, 3:31 PM
ELF/Writer.cpp
479–480

Ah, then this?

if (OutputSection<ELFT> *Sec = Map.lookup({".opd", ...))
  reinterpet_cast<PPC64TargetInfo *>(Target)->Opd = reinterpret_cast<OutputSection<ELF64BE> *>(Sec);
hfinkel added inline comments.Oct 12 2015, 3:42 PM
ELF/Writer.cpp
479–480

No, because they any object file with a section names .opd, that happens not to be targeting ppc64, will cause us to corrupt our internal data structures. This does not seem all that far fetched to me, because when we also add support for little-endian ppc64 (power8 and later), that target will not use .opd, but I can certainly imagine some tools still producing .opd sections.

To think ahead a little bit, can I add a Target callback to enable it to inspect the Map (or maybe just the OutputSections array)? The target needs to grab .opd to do this function-descriptor indirection, but that's not the end of this. We'll also want to grab .toc and .tocbss to really get the TOC base address calculation right. We'll want .branch_lt (which we'll need to add if not already present) in order to generate long-branch stubs.

ruiu added inline comments.Oct 12 2015, 3:52 PM
ELF/Writer.cpp
479–480

I'm not confident if we really want to have that function. That might be useful, but can be a bit overkill. I'd just add Opd and OpdBuf (but I'd name OpdStart instead of OpdBuf) to Out for now, which is the previous code. Sorry for back-and-force.

hfinkel added inline comments.Oct 12 2015, 3:57 PM
ELF/Writer.cpp
479–480

Not a problem.

Why OpdStart? I think that could be confused with the starting virtual address. Would OpdData be better?

ruiu added inline comments.Oct 12 2015, 4:06 PM
ELF/Writer.cpp
479–480

Mainly because Bigcheese is going to use Start prefix. But I don't have a strong preference. OK in either way.

hfinkel accepted this revision.Oct 12 2015, 4:19 PM
hfinkel added a reviewer: hfinkel.
This revision is now accepted and ready to land.Oct 12 2015, 4:19 PM
hfinkel closed this revision.Oct 12 2015, 4:19 PM

r250122, thanks!