1

My goal is to create a C API where the user is required to provide a block of memory with a certain size. This size happens to be the size of a struct in my rust library, and so I'd like to extract the size of the rust struct and place it in a header file as a C macro.

The problem is that I'm cross-compiling my library, so I can't run a program on my computer that prints core::mem::size_of::<MyStruct>(). But I can store that value in a const variable in my library.

Is there any way to extract the value of this const variable representing the size of my struct at compile time such that I can then paste it into a C header file?

awelkie
  • 2,422
  • 1
  • 22
  • 32
  • 2
    Do you just want to see your value with your own eyes? E.g. "I wonder how big my struct would be on ARM"? Or do you want to use that value in code? If yes, that would beg the question in the last comment: where? Inside a proc macro? – Lukas Kalbertodt Feb 01 '19 at 11:30
  • See [How do I evaluate expressions in Rust's macro system?](https://stackoverflow.com/questions/49576878/how-do-i-evaluate-expressions-in-rusts-macro-system) – Peter Hall Feb 01 '19 at 12:11
  • Would https://stackoverflow.com/questions/30330519/compile-time-generic-type-size-check solve this? – Matthieu M. Feb 01 '19 at 12:16
  • 4
    For a quick check I just do: `() as [(); MYSIZE];` that yields an error with the message `non-primitive cast: () as [(); X]`. – rodrigo Feb 01 '19 at 13:08
  • My goal is to create an API where the user is required to provide a block of memory with a certain size, and I'd like to automatically create a header file with that size specified in a macro. – awelkie Feb 01 '19 at 15:02
  • 1
    In that case, I would say this is a duplicate of the question I linked. There is no way to do this and the answer to that question explains that. – Peter Hall Feb 01 '19 at 15:05
  • 1
    Possible duplicate of [How do I evaluate expressions in Rust's macro system?](https://stackoverflow.com/questions/49576878/how-do-i-evaluate-expressions-in-rusts-macro-system) – Peter Hall Feb 01 '19 at 15:05
  • I don't see how this question is a duplicate of the one you've linked. "How can you be sure that the size will be the same on the target architecture?" - I'm not depending on the size of a struct being the same between two targets. What I'm saying is that when I compile for a specific target, the compiler knows the size of the struct, and I'm wondering how I can extract that information from the compiler. – awelkie Feb 01 '19 at 15:21
  • *I'd like to automatically create a header file* — how do you expect to automatically generate this? Are you going to scrape the stdout of `cargo build` and then run scripts to generate a header file? – Shepmaster Feb 01 '19 at 15:32
  • I suppose that depends on the answer to this question. Yes, scraping stdout is a possibility. Even just manually copy-pasting whenever I cut a release is fine, I'm just hoping for something better than running my library in an emulator or on target hardware. – awelkie Feb 01 '19 at 15:35
  • 1
    @awelkie: Please edit your question to include the usecase (creating a macro expanding to the size in a C header file when cross-compiling). Do you plan to have this header also contain the struct itself? Or not? Would an upper bound suffice? – Matthieu M. Feb 01 '19 at 16:12
  • Done. I hope it's clearer now. – awelkie Feb 01 '19 at 17:30

2 Answers2

2

Definitely do not do this:

  1. In a new crate, import the type in question, and create a function that returns its size:

    #[no_mangle]
    pub fn size_of_mystruct() -> usize {
        std::mem::size_of::<MyStruct>()
    }
    
  2. Get the LLVM-IR output:

    CARGO_INCREMENTAL=0 cargo rustc -- --emit=llvm-ir -o ir
    

    Make sure to add the --target option too. This will create a few files, one which should have extension .ll. The CARGO_INCREMENTAL=0 is important - without it, it will create lots of .ll files, and who knows which is the right one! Open the file and search for size_of_mystruct. You'll find something like this:

    ; Function Attrs: uwtable
    define i64 @size_of_mystruct() unnamed_addr #0 !dbg !142 {
    start:
    ; call core::mem::size_of
      %0 = call i64 @_ZN4core3mem7size_of17hc5e3caf4d8826b98E(), !dbg !144
      br label %bb1, !dbg !144
    
  3. Search for the internal function being called here. (In this case _ZN4core3mem7size_of17hc5e3caf4d8826b98E). It will look like this:

    ; core::mem::size_of
    ; Function Attrs: inlinehint uwtable
    define internal i64 @_ZN4core3mem7size_of17hc5e3caf4d8826b98E() unnamed_addr #1 !dbg !67 {
    start:
      %tmp_ret = alloca i64, align 8
      store i64 40, i64* %tmp_ret, align 8, !dbg !87
      %0 = load i64, i64* %tmp_ret, align 8, !dbg !87
      br label %bb1, !dbg !87
    
  4. This is the important bit: store i64 40. The struct is 40 bytes!

  5. Automate the process!

  6. Wait until the whole process mysteriously breaks.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
0

I think it would be wiser to approach this from the opposite direction: define a C struct in a .h file that you will distribute with your library for consumers to use, and then declare the struct #[repr(C)] in Rust (you could even have bindgen generate the declaration for you automatically from the .h as part of your build).

eggyal
  • 122,705
  • 18
  • 212
  • 237