Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use gimli without alloc #581

Closed
nbdd0121 opened this issue Aug 27, 2021 · 16 comments · Fixed by #596
Closed

Use gimli without alloc #581

nbdd0121 opened this issue Aug 27, 2021 · 16 comments · Fixed by #596

Comments

@nbdd0121
Copy link
Contributor

I am working on a pure Rust stack unwinder (https://github.com/nbdd0121/unwind). It's now fully functional on hosted platforms (with libc dependency), and my intention is for it to work on baremetal #![no_std] binaries as well. It's sort of working right now, but requires alloc, which is a big issue because allocator isn't always available or desirable for baremetal programs.

Currently my unwind crate uses two things that require allocation:

  • UninitializedUnwindContext and
  • Evaluation

One way to avoid having them performing allocation themselves is to ask the user to supply the backing memory. This creates usability issues for users that can use allocator, so I think an approach similar to managed crate could be ideal. This approach is used by smoltcp crate to avoid implicit allocations.

@bjorn3
Copy link
Contributor

bjorn3 commented Aug 27, 2021

UninitializedUnwindContext only uses a Box to prevent moving a large struct around. It doesn't use it to allocate a different amount of memory depending on the actual unwinding data. Evaluation does use a Vec with a dynamic size though.

As alternative maybe an ORC unwinder would be able to completely eliminate allocations? This probably would only allow backtraces though and not running cleanup on unwinding.

@nbdd0121
Copy link
Contributor Author

UninitializedUnwindContext only uses a Box to prevent moving a large struct around. It doesn't use it to allocate a different amount of memory depending on the actual unwinding data.

Yes. I am aware. I think that'll be a easy change to make it accept a &mut MaybeUninit<UnwindContext> or something similar instead. However because it internally essentially manages a stack vec of RegisterRuleMap, so I think there are possibilities it could do better, e.g. say, accept a &mut [MaybeUninit<RegisterRuleMap>] as backing storage instead.

Evaluation does use a Vec with a dynamic size though.

One approach would just be accepting a &mut [MaybeUninit<_>] for the stack and the results. It could throw an error if capacity is not enough. libgcc seems to just be using a fixed size stack of size 64, and since unwinding doesn't support piece op, result just need a capacity of 1.

I'll prototype to see if I can come up with an ergonomic approach so that users with alloc wouldn't need to care.

As alternative maybe an ORC unwinder would be able to completely eliminate allocations? This probably would only allow backtraces though and not running cleanup on unwinding.

I do actually want the ability to do unwind and catch panic! Catching panic is the only viable way to do panic recovery. Without unwinding the stack couldn't be cleanup so either the stack has to leak or intrusive data structures would be unsound.

@philipc
Copy link
Collaborator

philipc commented Aug 27, 2021

I think an approach similar to managed crate could be ideal.

What exactly does that mean? Use an enum and only enable the owned variant when the alloc feature is enabled? If so, I don't think that is needed because we have no need to dynamically switch between borrowed and owned, so it would unnecessarily add some overhead and a lifetime parameter when alloc enabled. I think type parameters would be fine instead. We can default them to what they are currently. For Evaluation, I guess we need to define a trait for the common interface between Vec and something like ArrayVec.

@philipc
Copy link
Collaborator

philipc commented Aug 27, 2021

If we can come up with a solution that doesn't need type parameters then that would be even better. e.g. Evaluation could swap between Vec and ArrayVec based on the feature. We already have a custom ArrayVec implementation for RegisterRuleMap.

@nbdd0121
Copy link
Contributor Author

What's the policy on raising MSRV? I'd like to use const_generics but that's only available in Rust 1.51.

@philipc
Copy link
Collaborator

philipc commented Aug 27, 2021

No fixed policy, raise when needed but try to avoid causing pain for others. Rust 1.51 is probably still too recent.

@philipc
Copy link
Collaborator

philipc commented Oct 12, 2021

From a quick look at disabling the alloc dependency completely, the main problem is Reader has methods that require it.

@nbdd0121
Copy link
Contributor Author

These methods could be cfg-ed away. To me, the main problem seems to be Abbreviations with its btreemap.

@bjorn3
Copy link
Contributor

bjorn3 commented Oct 12, 2021

These methods could be cfg-ed away.

What if one dep doesn't use alloc and implements this trait and another dep uses alloc? You would get a compilation error due to the missing functions in the impl.

@nbdd0121
Copy link
Contributor Author

A default implementation could be supplied

@philipc
Copy link
Collaborator

philipc commented Oct 12, 2021

Do you need Abbreviations without alloc? I was expecting to disable everything except for cfi/op.

@nbdd0121
Copy link
Contributor Author

Do you need Abbreviations without alloc? I was expecting to disable everything except for cfi/op.

No, but op depends on unit which depends on abbrev. I guess I'll just keep cfging out things until it compiles :)

@philipc
Copy link
Collaborator

philipc commented Oct 12, 2021

Yeah, cfg everywhere is a bit ugly but should work. I think op only needs UnitOffset, which can be moved out of unit.

If we had to, the btreemap in Abbreviations could be made optional, since most DWARF shouldn't need it.

@mstange
Copy link
Contributor

mstange commented Oct 14, 2021

As alternative maybe an ORC unwinder would be able to completely eliminate allocations?

I believe ORC is only used in the kernel. An ORC unwinder would not be able to unwind user-space programs. Unless I've misunderstood things?

@bjorn3
Copy link
Contributor

bjorn3 commented Oct 14, 2021

It should be possible to generate ORC unwind info from DWARF unwind info for userspace programs the same way I believe the Linux kernel does. It may require an extension to provide the personality function and LSDA though if ORC doesn't already have a field for this if you want to use the unwinder for exception handling and not just backtraces.

@mstange
Copy link
Contributor

mstange commented Oct 14, 2021

Interesting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants