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

Add no_std compatibility #106

Merged
merged 4 commits into from
Jun 17, 2024
Merged

Conversation

SzymonKubica
Copy link
Contributor

@SzymonKubica SzymonKubica commented Mar 18, 2024

What was added?

Added support for rbpf to run on no_std environments.

Why is it needed?

Using rbpf on targets without std support increases compatibility of the crate.
I've been using a modified version of rbpf to run eBPF programs on microcontrollers (see [here(https://github.com/SzymonKubica/mibpf))

Testing

Currently the code builds successfuly with both configuration options enabled, i.e. all build configurations succeed:

cargo build
cargo build --features cranelift
cargo build --no-default-features
cargo build --features cranelift --no-default-features

All tests (except for the ones using the print - only available in std) pass in both configuration options.

TODO items for the PR

  • test no_std on an example embedded device

Notes

The structure of std / no_std feature-gating is inspired by serde, inside of lib.rs a public module: lib is defined and it
contains reexports of all required items from std/alloc/core. It is then used throughout the codebase by importing all items
declared inside of it:

use crate::lib::*;

This behaves similarly as if we were using the standard library as we don't have to import things like String or Vec manually.

@qmonnet
Copy link
Owner

qmonnet commented Mar 25, 2024

Hi, and thanks!
Sorry for the delay, I meant to take a look last weekend but this slipped out of my mind 😬. I'll probably need a few more days before I have time to look into the details of your PR.

In the meantime, would you mind fixing the issue raised by the Appveyor workflow please (Windows)? Given that you use config(jit) instead of Windows-related configuration options, we no longer disable the JIT stuff on Windows, causing the tests to try to run with the JIT and fail on Windows.

Could you please also sign-off your commits?

@qmonnet qmonnet self-requested a review March 25, 2024 15:31
@SzymonKubica
Copy link
Contributor Author

Hi, thank you for looking into the PR. Please don't worry about the timeline / delays, I realise that it is a non-trivial change and requires time to actually look through all of this.

I will look into the appveyor issue on windows and make sure it gets fixed. I will also start signing off the commits from now on.
Thanks

Copy link
Owner

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delay, I've kept this PR in a corner of my mind but struggled to find the time (and motivation) to dive into it.

Here are some elements of feedback, I see that the PR is indeed still in progress and needs some polishing. See also inline below.

We'd need specific jobs in the CI workflow as well, to make sure that we build both with and without the standard library to test your changes and detect regressions.

examples/rbpf_plugin.rs Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
src/ebpf.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/without_std.rs Outdated Show resolved Hide resolved
src/without_std.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
examples/rbpf_plugin.rs Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
@SzymonKubica
Copy link
Contributor Author

Hi,
Thank you for reviewing the PR and listing the required changes.
I will start going through these and clarify in the comments above if anything turns out to be unclear.

