Estoy tratando de usar sscanf() de preload.so que se genera desde preload.c .
Para verificar mi sscanf() de preload.so se llama, agregué una declaración de impresión adicional: printf("test\n");
¿Hay algo que este olvidando?
El contenido de los archivos se menciona a continuación:
//preload.c #include <stdarg.h> #include <stdio.h> __attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...) { int ret; va_list ap; va_start(ap, format); printf("test\n"); ret = vsscanf(str, format, ap); va_end(ap); return ret; } //foo.c #include <stdio.h> int main(void) { int i; sscanf("42", "%d", &i); printf("%d\n", i); return 0; }Estoy haciendo los siguientes pasos:
# gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1 # export LD_PRELOAD=$PWD/preload.so # gcc foo.c -o foo test test test test test test test test test test testO/p estoy recibiendo:
# echo $LD_PRELOAD /AMIT/sscanf_override/preload.so # ./foo 42 # LD_PRELOAD=$PWD/preload.so ./foo 42La salida esperada es:
$ gcc foo.c -o foo $ LD_PRELOAD=$PWD/preload.so ./foo test 42 Incluso la salida de ldd apunta a preload.so como se muestra a continuación, mientras que la ejecución da preferencia a sscanf() del sistema y no al de preload.so
root@***sscanf_override]# ldd foo linux-vdso.so.1 (0x00007fff4a5e0000) /AMIT/sscanf_override/preload.so (0x00007f1cf270a000) libc.so.6 => /lib64/libc.so.6 (0x00007f1cf2345000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f1cf2141000) /lib64/ld-linux-x86-64.so.2 (0x00007f1cf290c000) [root@***sscanf_override]# El ajuste aquí es, si -D_GNU_SOURCE=1 , funciona, gcc -fPIC -shared preload.c -o preload.so -ldl Pero no puedo continuar sin definir GNU_SOURCE como imprescindible para uso futuro.
Como sugirió @Rachid K, funcionó cuando redefiní mi preload.c como se muestra a continuación:
#include <stdarg.h> #include <stdio.h> __attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...) { int ret; va_list ap; va_start(ap, format); printf("test\n"); ret = vsscanf(str, format, ap); va_end(ap); return ret; } __attribute__((force_align_arg_pointer)) int __isoc99_sscanf(const char *str, const char *format, ...) { int ret; va_list ap; va_start(ap, format); printf("test\n"); ret = vsscanf(str, format, ap); va_end(ap); return ret; }Eche un vistazo a readelf -s foo .
Creo que es probable que no haya una llamada a sscanf en su ejecutable. Suponiendo que está utilizando GLIBC como libc, sospecho que es una llamada a __isoc99_sscanf . Esta es una redirección que hace la biblioteca, aparentemente porque su variante sscanf original usa extensiones en conflicto con el estándar, consulte esta pregunta .
Si observa readelf -s preload.so también, probablemente mostrará una definición para sscanf .
La redirección ocurre a través de una macro en stdio.h que incluye en ambos, pero sospecho que _GNU_SOURCE=1 desactiva la redirección, por lo que aunque stdio.h está incluido en preload.c , no está reemplazando sscanf con __isoc99_sscanf allí.
En la compilación de foo , probablemente no use -D_GNU_SOURCE=1 y, por lo tanto, obtenga una discrepancia entre los nombres de los símbolos.
La interposición de símbolos con LD_PRELOAD siempre es un poco complicada. Además de problemas como los anteriores, también hay muchas situaciones en las que los compiladores optimizarán o transformarán las llamadas de biblioteca estándar. Por ejemplo, printf -> puts si la cadena de formato no usa ningún formato.
sscanf() puede ser una macro que hace referencia a una función interna. Eche un vistazo a <stdio.h> . Por ejemplo, en mi sistema, tengo:
extern int __isoc99_sscanf (const char *__restrict __s, const char *__restrict __format, ...) __THROW; # define fscanf __isoc99_fscanf # define scanf __isoc99_scanf # define sscanf __isoc99_sscanf Por lo tanto, sscanf() es en realidad una macro que hace referencia __isoc99_sscanf() . Entonces, si redefines este último, obtienes lo que esperas.
#include <stdarg.h> #include <stdio.h> //__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...) int __isoc99_sscanf(const char *str, const char *format, ...) { int ret; va_list ap; va_start(ap, format); printf("test\n"); ret = vsscanf(str, format, ap); va_end(ap); return ret; }Después de la reconstrucción, obtienes:
$ gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1 $ LD_PRELOAD=`pwd`/preload.so ./foo test 42