This is an archive of the discontinued LLVM Phabricator instance.

[WebAssembly] Verify contents of relocations target before writing it
ClosedPublic

Authored by sbc100 on Mar 9 2018, 9:25 PM.

Details

Summary

Verify that the location where a relocation is about the be
applied contains the expected existing value.

This is essentially a sanity check to catch bugs in the compiler
and the linker.

Event Timeline

sbc100 created this revision.Mar 9 2018, 9:25 PM
sbc100 updated this revision to Diff 137898.Mar 9 2018, 9:30 PM
  • factor out separate change
sbc100 updated this revision to Diff 137899.Mar 9 2018, 9:32 PM

fix type

Harbormaster completed remote builds in B15927: Diff 137899.
ncw added a comment.Mar 10 2018, 8:28 AM

Cool, this is a good error-catching device. It could perhaps be debug-only on assert-only - but it's not exactly an expensive check, so I don't mind it being always on.

wasm/InputFiles.cpp
133

Can't you take the address of imported functions - yet functionTypes only contains the defined ones?

Something doesn't seem quite right about these indexes, but I'd have to play around with a couple of examples to be sure. I think the code that uses TableEntries above is correct (ie ElementIndex is the right thing to use).

sbc100 added inline comments.Mar 10 2018, 5:39 PM
wasm/InputFiles.cpp
133

You are right.. i was trying to calculate the max function index here but I failed. I will try again :)

sbc100 updated this revision to Diff 137933.Mar 10 2018, 6:13 PM
  • feedback
ncw accepted this revision.Mar 12 2018, 8:56 AM

Looks good!

This revision is now accepted and ready to land.Mar 12 2018, 8:56 AM
sbc100 retitled this revision from [WebAssembly] Verify contents of relocations target before writing in to [WebAssembly] Verify contents of relocations target before writing it.Mar 12 2018, 12:54 PM
This revision was automatically updated to reflect the committed changes.
ruiu added a comment.Mar 14 2018, 1:58 PM

I doubt that you really need to do that for every invocation of the linker. Generally speaking, relocation handling is what you want to optimize the most because the number of relocation can be really huge (it can be tens of millions). In addition to that, there are a lot of different ways that a compiler can be wrong, and catching only one error in the linker doesn't make much sense to me. Why is finding this particular error so important? We generally trust compilers that they create sane object files. To be honest, I think we should remove this check completely.

I doubt that you really need to do that for every invocation of the linker. Generally speaking, relocation handling is what you want to optimize the most because the number of relocation can be really huge (it can be tens of millions). In addition to that, there are a lot of different ways that a compiler can be wrong, and catching only one error in the linker doesn't make much sense to me. Why is finding this particular error so important? We generally trust compilers that they create sane object files. To be honest, I think we should remove this check completely.

I'd be OK with doing this is debug builds only perhaps.

The motivating reason is that we are thinking of adding linker relaxation for accessing external global addresses. I was speaking to @mcgrathr about this and he said that the linker (in this case at least) can/should/does check for sanity of the surrounding instructions before replacing/modifying it. So I'd also be OK only doing this when we do relaxation perhaps?

ruiu added a comment.Mar 14 2018, 2:44 PM

The motivating reason is that we are thinking of adding linker relaxation for accessing external global addresses. I was speaking to @mcgrathr about this and he said that the linker (in this case at least) can/should/does check for sanity of the surrounding instructions before replacing/modifying it. So I'd also be OK only doing this when we do relaxation perhaps?

Maybe. If you already have some piece of data when processing something, and you can easily use that data to verify something, you probably should do that. But I don't think we do some extra validation for the sake of validation. In particular, creating a map and look it up for each relocation just for validation is too expensive and doesn't feel lld-ish.

