Bug 218607
Summary: | psx_syscall6() doesn't work on C++ | ||
---|---|---|---|
Product: | Tools | Reporter: | vini.ipsmaker |
Component: | libcap | Assignee: | Tools/Libcap default virtual assignee (tools_libcap) |
Status: | NEW --- | ||
Severity: | enhancement | CC: | morgan |
Priority: | P3 | ||
Hardware: | All | ||
OS: | Linux | ||
Kernel Version: | Subsystem: | ||
Regression: | No | Bisected commit-id: |
Description
vini.ipsmaker
2024-03-16 03:09:53 UTC
libpsx is a wrapper for thread creation and management that uses -lpthread. On the face of it your program makes no reference to `pthread_create` in the output of `objdump -x a.out`. The `libpsx` code expects the linking to wrap these function calls with some accounting code that keeps track of POSIX threads. ``` $ pkg-config --libs libpsx -lpsx -lpthread -Wl,-wrap,pthread_create ``` I'm not familiar with how c++ `<thread>` is implemented. Does it even use `pthread_create` on Linux? Running `ltrace a.out` on your program does not suggest that the `$(pkg-config --libs libpsx)` linking options are attaching themselves to anything. You should see something like: `__wrap_pthread_create` in the output at thread creation time. > Running `ltrace a.out` on your program does not suggest that the > `$(pkg-config --libs libpsx)` linking options are attaching themselves to > anything. That's the function it's calling here according to ltrace: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) Should we also have a libpsx++ then? I suppose it'd wrap the above function and use the same sighandling mechanism from libpsx? > I also tried to convert the C++ program to a C program using pthread_create() > manually, and it didn't work (one of the threads will just die indicating > that the sighandler for libpsx isn't installed?). I really don't understand > how one is supposed to use libpsx. I finally found out what was wrong with my C version of the same program. You can ignore this part of my initial report. It seems the only remaining problem now is how one is supposed to use libpsx in C++ programs. Should we have a libpsx++ library that C++ programs link against? Possibly, but there is a lot of name mangling going on, so I'm not sure. Ideally, if it can be made to work, we would provide both wrapper calls in the same library and not need a separate library build. > a lot of name mangling going on
libc++ (LLVM) might be using a different function to wrap as well, so maybe there's yet another function to wrap later. Unfortunately I don't have a handy libc++-based env to test right now. However having just libstdc++ should be good enough for a start anyway as that's the most common ABI one founds in Linux systems by a large margin.
This isn't very promising: https://stackoverflow.com/a/8405029/14760867 I wonder if a potential workaround more natural to C++ might be to use inheritance to provide, say, a `psx::thread` object, and substitute wrapper methods for creation? It is not clear to me how useful this would be since the wrapping would only apply to code that used `psx::thread`, but it might be workable for some projects. > This isn't very promising [...] Why isn't <https://stackoverflow.com/a/8405029/14760867> promising? Doesn't wrapping the mangled name look good enough? I think that should be the way to go. The real problem should actually come later, adding a C++ compilation unit so you can access std::thread::native_handle() to do the bookkeeping for the new thread. If you do decide to add a C++ compilation unit to libpsx, you complicate the build for libpsx by adding a new dependency (a C++ compiler). If you decide to add the C++ compilation unit in a different library, then you complicate orchestration between the new library and libpsx (libcap using libpsx should work on C++ projects that link against the new library as well... and C libraries in the C++ project calling pthread_create() directly should also work here). > I wonder if a potential workaround more natural to C++ might be to use > inheritance to provide, say, a `psx::thread` object, and substitute wrapper > methods for creation? It is not clear to me how useful this would be since > the wrapping would only apply to code that used `psx::thread`, but it might > be workable for some projects. This wouldn't work for my C++ project. I don't control 3rd party libraries that might create threads. And I do need to use std::thread so functions such as std::notify_all_at_thread_exit() work correctly. std::*_at_thread_exit() functions trigger some hardcoded libstdc++ functions that are guaranteed to execute only after thread-local variables are destroyed. How many mangled names would we have to wrap though? Is the mangling even consistent for single compiler, libc++ variant? > How many mangled names would we have to wrap though? Is the mangling even
> consistent for single compiler, libc++ variant?
C++ mangling is very stable. The developers for libstdc++ and libc++ also take ABI stability very seriously.
It'd require mangling for two functions in total:
* libstdc++ thread constructor
* libc++ thread constructor
However I suggest to start with just libstdc++ to gather experience. It can expand to support libc++ in later releases.
So I took a look at this (try.cc): //// #include <iostream> #include <thread> #include <sys/psx_syscall.h> #include <unistd.h> #include <sys/prctl.h> #include <sys/syscall.h> extern "C" void __wrap__ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE() { std::cout << "oh no\n"; exit(1); } int main() { std::thread t{[]() { int x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0); sleep(2); int y = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0); std::cout << "thread was: " << x << " is: " << y << "\n"; }}; int x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0); sleep(1); psx_syscall6(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0); int y = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0); std::cout << "main was: " << x << " is: " << y << "\n"; t.join(); } //// Compiled with: $ g++ try.cc $(pkg-config --libs libpsx) -Wl,-wrap,_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE $ ./a.out oh no Which does imply we can intercept the thread constructor. I just am not yet sure how to chain to the real version. |