2015-08-30 36 views
1

请让我知道什么是_nocancel()系统调用(如__pwrite_nocancel()的方式,以及是否有创建方式一个LD_PRELOAD库来拦截这些调用,下面是一些背景:什么是_nocancel()系统调用在Linux和有使用LD_PRELOAD去拦截

我正在调查Oracle数据库的功能,并且希望使用LD_PRELOAD添加一个小的填充图层来捕获用户空间中的调用信息。我知道使用系统抽头捕获这些信息的其他方法,但使用LD_PRELOAD是来自客户的一个硬性要求。 strace显示此特定进程正在重复调用pwrite();同样,pstack堆栈跟踪显示__pwrite_nocancel( )被称为t他最后一次进入堆栈。我试图复制我自己__libc_pwrite()函数,并宣布 extern ssize_t pwrite(int fd, const void *buf, size_t numBytes, off_t offset)__attribute__((weak, alias ("__libc_pwrite"))); 但是当我链接库和运行纳米-a | grep的PWRITE,我得到这个:

000000000006c190 T __libc_pwrite 
000000000006c190 W pwrite 
相比之下

,纳米-a/lib64的/ libpthread.so.0 | grep的PWRITE给出如下:

000000000000eaf0 t __libc_pwrite 
000000000000eaf0 t __libc_pwrite64 
000000000000eaf0 W pwrite 
000000000000eaf0 t __pwrite 
000000000000eaf0 W pwrite64 
000000000000eaf0 W __pwrite64 
0000000000000000 a pwrite64.c 
000000000000eaf9 t __pwrite_nocancel 

我已经注意到,_nocancel版本是提前__pwrite的只有9个字节,但看源代码,我不能确定在何处正在创建:

/* Copyright (C) 1997, 1998, 2000, 2002, 2003, 2004, 2006 
    Free Software Foundation, Inc. 
    This file is part of the GNU C Library. 
    Contributed by Ulrich Drepper <[email protected]>, 1997. 
    The GNU C Library is free software; you can redistribute it and/or 
    modify it under the terms of the GNU Lesser General Public 
    License as published by the Free Software Foundation; either 
    version 2.1 of the License, or (at your option) any later version. 
    The GNU C Library is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
    Lesser General Public License for more details. 
    You should have received a copy of the GNU Lesser General Public 
    License along with the GNU C Library; if not, see 
    <http://www.gnu.org/licenses/>. */ 

#include <assert.h> 
#include <errno.h> 
#include <unistd.h> 
#include <endian.h> 

#include <sysdep-cancel.h> 
#include <sys/syscall.h> 
#include <bp-checks.h> 

#include <kernel-features.h> 

#ifdef __NR_pwrite64   /* Newer kernels renamed but it's the same. */ 
# ifdef __NR_pwrite 
# error "__NR_pwrite and __NR_pwrite64 both defined???" 
# endif 
# define __NR_pwrite __NR_pwrite64 
#endif 

#if defined __NR_pwrite || __ASSUME_PWRITE_SYSCALL > 0 

# if __ASSUME_PWRITE_SYSCALL == 0 
static ssize_t __emulate_pwrite (int fd, const void *buf, size_t count, 
       off_t offset) internal_function; 
# endif 

ssize_t 
__libc_pwrite (fd, buf, count, offset) 
    int fd; 
    const void *buf; 
    size_t count; 
    off_t offset; 
{ 
    ssize_t result; 

    if (SINGLE_THREAD_P) 
    { 
     /* First try the syscall. */ 
     result = INLINE_SYSCALL (pwrite, 6, fd, CHECK_N (buf, count), count, 0, 
        __LONG_LONG_PAIR (offset >> 31, offset)); 
# if __ASSUME_PWRITE_SYSCALL == 0 
     if (result == -1 && errno == ENOSYS) 
     /* No system call available. Use the emulation. */ 
     result = __emulate_pwrite (fd, buf, count, offset); 
# endif 
     return result; 
    } 

    int oldtype = LIBC_CANCEL_ASYNC(); 

    /* First try the syscall. */ 
    result = INLINE_SYSCALL (pwrite, 6, fd, CHECK_N (buf, count), count, 0, 
       __LONG_LONG_PAIR (offset >> 31, offset)); 
# if __ASSUME_PWRITE_SYSCALL == 0 
    if (result == -1 && errno == ENOSYS) 
    /* No system call available. Use the emulation. */ 
    result = __emulate_pwrite (fd, buf, count, offset); 
# endif 

    LIBC_CANCEL_RESET (oldtype); 

    return result; 
} 

strong_alias (__libc_pwrite, __pwrite) 
weak_alias (__libc_pwrite, pwrite) 

# define __libc_pwrite(fd, buf, count, offset) \ 
    static internal_function __emulate_pwrite (fd, buf, count, offset) 
#endif 

#if __ASSUME_PWRITE_SYSCALL == 0 
# include <sysdeps/posix/pwrite.c> 
#endif 

任何帮助表示赞赏。

回答

0

既然你想通过LD_PRELOAD夹着,你需要看看rtld使用相同的符号。因此,而不是nm -a,请使用objdump -T。如果你想要的符号没有在那里列出,你不能介入它。所有导出的glibc符号都有一个“版本”字符串,该字符串通过rtld进行比较,并允许具有相同符号的多个版本(这就是为什么即使nm -D也没用 - 它不显示版本)。要介绍一些东西,你应该有相同的版本字符串。这可以使用版本脚本(请参阅info ld),.symver asm指令或调用该指令的宏来完成。请注意,带有“私有”版本的符号可能会在libc的任何新版本中发生变化,其他符号可能会在任何新版本中被新版本取代(符号仍然有效,但针对新版本编译的应用程序不会使用他们了)。

+0

感谢您的信息;幸运的是,尽管LD_PRELOAD似乎无视版本符号(可能因为只定义了一个版本)而覆盖了该函数,所以我不必经历樱桃挑选符号的麻烦。当我做objdump -T时,libpthread符号的确显示为GLIBC-2.2.5,而我的是Base。经过一番挖掘,__pwrite_nocancel()实际上来自我没有导出的pwrite64符号。在添加了另一个弱别名之后,没关系。再次感谢您的回复。 – Leo

2

pwrite_nocancel()等等都没有在Linux的系统调用。它们是C库内部的函数,并且与pthread和线程取消紧密结合。

_nocancel()版本有着完全一样的原始功能,但这些版本不是线程取消点。

大多数I/O功能都是取消点。也就是说,如果线程取消类型是推迟和取消状态启用,并在过程中另一个线程已经请求被取消的线程,该线程将取消(出口)进入取消点时。进一步的细节参见man 3 pthread_cancel,man 3 pthread_setcancelstateman 3 pthread_setcanceltype

不幸的是,pwrite_nocancel()_nocancel()功能是内部(本地)到pthreads库,因此,是相当困难的干预;它们不是动态符号,所以动态链接器不能覆盖它们。在这一点上,我怀疑,但不确定,干涉它们的方式将涉及重写库代码的开始,直接跳转到您自己的代码。

如果它们是导出(全局)函数,则可以像使用常规方法一样插入,就像任何其他库函数(这些由pthread库提供,libpthread)一样。 (这里我自己的答案,你可能会发现thisthisthis信息。否则,只搜索LD_PRELOAD例如。)

+0

谢谢您的详细解答,特别是有用的链接。幸运的是,好的LD_PRELOAD最终还是有效的。在使用gdb并附加到oracle并使用'layout asm'模式后,我发现尽管它没有出现在堆栈中,但__pwrite_nocancel实际上是一个从pwrite64符号开始的标签。在添加行extern之后,我们可以使用pset64(int fd,const void * buf,size_t numBytes,off_t offset)__属性__((弱,别名(“__libc_pwrite”)));到我的代码,一切开始工作。 – Leo

+0

@Leo,所以'_nocancel()'变体只能从库函数本身调用,因此不需要单独插入;插入标准库函数也会照顾它们。有道理,真的。我懒得去查看pthreads来源,看看是否从其他地方调用_nocancel()变体。它们对图书馆来说是本地的,意味着你不应该直接调用它们 - 除非你获得了一个指向它们的函数指针。动态链接器肯定不会。 –