No worries about the delay, my goal with this PR was to contribute back a small subset of changes that I needed to introduce to
rbpf for my project. I've been working on a heavily-modified fork for a while and added an ARMv7 JIT to it. Given the scope of those changes and the fact that they are coupled with my specific requirements, I don't think I will be trying to make them mergeable with the upstream rbpf (although if you want to have a look it's here)

I think compared to those changes, adding no_std support is potentially useful so I will continue working on this PR

@SzymonKubica SzymonKubica force-pushed the rbpf-no-std-port branch 2 times, most recently from b320dfd to 88e27cd Compare May 29, 2024 09:39
@SzymonKubica
Copy link
Contributor Author

One note regarding your comment about asm_parser and disassembler being disabled:
I have looked into those and I don't see anything which would prevent them from running under no_std. I will update those so that they don't have to be disabled. It was probably an oversight on my part as I was primarily interested in using the VM.

@SzymonKubica
Copy link
Contributor Author

Ok I did some restructuring and now the import switching is done the same way serde does it.
I also changed some dependencies and now all modules build with both std/no_std.
I will look into the jit feature and see if I can make it work with windows (it could be that I can make the JIT enabled in no_std, in which case I could simply revert to using the windows feature flag)

README.md Outdated Show resolved Hide resolved
src/ebpf.rs Outdated Show resolved Hide resolved
examples/rbpf_plugin.rs Outdated Show resolved Hide resolved
@qmonnet
Copy link
Owner

qmonnet commented May 29, 2024

Can you please sign-off your commits?

@SzymonKubica
Copy link
Contributor Author

Re: signing off commits
Yes I will do that from now on, I think some of my previous commits were already signed-off, but I noticed that the build doesn't go through because the old ones weren't signed-off. I will apply the clean changes by signing off commits and force push the update

@SzymonKubica SzymonKubica reopened this May 29, 2024
@SzymonKubica SzymonKubica changed the title [WIP] Adding no_std support Add no_std compatibility May 29, 2024
@SzymonKubica SzymonKubica force-pushed the rbpf-no-std-port branch 4 times, most recently from 1544093 to ce48245 Compare May 30, 2024 16:30
@SzymonKubica
Copy link
Contributor Author

Ok @qmonnet I think the PR is in much better state now. Following your advice I have split my changes into two logical commits:

  • adding the "std" feature and modifying the feature-gating for the jit to require that both not(windows) and feature = "std" are set.
  • adding the proper reexports for no_std
    I updated the description of the PR, now everything builds correctly and all available tests pass in both std and no_std.

I have also tested that it builds correctly on an actual no_std target: STM32-f439zi running RIOT. I managed to flash this example program:

#![no_std]

use riot_wrappers::riot_main;
use riot_wrappers::println;
use riot_wrappers::riot_sys::malloc;

extern crate rust_riotmodules;
extern crate rbpf;
extern crate alloc;

riot_main!(main);

fn main() {
    let prog = &[
         0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
         0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
         0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
     ];
     let mem = &mut [
         0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
     ];

     // Just for the example we create our metadata buffer from scratch, and we store the
     // pointers to packet data start and end in it.
     let mut mbuff = [0u8; 32];
     unsafe {
         let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
         let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
         *data     = mem.as_ptr() as u64;
         *data_end = mem.as_ptr() as u64 + mem.len() as u64;
     }

     // Instantiate a VM.
     let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

     // Provide both a reference to the packet data, and to the metadata buffer.
     let res = vm.execute_program(mem, &mut mbuff).unwrap();
     assert_eq!(res, 0x2211);
     println!("res: 0x{:x}", res);
}

It looks like everything works in no_std. One note: I ended up having to disable the assembler and asm_parser as they depend on the combine crate which doesn't have the no_std mode.

I can quickly publish a repo with the experimental setup that I used to test the new version of rbpf running without std in RIOT if you would find this helpful.

Copy link
Owner

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks much better, thanks! Some comments here and there.

examples/disassemble.rs Outdated Show resolved Hide resolved
examples/disassemble.rs Outdated Show resolved Hide resolved
examples/load_elf.rs Outdated Show resolved Hide resolved
examples/uptime.rs Outdated Show resolved Hide resolved
src/disassembler.rs Outdated Show resolved Hide resolved
src/no_std_error.rs Outdated Show resolved Hide resolved
src/no_std_error.rs Outdated Show resolved Hide resolved
tests/misc.rs Outdated Show resolved Hide resolved
tests/ubpf_verifier.rs Outdated Show resolved Hide resolved
tests/ubpf_verifier.rs Outdated Show resolved Hide resolved
Cargo.toml Show resolved Hide resolved
@qmonnet
Copy link
Owner

qmonnet commented May 30, 2024

Please clean up the commit log from your second commit, you have multiple sign-off tags (likely because you squashed commits together and kept the descriptions of all commits).

@SzymonKubica SzymonKubica force-pushed the rbpf-no-std-port branch 5 times, most recently from dd333b4 to da48944 Compare May 31, 2024 09:28
@qmonnet
Copy link
Owner

qmonnet commented Jun 5, 2024

Thanks a lot! Sorry I didn't have the time to review last night, I'll do it soon.

@SzymonKubica
Copy link
Contributor Author

Thanks a lot! Sorry I didn't have the time to review last night, I'll do it soon.

No worries at all, take your time. No need to rush, I realise it is quite a big change in terms of files affected (not that big functionality-wise). Thank you for your time and guidance with the PR.

Copy link
Owner

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your work!

I've got some comments, and I think I'm a bit confused as to when the assembler is supported without std. I probably need to take some time to play with your code and check why rbpf complains if I try to enable the tests; in the meantime, I commented at the locations where it is not clear to me.

The required changes are mostly minor things, we're getting closer!

Regarding the formatting of your patches, I'd recommend the following:

  • Squash the fixes from the last commit into the relevant earlier commits
  • Rework your commit descriptions, in particular for the second commit. What's interesting in this description is not so much to describe the changes (and not the history of the different changes and fixes incorporated during the review); instead, it should provide the motivation for the change, and explain what the commit addresses, what difficulties arose and how the commit works around them, etc. Everything related to the context of the change.

examples/rbpf_plugin.rs Outdated Show resolved Hide resolved
examples/rbpf_plugin.rs Outdated Show resolved Hide resolved
Cargo.toml Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/no_std_error.rs Show resolved Hide resolved
tests/assembler.rs Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
@SzymonKubica SzymonKubica force-pushed the rbpf-no-std-port branch 4 times, most recently from 205f395 to 9b6eb04 Compare June 9, 2024 06:19
@SzymonKubica
Copy link
Contributor Author

SzymonKubica commented Jun 9, 2024

Thanks a lot for your work!

I've got some comments, and I think I'm a bit confused as to when the assembler is supported without std. I probably need to take some time to play with your code and check why rbpf complains if I try to enable the tests; in the meantime, I commented at the locations where it is not clear to me.

The required changes are mostly minor things, we're getting closer!

Regarding the formatting of your patches, I'd recommend the following:

  • Squash the fixes from the last commit into the relevant earlier commits
  • Rework your commit descriptions, in particular for the second commit. What's interesting in this description is not so much to describe the changes (and not the history of the different changes and fixes incorporated during the review); instead, it should provide the motivation for the change, and explain what the commit addresses, what difficulties arose and how the commit works around them, etc. Everything related to the context of the change.

Ok thank you for the review and your suggestions. I will rework the commit messages and squash the subsequent fixes.
I propose the following logical changes:

  • add the std feature and updating the windows cfg flags
  • add lib module and conditional imports (the Configure combine crate and asm_parser to work under no_std. commit will be squashed into this one)
  • update readme with feature description
  • add workflow job

The last commit fixing pipeline warnings will be split and squashed into the above commits where the changes belong appropriately

@SzymonKubica SzymonKubica force-pushed the rbpf-no-std-port branch 2 times, most recently from 6e1fe11 to f357b45 Compare June 9, 2024 06:50
This was done to prepare the foundation for adding no_std compatibility.
The std feature can be disabled by the users of this library which will
compile rbpf in a way that doesn't require the standard library.

Signed-off-by: SzymonKubica <[email protected]>
@SzymonKubica SzymonKubica force-pushed the rbpf-no-std-port branch 3 times, most recently from 543b0e0 to 1897181 Compare June 9, 2024 07:11
This commit introduces a lib module in `lib.rs` responsible for
reexporting all imports from the standard library used by the crate.

It also adjusts all dependencies of rbpf so that they depend on the
standard library only if the `std` feature is used.

This was done to make rbpf compatible with `no_std`. This compatibility
is needed to run rbpf on embedded devices where the standard library
isn't available.

Main difficulties:

- println! macro isn't available on `no_std`. A workaround was
  introduced to use the `log` crate and call logging macros such as
  `warn!` or `info!` in places where println is used when we have access
  to the standard library. Users of the crate can then define their
  custom logger elsewhere in the code and thanks to the log crate it
  will be called by rbpf and print messages using the provided
  implementation.

- the VM interpreter depends on std::io::Error and std::io::ErrorKind.
  Those structs aren't available on no_std. We introduce a simple
  implementation of these two which exposes the same API and use it
  under `no_std` as a drop-in replacement. An important note is that the
  original ErrorKind enum has more than 40 available entries whereas our
  implemnentation only provides the one that is currently used
  throughout the rbpf code (ErrorKind:Other). Should a dependency on new
  variants be added in the future, the replacement ErrorKind (found in
  no_std_error.rs) would have to be updated to maintain compatibility.
  The reason we don't include all variants right now is that not all of
  those error kinds are meaningful in `no_std` context.

- the assembler and asm_parser depend on `combine` crate and use the
  EasyParser from there by default. The problem is that the EasyParser
  is not available in no_std. We get around this issue by using the
  regular Parser when std is not available. Note that the difference
  between the two parser implementations is the quality of error
  messages. The EasyParser is able to extract much more information and
  report it in an error. The regular one simply returns "invalid parse".
  Because of this, two tests of the assembler needed to be updated to
  conditionally check for different error messages when running
  with/without std.

Signed-off-by: SzymonKubica <[email protected]>
@SzymonKubica
Copy link
Contributor Author

Ok I think everything has been cleaned up now. Thank you very much for all of your feedback and time spent reviewing my changes. Now all imports / dependency changes are in the second commit. I managed to find some additional assembler tests that could be reenabled (tests/misc.rs). I think it helps a lot to see all changes in a single logical commit

@SzymonKubica
Copy link
Contributor Author

@qmonnet could you please approve the CI workflow run? Don't worry about reviewing the changes today, no need to rush. I just wanted to make sure that rearranging the changes didn't produce any new pipeline warnings. Enjoy your weekend and feel free to look into the PR when you are free.

@SzymonKubica
Copy link
Contributor Author

Hi @qmonnet, thank you for triggering the workload, seems like the pipeline passes successfully and no new warnings emitted.
I have addressed all of your comments and the commit descriptions have been revised.
Please let me know if anything else needs to be changed.

@SzymonKubica SzymonKubica requested a review from qmonnet June 17, 2024 15:29
Copy link
Owner

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies once again for the delay. I do realise this is not a great way to keep you motivated for this PR, and I'm sorry for the poor contribution experience in terms of delays. Thanks a lot for bearing with me.

And thanks for this work, this is in a very nice shape now! I think there's just a small fix to do in the README, but everything else looks good as far as I can tell. Well done, and nice work on the commit description for your second commit! ❤️

README.md Outdated Show resolved Hide resolved
Added a new entry in the ToC for 'build features'.
A description of the `no_std` feature was added there.

Signed-off-by: SzymonKubica <[email protected]>
@SzymonKubica
Copy link
Contributor Author

Apologies once again for the delay. I do realise this is not a great way to keep you motivated for this PR, and I'm sorry for the poor contribution experience in terms of delays. Thanks a lot for bearing with me.

And thanks for this work, this is in a very nice shape now! I think there's just a small fix to do in the README, but everything else looks good as far as I can tell. Well done, and nice work on the commit description for your second commit! ❤️

Ok @qmonnet, thank you for pointing out the issue it the README, it is fixed now. Thank you for picking up on it.

Copy link
Owner

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks a lot for this work!

@qmonnet qmonnet merged commit fe7021b into qmonnet:main Jun 17, 2024
8 checks passed
@SzymonKubica
Copy link
Contributor Author

Perfect, thank you for approving the PR 🚀
I really appreciate your help along the way. It was a great learning experience and I think it is going to be very useful for my future open-source contributions. Thanks for your time and being open to this contribution.

@qmonnet
Copy link
Owner

qmonnet commented Jul 22, 2024

Congrats on getting your µBPF paper into the ACM workshop! 🎉

@SzymonKubica
Copy link
Contributor Author

Congrats on getting your µBPF paper into the ACM workshop! 🎉

Thank you!

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 this pull request may close these issues.

2 participants