ttf-parser icon indicating copy to clipboard operation
ttf-parser copied to clipboard

make c wrapper no_std

Open ebraminio opened this issue 4 years ago • 9 comments

I am getting

/usr/bin/ld: subprojects/ttf-parser/libttf_parser_capi.a(std-205127404fcba336.std.595pxchd-cgu.0.rcgu.o): undefined reference to symbol 'pthread_getattr_np@@GLIBC_2.2.5'

and

/usr/bin/ld: subprojects/ttf-parser/libttf_parser_capi.a(std-205127404fcba336.std.595pxchd-cgu.0.rcgu.o): in function 'std::sys::unix::weak::fetch': /rustc/49cae55760da0a43428eba73abcb659bb70cf2e4//src/libstd/sys/unix/weak.rs:61: undefined reference to 'dlsym'

if I don't pass thread_dep, cpp.find_library('dl') (which currently I am) and guess those are coming from rust's std, can c-api also become no_std so I can avoid passing https://github.com/harfbuzz/harfbuzz/pull/2510/files#diff-9356922ca3ded90602b144c3b5851dd2R8 pthread and libdl to it? Or is it something you are interested in at all so I can also have a look?

Thanks!

ebraminio avatar Jun 28 '20 13:06 ebraminio

can c-api also become no_std

Not sure if it's possible. Will take a look.

You should use dynamic linking. Static linking is kinda broken in Rust. At least for me. That's why c-api doesn't build static at all. I guess it should be fixed in meson too.

RazrFalcon avatar Jun 28 '20 13:06 RazrFalcon

Not sure if it's possible. Will take a look.

Thank you so much. I will also.

You should use dynamic linking. Static linking is kinda broken in Rust. At least for me. That's why c-api doesn't build static at all. I guess it should be fixed in meson too.

Apparently I were able to somehow maybe at some limited cases, static linking is very convenient, am imagining some C library embeds a rust library and itself can be static linked to another library easily.

ebraminio avatar Jun 28 '20 13:06 ebraminio

No, static linking via C API in rust is very troublesome. You should always use dynamic one.

RazrFalcon avatar Jun 28 '20 13:06 RazrFalcon

Yes, no_std no possible due to std::panic::catch_unwind. So meson should be changed to cdylib.

Afaik, the problem with static linking is that rust links its own std with specific system libraries, which can cause conflicts during linking.

Personally, I always use dynamic linking for C API.

RazrFalcon avatar Jun 28 '20 14:06 RazrFalcon

Rust claims able to run on bare metal hardware but doesn't support static linking a c-api, that's very unfortunate :/ I wished it had some solution, maybe we should bring this to the rust development community and maybe I'm getting it also as a dylib also and I'm unaware of it?

ebraminio avatar Jun 28 '20 14:06 ebraminio

It does support static linking. The problem is that you have to provide required libraries manually. pthread in your case.

dylib is a Rust dynamic library. You need cdylib, which is a C dynamic library.

RazrFalcon avatar Jun 28 '20 14:06 RazrFalcon

Oh I see, so its use is on catch_unwind, This method invokes a lot of parsing, so let's catch any panics just in case., is there someway to avoid panics from the beginning somehow? And is there anything worth to investigate? https://github.com/rust-lang/rfcs/issues/2810 is related I guess.

ebraminio avatar Jun 28 '20 15:06 ebraminio

avoid panics from the beginning

It's one of the goals. But it will take a lot of time. Weeks.

RazrFalcon avatar Jun 28 '20 15:06 RazrFalcon

It's one of the goals. But it will take a lot of time. Weeks.

Thank you so much, so there is a hope which is great! I will also try so to improve my Rust experience. Thanks

ebraminio avatar Jun 28 '20 15:06 ebraminio

Yes, no_std no possible due to std::panic::catch_unwind. So meson should be changed to cdylib.

As an alternative to catch_unwind, you can use a drop-then-abort guard to prevent panics from unwinding into other code. This is no_std.

notgull avatar Mar 08 '23 17:03 notgull

@notgull can you elaborate?

RazrFalcon avatar Mar 13 '23 06:03 RazrFalcon

I think what he means is something like:

struct DropAbortGuard;

impl DropAbortGuard {
    fn new() -> Self {
        Self
    }
    
    fn end(self) {
        core::mem::forget(self); // Ensures drop doesn't run for self
    }
}

impl Drop for DropAbortGuard {
    fn drop(&mut self) {
        panic!(); // Panic while panicing becomes an abort
    }
}

let guard = DropAbortGuard::new();

some_code_that_might_panic();

guard.end();

It's not pretty, but it seems to work.

staticintlucas avatar Mar 22 '23 18:03 staticintlucas

Yeah, you can find an example of this pattern here. Since you don't expect the code to panic anyways it should be a good fit

notgull avatar Mar 22 '23 18:03 notgull

Hmm... it simply replaces panic with abort, which is not what I want. The whole point behind using catch_unwind is that I cannot statically prove that ttf-parser will not panic. Sadly, this is the current Rust limitation. We need language-level #[no-panic] attribute first.

RazrFalcon avatar Mar 22 '23 18:03 RazrFalcon

Hmm... it simply replaces panic with abort, which is not what I want.

Are you sure? In your README, you say that any panic is a critical bug. Therefore, I would think that replacing the panic with an snort would be the best option, since unwinding into the C stack frames is unsound behavior.

In fact, I'd say that wrapping every C API function in an abort guard like this is required for soundness. While, in theory, many of the function you use don't panic, there may be devils hidden in those details, and a core dump is preferable to corrupting the stack.

notgull avatar Mar 22 '23 20:03 notgull

I threat every function as panicking in Rust. Which is true until we get #[no-panic].

In your README, you say that any panic is a critical bug.

It doesn't mean that there are no panics in ttf-parser. ttf-parser is like 15 KLOC. I cannot guarantee anything at this scale.

RazrFalcon avatar Mar 23 '23 10:03 RazrFalcon