Return errors in Rust function in C program

I have trouble with propertly running Rust function
and call this from C. I would like to using error returning code. Panic, Error etc.

----- lib.rs ----
#![crate_type = "dylib"]
#![crate_type = "staticlib"]

#[no_mangle]
pub extern "C" fn z_c()
{
let result = std::panic::catch_unwind(|| {
println!("Po prostu wypisz napis!"); });
if result.is_err()
{ eprintln!("Łojej: rust panikuje"); }
}

--- main.c ----
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

extern void z_c();

int main(void)
{
z_c();
return 0;
}

--- Makefile -----
clang -c main.c
rustc --crate-type staticlib --crate-name ex lib.rs
clang -L. -o main main.o -lex -lpthread -lgcc_s -ldl


the trouble is with
$valgrind -v --leak-check=full --show-leak-kinds=all --track-origins=yes --tool=memcheck ./main

I have memory leak

---- Fabian / return rust function from C · GitLab

Please format code using backticks,

```
like this
```

It's very hard to read otherwise.

I don't think this has any leaks. The code looks correct!

I also ran valgrind, and it was OK:

$ valgrind ./main 
==12031== Memcheck, a memory error detector
==12031== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12031== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12031== Command: ./main
==12031== 
Po prostu wypisz napis!
Wynik 3
==12031== 
==12031== HEAP SUMMARY:
==12031==     in use at exit: 1,200 bytes in 7 blocks
==12031==   total heap usage: 9 allocs, 2 frees, 2,256 bytes allocated
==12031== 
==12031== LEAK SUMMARY:
==12031==    definitely lost: 0 bytes in 0 blocks
==12031==    indirectly lost: 0 bytes in 0 blocks
==12031==      possibly lost: 0 bytes in 0 blocks
==12031==    still reachable: 1,200 bytes in 7 blocks
==12031==         suppressed: 0 bytes in 0 blocks
==12031== Rerun with --leak-check=full to see details of leaked memory
==12031== 
==12031== For lists of detected and suppressed errors, rerun with: -s
==12031== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

There is nothing lost, so there are no leaks. The bytes "still reachable" are global data rust always stores; they are not leaks.

@Cerber-Ursi I put in my prev post gitlab repo for more cleary message. Please read this.
@daboross
In my opinion THIS IS memory leak and dont free memory. Please make experiment. Delete fn z_c() and run code.

If You leave only suma() function You get clear memory usage log.

$make mem

[...]

--3513-- REDIR: 0x54fa2e0 (libc.so.6:malloc) redirected to 0x4c2edc9 (malloc)
--3513-- REDIR: 0x5517e00 (libc.so.6:__mempcpy_sse2_unaligned) redirected to 0x4c36b10 (mempcpy)
Wynik 3
--3513-- REDIR: 0x54fa930 (libc.so.6:free) redirected to 0x4c2ffca (free)
==3513==
==3513==
==3513== HEAP SUMMARY:
==3513== in use at exit: 0 bytes in 0 blocks
==3513== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==3513==
==3513== All heap blocks were freed -- no leaks are possible
==3513==
==3513== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==3513== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

It's true that running rust code allocates memory that it does not deallocate. And as rust is run from C, and doesn't control main, it will never deallocate it. This is what valgrind is complaining about.

But this isn't a leak because the memory is still accessible. Rust line-buffers stdout, and to do that buffering, it allocates a static vec. But it doesn't do this every time you call into Rust. It will do this once, and then every other println! call will just reuse the existing buffer. I say it isn't a leak because of this reuse: the buffer is still accessible, and Rust code will use it again and again when you redo the call.

A memory leak is some memory which you never free and you never use again. It's bad because it's useless memory. This global memory is never freed, but it is used. It's not useless nor a leak because it'll be reused next println! call, and will keep being reused.


If you're concerned about the memory usage, know that it is effectively limited. It's a small amount of memory allocated by any and all rust code which prints to console- allocated once, and never again (unless the buffer size needs to be increased).

