This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Refactor DataBuffer so we can map files as read-only
ClosedPublic

Authored by JDevlieghere on Mar 31 2022, 6:09 PM.

Details

Summary

Currently, all data buffers are assumed to be writable. This is a problem on macOS where it's not allowed to load unsigned binaries in memory as writable. To be more precise, MAP_RESILIENT_CODESIGN and MAP_RESILIENT_MEDIA need to be set for mapped (unsigned) binaries on our platform.

Binaries are loaded through FileSystem::CreateDataBuffer which returns a DataBufferLLVM. The latter is backed by a llvm::WritableMemoryBuffer because every DataBuffer is considered to be writable. In order to use a read-only llvm::MemoryBuffer I had to split DataBuffer into DataBuffer (read-only) and WritableDataBuffer (read-write).

rdar://74890607

Diff Detail

Event Timeline

JDevlieghere created this revision.Mar 31 2022, 6:09 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 31 2022, 6:09 PM
JDevlieghere requested review of this revision.Mar 31 2022, 6:09 PM

Yeah, I tried doing this (well, i used const DataBuffer to represent read-only data) last time this topic came up, and ran into the same problem. Since there was no pressing need for it, I put the patch away.

I think the best way to fix this is to allow the object file classes to control the way the object file is mapped into memory (during creation). Then the ObjectFileELF could map the the file read-write, and the rest would do it read-only (though I think it's possible we need to relocate COFF files as well, only no one ran into the issue yet). If we wanted to be very fancy, we could check whether the object file needs relocations (basically, does it have any .rel(a).debug_*** sections) and *then* map the file read-write, but I don't think that's really necessary.

That doesn't directly help with the code in ObjectFileELF::ApplyRelocations, since it would still get an object whose static type prohibits writes. However, if we can ensure that the dynamic type of the object is alright, then we can put an appropriate cast there, and put a comment pointing to the place where the object is created.

In llvm the relocations are being applied on-the-fly as the data is being read. That would be another possibility, but it would be a much more intrusive change (and tbh, I can't say I like the llvm model that much). OTOH, it would go a long way towards unifying llvm and lldb object&dwarf readers....

JDevlieghere edited the summary of this revision. (Show Details)
  • Implement Pavel's suggestion of mapping ELF files as writable and casting to a WritableMemoryBuffer.
shafik added a subscriber: shafik.Apr 1 2022, 10:25 PM
shafik added inline comments.
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
236

nitpick reinterpret_cast instead of C-style cast.

labath added inline comments.Apr 4 2022, 11:07 AM
lldb/include/lldb/Utility/DataBuffer.h
53–58

Replace with using DataBuffer::GetBytes ?

58

Are you sure this should be public?

60–124

ditto

lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
363

I guess this should be done unconditionally now.

393

I am assuming this will always point to a writable kind of a data buffer. Could we change the prototype to reflect that?

2635

add // ObjectFileELF creates a WritableDataBuffer in CreateInstance

JDevlieghere marked 7 inline comments as done.
JDevlieghere added inline comments.
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
363

No, I tried doing that actually and it broke a bunch of unit tests. I can take a look at it later this week if you think this is important.

393

Are you okay with making that a separate patch?

JDevlieghere added inline comments.
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
393
labath added inline comments.Apr 5 2022, 1:31 AM
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
363

Right, I think I know what's going on. The unit tests use a (relatively new) object file feature where you can construct an ObjectFile instance without a backing file by providing all of its data upfront.

I think it is important to have a story for all entry points into the ELF code, since that is what makes the cast in ApplyRelocations sound. There are several ways to handle this. In my order of preference, they would be:

  • In the file backed case, the initial 512-byte buffer will be heap-based (hence, effectively writable). If it is acceptable (and statically type-safe) to make the the buffer for the buffer-backed case writable, then we could change the CreateInstance prototype to take a WritableDataBuffer. Only the full mappings done by individual subclasses would be read-only. The only (non-test) use case for this feature is for some mach-o shenanigans so you'd be best qualified to determine if this would work.
  • If we can be sure that the buffer-backed buffer is always writable, but we cannot enforce that in the type system, then we could add a comment documenting that the buffers dynamic type must be writable in one does not provide a backing file.
  • Failing all that, we could have ObjectFileELF make a heap copy of the incoming data buffer in the buffer-backed case. Since there's no production use case for buffer-backed ELF files I think that should be fine. We could also have the ELF class refuse to open such files, but that would mean changing the unit tests back to using temporary files, which would make me sad.
JDevlieghere marked an inline comment as done.

I don't really like the idea of making the argument to createInstance writable. That seems like an implementation detail. Instead I added a virtual method IsWritable() to the DataBuffer and an assert to ObjectFileELF that ensures we always have a writable buffer.

@labath: does that cover your concerns?

  • Copy the buffer if ObjectFileELF didn't map it
  • Use LLVM style RTTI
labath accepted this revision.Apr 5 2022, 12:30 PM

Looks good. Thanks for your patience.

lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
2636–2638

use llvm::cast instead.

This revision is now accepted and ready to land.Apr 5 2022, 12:30 PM
This revision was landed with ongoing or failed builds.Apr 5 2022, 1:46 PM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptApr 5 2022, 1:46 PM