I am trying to interpose calls to pthread_cond_broadcast using LD_PRELOAD mechanism. My interposed pthread_cond_broadcast function just calls the original pthread_cond_broadcast. However, for a very simple pthread code where both pthread_cond_wait and pthread_cond_broadcast get invoked, I either end up with a segfault in glibc (for glibc 2.11.1) or the program hangs (for glibc 2.15). Any clues on that is going on?
The interposition code (that gets compiled as a shared library):
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <dlfcn.h>
static int (*orig_pthread_cond_broadcast)(pthread_cond_t *cond) = NULL;
__attribute__((constructor))
static void start() {
orig_pthread_cond_broadcast =
(int (*)()) dlsym(RTLD_NEXT, "pthread_cond_broadcast");
if (orig_pthread_cond_broadcast == NULL) {
printf("pthread_cond_broadcast not found!!!\n");
exit(1);
}
}
__attribute__((__visibility__("default")))
int pthread_cond_broadcast(pthread_cond_t *cond) {
return orig_pthread_cond_broadcast(cond);
}
The simple pthread program:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t cond_mutex;
pthread_cond_t cond_var;
int condition;
void *thread0_work(void *arg) {
pthread_mutex_lock(&cond_mutex);
printf("Signal\n");
condition = 1;
pthread_cond_broadcast(&cond_var);
pthread_mutex_unlock(&cond_mutex);
return NULL;
}
void *thread1_work(void *arg) {
pthread_mutex_lock(&cond_mutex);
while (condition == 0) {
printf("Wait\n");
pthread_cond_wait(&cond_var, &cond_mutex);
printf("Done waiting\n");
}
pthread_mutex_unlock(&cond_mutex);
return NULL;
}
int main() {
pthread_t thread1;
pthread_mutex_init(&cond_mutex, NULL);
pthread_cond_init(&cond_var, NULL);
pthread_create(&thread1, NULL, thread1_work, NULL);
// Slowdown this thread, so the thread 1 does pthread_cond_wait.
usleep(1000);
thread0_work(NULL);
pthread_join(thread1, NULL);
return 0;
}
EDIT:
For glibc 2.11.1, gdb bt gives:
(gdb) set environment LD_PRELOAD=./libintercept.so
(gdb) run
Starting program: /home/seguljac/intercept/main
[Thread debugging using libthread_db enabled]
[New Thread 0x7ffff7436700 (LWP 19165)]
Wait
Signal
Before pthread_cond_broadcast
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff79ca0e7 in pthread_cond_broadcast##GLIBC_2.3.2 () from /lib/libpthread.so.0
(gdb) bt
#0 0x00007ffff79ca0e7 in pthread_cond_broadcast##GLIBC_2.3.2 () from /lib/libpthread.so.0
#1 0x00007ffff7bdb769 in pthread_cond_broadcast () from ./libintercept.so
#2 0x00000000004008e8 in thread0_work ()
#3 0x00000000004009a4 in main ()
EDIT 2:
(Solved)
As suggested by R.. (thanks!), the issue is that on my platform pthread_cond_broadcast is a versioned symbol, and dlsym gives the wrong version. This blog explains this situation in great detail: http://blog.fesnel.com/blog/2009/08/25/preloading-with-multiple-symbol-versions/
The call through your function seems to end up in a different version of the function:
With LD_PRELOAD: __pthread_cond_broadcast_2_0 (cond=0x804a060) at old_pthread_cond_broadcast.c:37
Without LD_PRELOAD: pthread_cond_broadcast##GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/pthread_cond_broadcast.S:39
So your situation is similar to this question, i.e. you are getting incompatible versions of pthread functions: symbol versioning and dlsym
This page gives one way to solve the problem, though a bit complex: http://blog.fesnel.com/blog/2009/08/25/preloading-with-multiple-symbol-versions/
Related
Looking at the output of readelf -s /lib/x86_64-linux-gnu/libc.so.6 on my Ubuntu 22.04 box, I see (what looks to be) the entire pthread API contained in the .text section.
As a sanity check, I successfully compiled and ran
#include <stdio.h>
#include <string.h>
#include <pthread.h>
static void *
func(void *args) {
return args;
}
int main() {
int ret;
pthread_t thread;
ret = pthread_create(&thread, NULL, func, NULL);
if ( ret != 0 ) {
fprintf(stderr, "pthread_create: %s\n", strerror(ret));
return ret;
}
pthread_join(thread, NULL);
return 0;
}
without using -pthread.
Given all of this, is there any purpose to libpthread on my computer other than providing support for older applications which expect it to be there?
Is libpthread needed if the pthread API is in libc
No.
is there any purpose to libpthread on my computer other than providing support for older applications which expect it to be there?
No.
See https://developers.redhat.com/articles/2021/12/17/why-glibc-234-removed-libpthread . Since then libpthread is an empty library.
Can please some one explain the output of the code below!
I was working with threads in C language, and caught my eye that sometimes the output of the programs is not totally one of my prediction.
So I tried to write this code to understand more..
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void * threadfun(void *arg){
printf("Hello from thread id %ld\n",*(pthread_t*)arg);
return NULL;
}
int main (int argc, char *argv[]){
pthread_t tid;
void * (*fp)(void*) = &threadfun;
printf("The Main program. pid = %d.\n",getpid());
pthread_create(&tid, NULL,fp,(void*)&tid);
printf("%ld was the thread id.\n",tid);
//pthread_join(tid, NULL);
printf("ENd. \n");
return 0;
}
I comment out the join function on purpose.
The output of this code was sometimes something like this:
gcc (Debian 11.2.0-10) 11.2.0
└─$ gcc s1_LD_thread01.c -o out_s1_thread01 -lpthread
└─$ ./out_s1_thread01
The Main program. pid = 8683.
140305239197248 was the thread id.
ENd.
Hello from thread id 140305239197248
Hello from thread id 140305239197248
I am wondering why the created thread outputting two strings.!
I am currently facing issue with the glibc v2.22 where I am not able to get the proper unwind information.
When there is SIGABRT application, it is calling abort function from glibc. It should be using unwind information which is enabled in the build. However, it is scanning the stack (as indicated by the red line below the address in the screenshot) and providing the misleading information as shown in the screenshot attached (using sentry for analyzing the dump).
Here, do_crash is called which does assert(0) which then aborts the main application. While analyzing the dump, the do_crash function calls the _fini which is never in the main application's stack.
I have enabled unwind for the glibc by using CFLAGS += "-funwind-tables". I also tried with the flags such as -rdynamic and -fno-omit-frame-pointer but it was also of no use.
Am I missing something here? How can I get the complete backtrace of the signals, particularly SIGABRT?
Thanks in advance
When there is SIGABRT application, it is calling abort function from glibc
That is not true, this is not happening, unless you explicitly registered it.
I have enabled unwind for the glibc by using CFLAGS += "-funwind-tables"
It tells the compiler to add the information, it doesn't "enable unwind". What exactly happens when compiling with -funwind-tables?
Here, do_crash is called which does assert(0) which then aborts the main application.
This is not related to receiving SIGABRT signal.
Am I missing something here?
I believe you are making wrong assumptions - that something is called on SIGABRT, that SIGABRT is sent on assert, that abort() is called on SIGABRT. Nothing is called on SIGABRT and the program is terminated when receiving SIGABRT by default (see man 7 signal), assert just terminates the program and doesn't raise SIGABRT, and abort() raises the SIGABRT signal, not receives it.
How can I get the complete backtrace of the signals, particularly SIGABRT?
Register a handler that will do that. See How to automatically generate a stacktrace when my program crashes .
#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void handler(int sig) {
void *array[10];
size_t size;
size = backtrace(array, 10);
backtrace_symbols_fd(array, size, STDERR_FILENO);
_Exit(1);
}
int main(int argc, char **argv) {
signal(SIGABRT, handler); // install our handler
raise(SIGABRT);
}
If you want to print stacktrace on assert() that's completely different and you would overwrite the glibc handler for assert to do that:
#include <assert.h>
#include <execinfo.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
void print_trace(void) {
void *array[10];
size_t size;
size = backtrace(array, 10);
backtrace_symbols_fd(array, size, STDERR_FILENO);
}
// based on https://code.woboq.org/userspace/glibc/assert/assert.c.html
void __assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) {
extern const char *__progname;
fprintf(stderr, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
__progname, __progname[0] ? ": " : "",
file, line,
function ? function : "", function ? ": " : "",
assertion);
print_trace();
abort();
}
int main() {
assert(0);
}
Assuming the following code (main.c):
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{
pause(); /* line 7 */
}
int main(void)
{
signal(SIGALRM, handler);
alarm(1);
pause();
}
When I run this in gbd and set a break point inside handler(), run the code and wait a second I can do the following:
(gdb) b 7
Breakpoint 1 at 0x4005b7: file main.c, line 7.
(gdb) r
Starting program: a.out
Breakpoint 1, handler (sig=14) at main.c:7
7 pause();
(gdb) bt
#0 handler (sig=14) at main.c:7
#1 <signal handler called>
#2 0x00007ffff7afd410 in __pause_nocancel () at ../sysdeps/unix/syscall-template.S:82
#3 0x00000000004005e0 in main () at main.c:14
Is there a portable way to get 0x00007ffff7afd410 or 0x00000000004005e0?
With sigaction instead of signal the handler is called with the ucontext of the location where the signal occurred:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <ucontext.h>
static void handler(int sig, siginfo_t *siginfo, void *context)
{
ucontext_t *ucontext = context;
printf("rip %p\n", (void *)ucontext->uc_mcontext.gregs[REG_RIP]);
pause();
}
int main(void)
{
struct sigaction sact;
memset(&sact, 0, sizeof sact);
sact.sa_sigaction = handler;
sact.sa_flags = SA_SIGINFO;
if (sigaction(SIGALRM, &sact, NULL) < 0) {
perror("sigaction");
return 1;
}
alarm(1);
pause();
return 0;
}
rip output and gdb bt output:
(gdb) b 13
Breakpoint 1 at 0x4006de: file main.c, line 13.
(gdb) r
Starting program: /home/osboxes/a.out
rip 0x7ffff7ae28a0
Breakpoint 1, handler (sig=14, siginfo=0x7fffffffdf70, context=0x7fffffffde40)
at main.c:13
13 pause();
(gdb) bt
#0 handler (sig=14, siginfo=0x7fffffffdf70, context=0x7fffffffde40)
at main.c:13
#1 <signal handler called>
#2 0x00007ffff7ae28a0 in __pause_nocancel () from /lib64/libc.so.6
#3 0x0000000000400758 in main () at main.c:28
Not extremely portable I guess, but backtrace(3) is available in glibc and a few other libc's:
backtrace() returns a backtrace for the calling program, in the array
pointed to by buffer. A backtrace is the series of currently active
function calls for the program.
You'd have to check how many entries up the stack you need to look. It should be consistent for Linux at least.
If you want to translate the backtrace to something resembling gdb's display, you could use addr2line(1) from binutils. With something like
popen("addr2line -Cfip -e ./myprog", "w")
you could even do it at runtime by writing addresses (as strings) to the FILE* you get back.
I actually asked a similar question earlier at Correct way to shutdown C application to ensure CRITICAL sections completed? but wanted to start fresh and ask is anyone can see and FUNDAMENTAL issues with the following code.
I essentially whipped up a proof of concept, and it goes like this:
Main application launches a thread that needs to "process messages in a transaction"
When the user hits CTRL-C I need the application to run elegantly until the current thread transaction is complete
NOTE: Callbacks from the thread into the MAIN app is ESSENTIAL and I would like to know if this is "bad programming practice"....
The code is working from the output, here is a sample:
lynton#lynton ~/Desktop/ThreadTest $ ./main
Main program started
In thread testMainLoop
Transaction started in spawned thread
onBeginTransaction in main thread
onMessageArrived in main thread
onCommitTransaction in main thread
Transaction ended in spawned thread
Transaction started in spawned thread
onBeginTransaction in main thread
onMessageArrived in main thread
onCommitTransaction in main thread
Transaction ended in spawned thread
Transaction started in spawned thread
onBeginTransaction in main thread
onMessageArrived in main thread
onCommitTransaction in main thread
Transaction ended in spawned thread
^CIn shutdown hook...
Thread still running
listenForMessages loop completed in spawned thread
In onProcessingComplete in main thread
Exciting testMainLoop
All thread transactions complete
Main program exiting
lynton#lynton ~/Desktop/ThreadTest $
In the above you can see that the shutdown hook is initiated and the application ends gracefully...
The basis test code is as follows:
PLEASE take special note of the "(*onProcessingCompleteCallbackFunc)(TRUE);" call from the thread to the main application to say to the main application that the thread is 100% complete. The pthread_join did not seem to work as I needed it to here....
NOTE: The following is the SHARED LIB (libwmq_sender.so) that I created that is used by the main application.
wmq_sender.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define TRUE 0
#define FALSE 1
int wmq_sender_start();
int wmq_sender_stop();
void wmq_sender_registerOnBeginTransactionCallback(int (*callbackFunc)(char *tid));
void wmq_sender_registerOnCommitTransactionCallback(int (*callbackFunc)(char *tid));
void wmq_sender_registerOnMessageArrivedCallback(int (*callbackFunc)(char *tid, char *buffer, int size));
void wmq_sender_registerOnProcessingCompleteCallback(void (*callbackFunc)(int completeFlag));
int listenForMessages();
int (*onBeginTransactionCallbackFunc)(char *tid);
int (*onCommitTransactionCallbackFunc)(char *tid);
int (*onRollbackTransactionCallbackFunc)(char *tid);
int (*onMessageArrivedCallbackFunc)(char *tid, char *buffer, int size);
void (*onProcessingCompleteCallbackFunc)(int completeFlag);
int running;
int rc;
int transactionRunning;
wmq_sender.c
#include "wmq_sender.h"
int listenForMessages(){
char *uuid = NULL;;
char *buffer = NULL;
uuid = malloc(10 * sizeof(char));
strcpy(uuid, "1234567891");
buffer = malloc(11 * sizeof(char));
strcpy(buffer, "test_buffer");
while(running == TRUE){
printf("Transaction started in spawned thread\n");
transactionRunning = TRUE;
(*onBeginTransactionCallbackFunc)(uuid);
(*onMessageArrivedCallbackFunc)(uuid, buffer, 11);
(*onCommitTransactionCallbackFunc)(uuid);
transactionRunning = FALSE;
printf("Transaction ended in spawned thread\n");
sleep(2);
}
printf("listenForMessages loop completed in spawned thread\n");
free(uuid);
free(buffer);
(*onProcessingCompleteCallbackFunc)(TRUE);
return 0;
}
int wmq_sender_start(){
return listenForMessages();
}
int wmq_sender_stop(){
running = FALSE;
return 0;
}
void wmq_sender_registerOnBeginTransactionCallback(int (*callbackFunc)(char *tid)){
onBeginTransactionCallbackFunc = callbackFunc;
}
void wmq_sender_registerOnCommitTransactionCallback(int (*callbackFunc)(char *tid)){
onCommitTransactionCallbackFunc = callbackFunc;
}
void wmq_sender_registerOnMessageArrivedCallback(int (*callbackFunc)(char *tid, char *buffer, int size)){
onMessageArrivedCallbackFunc = callbackFunc;
}
void wmq_sender_registerOnProcessingCompleteCallback(void (*callbackFunc)(int completeFlag)){
onProcessingCompleteCallbackFunc = callbackFunc;
}
The following is the MAIN application that spawns the thread and shuts down gracefully etc.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>
#include "wmq_sender.h"
#define TRUE 0
#define FALSE 1
void *testMainLoop(void *arg);
void shutdownHook(int sig);
int main(int argc, char * argv[]);
int onBeginTransaction(char *tid);
int onCommitTransaction(char *tid);
int onMessageArrived(char *tid, char *buffer, int size);
void onProcessingComplete(int completeFlag);
pthread_t testThread;
int threadRunning;
int rc;
void *testMainLoop(void *arg){
printf("In thread testMainLoop\n");
wmq_sender_registerOnBeginTransactionCallback(onBeginTransaction);
wmq_sender_registerOnCommitTransactionCallback(onCommitTransaction);
wmq_sender_registerOnMessageArrivedCallback(onMessageArrived);
wmq_sender_registerOnProcessingCompleteCallback(onProcessingComplete);
threadRunning = TRUE;
rc = wmq_sender_start();
printf("Exciting testMainLoop\n");
}
void shutdownHook(int sig){
printf("In shutdown hook...\n");
rc = wmq_sender_stop();
while(threadRunning == TRUE){
printf("Thread still running\n");
sleep(2);
}
printf("All thread transactions complete\n");
}
void onProcessingComplete(int completeFlag){
printf("In onProcessingComplete in main thread\n");
threadRunning = FALSE;
}
int main(int argc, char * argv[]){
(void) signal(SIGINT, shutdownHook);
printf("Main program started\n");
rc = pthread_create(&testThread, NULL, testMainLoop, (void *)argv);
pthread_join(testThread, NULL);
printf("Main program exiting\n");
return 0;
}
int onBeginTransaction(char *tid){
printf("onBeginTransaction in main thread\n");
return 0;
}
int onCommitTransaction(char *tid){
printf("onCommitTransaction in main thread\n");
return 0;
}
int onMessageArrived(char *tid, char *buffer, int size){
printf("onMessageArrived in main thread\n");
return 0;
}
Compiling on my test machine is:
gcc -m64 -Wall -g -I./ -c ./main.c -o ./main.o
gcc -m64 -o ./main ./main.o -L./ -L/usr/lib -L/usr/local/lib -lpthread -lwmq_sender
gcc -m64 -Wall -g -c -I./ -I/usr/local/include/ -fPIC ./wmq_sender.c -o ./wmq_sender.o
gcc -shared -o ./libwmq_sender.so ./wmq_sender.o
export LD_LIBRARY_PATH="/home/lynton/Desktop/ThreadTest"
Do you see anything wrong with the way that I use CALLBACKS to tell the main application that the thread is complete etc?
Any help or advise would be great appreciated ;-)
Thanks
Lynton
You do realise that your callback functions are actually running on the thread don't you? As far as I can see, your main thread just creates another thread which does everything and then waits for it to finish. I don't see any point in creating the second thread at the moment.
Anyway, the other horrible problem you have is that your signal handler calls printf and sleep. I'm pretty sure printf is not safe to call in a signal handler and neither is sleep. Really all it should do is set a flag to say "terminating" and then return.
According to the C99 standard, you can't reliably assign to any static variable that isn't declared volatile sig_atomic_t or call any standard library function except abort(), _Exit() or signal() and the last with the same signal number that you received.