I hope that clarifies things! Let me know if you have questions.

1 Like

Basically try to run 1000 times the z_c() function and see if the "memory leak " gets bigger. If it does, then it is indeed a memory leak which needs to be fixed. If, however, the number of "leaked" bytes is (upper) bounded, then, instead of seeing it as a memory leak, see it as lazily allocated global / static data.

  • For instance, there isn't really a difference, conceptually, between:

    // 1000 bytes of static memory in the `.bss` (+ a pointer)
    static FOO: &'static [u8] = &[0; 1000];
    

    and

    // 1000 bytes of static memory in the heap (+ a pointer)
    ::lazy_static::lazy_static! {
        static ref FOO: &'static [u8] = Box::leak(vec![0; 1000].into_boxed_slice());
    }
    

Although I'd personally love if there was a parallel "leaking" allocator on top of the classic one to make this intent clearer, which would incidentally reduce the heap fragmentation of this pattern.

1 Like

yes & no :wink:

This is error for me. And it doesn't matter what we call it. Tell me how to remove it.

Second question. How to generate a static file in C. That is, how to compile my library best into an object .o
--emit=obj unfortunately doesn't work for me.
(I can create lib*.o but it have core::panicking::panic ::fmt and std::io::stdio::_print how remove it, how writing myself?)

Rust have errors function. Alvays exist. How writing my own function, remowe pthread (I no need this) etc.

How creating in Rust normal static lib/obj for compiling gcc --static ?
Without any errors in valgrind.

If you don't use println!/eprintn! on Rust side, does the error go away?

2 Likes

Yes, You are right, but how removing dependecies -lpthread -lgcc_s -ldl
how creating -static ? how using #[no_std] and with this and panic function

https://doc.rust-lang.org/1.7.0/book/no-stdlib.html

@Dushistov Your link no show how generating .object or --static
Your code dont work

error[E0554]: #![feature] may not be used on the stable release channel
--> src/lib.rs:4:1
|
4 | #![feature(lang_items)]
| ^^^^^^^^^^^^^^^^^^^^^^^

error[E0554]: #![feature] may not be used on the stable release channel
--> src/lib.rs:5:1
|
5 | #![feature(start)]
| ^^^^^^^^^^^^^^^^^^

error[E0522]: definition of an unknown language item: panic_fmt
--> src/lib.rs:18:1
|
18 | #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
| ^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item panic_fmt

error: #[panic_handler] function required, but not found

If You can please show me a code. You can modyfy Fabian / return rust function from C · GitLab if You would like

I need -static program in C, in any way. I need only some function write in rust.

No, I still have core::panicking::panic and others.

It likely won't be possible to remove these errors entirely. If you set panic to abort (see this page) then you might be able to remove some other global non-deallocated allocations, but I would not guarantee that removes all of them.

In general, these do not have any impact in program runtime nor safety, and are not seen as errors.

I can help with the other stuff, though!

To remove pthread, gcc, etc., you can use the MUSL linux target. Then rust will depend only on that fully-static libc, and shouldn't have these dependencies. See MUSL support for fully static binaries. I believe the adjustments are the same when making fully static libraries.

Unfortunately, this won't work for building dynamic libraries, as dynamic libraries don't exist on musl. If you do want a dynamic library, as opposed to a static one, then I don't know how to remove the libdl,libpthread and libgcc_s dependencies. It seems the standard is to leave them in, and this matches the C libraries which also depend on them.

My first guess would be to add

[lib]
crate-type = ["cdylib"]

to the Cargo.toml file, and then use cargo build to produce the library.

I haven't done the full release process, though. Many crates I know which have C interfaces, though, use cargo-c. There's a blog post about it - Building crates so they look like C(ABI) Libraries.

Maybe existing crates can help? These two crates both have C interfaces, and use two different approaches to make them:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.