So Sam and I had dinner together. :-) I don't know a lot about WebAssembly and Sam doesn't know a lot about traditional (e.g. ELF) linkers, so we talked about the parallels in somewhat general terms while explaining the specifics to each other without distracting from our dessert.
In talking about what WebAssembly wants to do with converting some kinds of references to other kinds, I described the obvious analogy to the various kinds of linker relaxation that have been done in traditional machine code linking such as ELF.
There are two precedents that I can cite in detail off hand. In both cases the reloc type is specified in the ABI as for use with certain instruction patterns and the linker is expected to rewrite instructions with complete knowledge of their meaning, rather than just deliver values into bitfields.
One is x86-64's GOTPCREL and GOTPCRELX relocs, where GNU linkers check for expected opcode bytes immediate prior to the r_offset location and silently forego the instruction-rewriting optimization if the reloc is not used in the expected context.
The other is x86-64's TLS relocs, where GNU linkers check the opcode bytes immediatley prior to the r_offset location and diagnose an error if they don't match specific instruction patterns prescribed in the ABI for each reloc that enables linker relaxation (IIRC for some cases the x86 ABI mandates that linkers perform the relaxation, rather than just describing how the optimization is possible).
I don't know off hand how LLD handles these cases, but I would say that matching the GNU linkers' behavior is right--with the possible caveat that perhaps there should be a warning emitting for using GOTPCRELX relocs with instructions the linker doesn't know how to rewrite.
I know that other machines have many more relocs that are prescribed in their ABIs as for use with specific instruction patterns, but I don't know off hand what the details of those are, nor how existing linkers handle situations where such relocs are used with mismatching instructions.

ruiu added a comment.Mar 14 2018, 3:16 PM

So Sam and I had dinner together. :-) I don't know a lot about WebAssembly and Sam doesn't know a lot about traditional (e.g. ELF) linkers, so we talked about the parallels in somewhat general terms while explaining the specifics to each other without distracting from our dessert.
In talking about what WebAssembly wants to do with converting some kinds of references to other kinds, I described the obvious analogy to the various kinds of linker relaxation that have been done in traditional machine code linking such as ELF.
There are two precedents that I can cite in detail off hand. In both cases the reloc type is specified in the ABI as for use with certain instruction patterns and the linker is expected to rewrite instructions with complete knowledge of their meaning, rather than just deliver values into bitfields.
One is x86-64's GOTPCREL and GOTPCRELX relocs, where GNU linkers check for expected opcode bytes immediate prior to the r_offset location and silently forego the instruction-rewriting optimization if the reloc is not used in the expected context.
The other is x86-64's TLS relocs, where GNU linkers check the opcode bytes immediatley prior to the r_offset location and diagnose an error if they don't match specific instruction patterns prescribed in the ABI for each reloc that enables linker relaxation (IIRC for some cases the x86 ABI mandates that linkers perform the relaxation, rather than just describing how the optimization is possible).
I don't know off hand how LLD handles these cases, but I would say that matching the GNU linkers' behavior is right--with the possible caveat that perhaps there should be a warning emitting for using GOTPCRELX relocs with instructions the linker doesn't know how to rewrite.

lld relaxes these relocations assuming that a compiler emit an appropriate instruction to where a relocation is applied. We read data from the location where a relocation is applied to if it is needed to relax it, but we don't really verify whether an instruction at that location is a valid one or not. lld just does what is instructed to do by a compiler when processing relocations, and I like it. I don't think that finding a mismatching instruction for some type of relocation adds a practical value to the linker.

I know that other machines have many more relocs that are prescribed in their ABIs as for use with specific instruction patterns, but I don't know off hand what the details of those are, nor how existing linkers handle situations where such relocs are used with mismatching instructions.

Would you be OK with leaving this code in behind ifndef NDEBUG? I think it is of some use during development at least.

ruiu added a comment.Mar 14 2018, 3:33 PM

Can you make it a completely separated pass in the linker? I think that you can construct a table and visit all relocations (or symbols?) in a separate function rather than doing while processing some other data.