liballocs icon indicating copy to clipboard operation
liballocs copied to clipboard

Cache syscall trap sites using static metadata

Open stephenrkell opened this issue 6 years ago • 4 comments

It's not necessary or desirable to suffer start-up delay scanning a whole libc binary for syscalls. We used to hack around this by only scanning certain parts, but that is not robust.

Instead, we should do the scan offline and use static metadata. Just like our type metadata, it needs to be rebuilt when the binary is rebuilt, but that is OK and we can do a sanity check using timestamps.

Also it will eventually be necessary to worry about CoW and that fact that if we modify text in a shared library, it is no longer shareable. Most likely a pre-rewritten cached binary needs to be created, stored at a well-known place in the filesystem, and loaded by our modified dynamic linker in place of whatever the executable actually asked to load. That would restore text-sharing, at the cost of a hacky parallel world of library binaries in the filesystem. (Of course when every process in our whole system uses liballocs, that will go away!)

The rudiments of this static metadata should be pulled up into libsystrap or librunt.

stephenrkell avatar Oct 10 '19 09:10 stephenrkell

I'm thinking about using BAP to do this analysis, but still need to figure out how to write simple BAP clients.

stephenrkell avatar Nov 08 '19 13:11 stephenrkell

Maintaining a whole parallel cache of binaries doesn't seem great. Maybe a better idea is to 'flip' a binary: it can be memory-efficient in uninstrumented (U) form, or memory-efficient in instrumented (I) form, but functionally identical in both cases.

I suspect we can do this by fiddling with dynamic relocs. To make an I-form binary that includes a syscall, we statically munge the syscall into ud2 or whatever we want it to be, but add a dynamic reloc that will munge it back into its original form. For most two-byte system call instructions, this would probably be an R_X86_64_16, although wider relocs could also be used. When loaded under liballocs, we would ignore these "U-relocs".

Flipping is appealing because not many binaries include many syscalls. It's mostly only the libc that does. On the other hand, that is shared by all processes! Maybe we can relink the libc so that syscall sites are grouped together, limiting the cost of CoW-forking those pages.

Our liballocs loader must be able to distinguish I-form from U-form binaries, maybe from a note. An I-form binary need not be scanned for syscalls, and U-relocs should not be applied. It's not clear how to distinguish the U-relocs from ordinary relocs... especially on 32-bit platforms where the reloc type is only 8 bits.

Maybe we could get around this by using the symbol rather than the addend... this need not create proliferation, e.g. the displacement between ud2 and syscall is a single symbol shared by all such reloc sites. By using the symbol, we can simply define the symbol to be 0 under instrumented execution. One problem is that the ld.so may still write to the code, albeit writing an identical 0-adjusted value, so may still trigger copy-on-write. Being optimistic, it might be that the ld.so is clever enough to skip adjustments by 0, and/or that Linux is clever enough to avoid copying on no-change writes.

I'll try doing a proof-of-concept in a separate tree some time.

stephenrkell avatar Mar 13 '21 13:03 stephenrkell

This is more of a libsystrap issue, so I expect to fix it there... more here and here.

stephenrkell avatar Jun 04 '21 10:06 stephenrkell