两次调用main表现不同

问题描述 投票:0回答:1

我有以下代码,假设调用main函数两次。

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200112L

#include <limits.h>
#include <link.h>
#include <stdio.h>
#include <time.h>
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
#include <stdlib.h>

#include <sys/mman.h>

extern char** environ;

static int (*main_orig)(
    int,
    char**,
    char**
);
static int (*__libc_start_main_orig)(
    int (*)(int, char**, char**),
    int,
    char**,
    int (*)(int, char**, char**),
    void (*)(void),
    void (*)(void),
    void*
);
static int (*init_orig)(
    int,
    char**,
    char**
);
static void (*fini_orig)(void);
static void (*rtld_fini_orig)(void);
static void* stack_end_orig;

static char* libs[] = {
    "libpthread-2.22.so",
    NULL
};

static void my_dlopen(char* lib_name)
{
    int             ret;
    struct timespec before;
    struct timespec after;
    long long       dl_time;
    void*           handle = NULL; 

    (void) clock_gettime(CLOCK_MONOTONIC, &before);

    handle = dlopen(lib_name, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
    if (handle == NULL) {
        fprintf(stdout, "%-35s :: dlopen failed with %s :: ", lib_name, dlerror());
    }
    else {
        fprintf(stdout, "%-35s :: dlopen [OK] :: ", lib_name);

        ret = dlclose(handle);
        if (ret != 0) {
            fprintf(stdout, "dlclose failed with %s :: ", dlerror());
        }
        else {
            fprintf(stdout, "dlclose [OK] :: ");
        }
    }

    (void) clock_gettime(CLOCK_MONOTONIC, &after);
    dl_time = ((long long) (after.tv_sec  - before.tv_sec)) * 1000000000ll + 
              ((long long) (after.tv_nsec - before.tv_nsec));
    fprintf(stdout, "%12lld us\n", dl_time / 1000ll);
}

static int my_main(int argc, char** argv, char** envp)
{
    int i;
    int ret;

    fprintf(stdout, "--- Before main --- %d :: %d :: %p\n", getpid(), getppid(), main_orig);
    for (i = 0; argv[i] != NULL; i++) {
        fprintf(stdout, "argv[%2d] = %s\n", i, argv[i]);
    }
    for (i = 0; envp[i] != NULL; i++) {
        fprintf(stdout, "envp[%2d] = %s\n", i, envp[i]);
    }
    ret = main_orig(argc, argv, envp);
    fprintf(stdout, "--- After main --- %d :: %d :: %p\n", getpid(), getppid(), main_orig);

    return ret;
}

int main_hook(int argc, char** argv, char** envp)
{
    int i;
    int ret;
    struct timespec before;
    struct timespec after;
    long long       dl_time;
    void*           dummy_buffer = NULL;
    const size_t    dummy_size   = 256 * 1024 * 1024;

    asm volatile("": : :"memory");

    fprintf(stdout, "****************************************************\n");
    ret = mlockall(MCL_CURRENT | MCL_FUTURE);
    if (ret == -1) {
        fprintf(stdout, "mlockall failed with errno = %d\n", errno);
    }
    else {
        fprintf(stdout, "mlockall [OK]\n");
    }
    ret = mallopt(M_TRIM_THRESHOLD, -1);
    if (ret == 1) {
        fprintf(stdout, "mallopt(M_TRIM_THRESHOLD, -1) [OK]\n");
    }
    else {
        fprintf(stdout, "mallopt(M_TRIM_THRESHOLD, -1) [!!]\n");
    }
    ret = mallopt(M_MMAP_MAX, 0);
    if (ret == 1) {
        fprintf(stdout, "mallopt(M_MMAP_MAX, 0)        [OK]\n");
    }
    else {
        fprintf(stdout, "mallopt(M_MMAP_MAX, 0)        [!!]\n");
    }
    dummy_buffer = malloc(dummy_size);
    if (dummy_buffer != NULL) {
        fprintf(stdout, "dummy_buffer = %p :: %zu\n", dummy_buffer, dummy_size);
        memset(dummy_buffer, 0x00, dummy_size);
        free(dummy_buffer);
    }

    asm volatile("": : :"memory");

    fprintf(stdout, "****************************************************\n");
    (void) clock_gettime(CLOCK_MONOTONIC, &before);
    for (i = 0; libs[i] != NULL; i++) {
        my_dlopen(libs[i]);
    }
    (void) clock_gettime(CLOCK_MONOTONIC, &after);
    dl_time = ((long long) (after.tv_sec  - before.tv_sec)) * 1000000000ll + 
              ((long long) (after.tv_nsec - before.tv_nsec));
    fprintf(stdout, "****************************************************\n");
    fprintf(stdout, "Total dlopen time = %lld ms\n", dl_time / 1000000ll);

    asm volatile("": : :"memory");

    fprintf(stdout, "****************************************************\n");
    ret  = 0;
    ret += my_main(argc, argv, envp);
    ret += my_main(argc, argv, envp);

    return ret;
}

int __libc_start_main(  int (*main)(int, char**, char**),
                        int argc,
                        char** argv,
                        int (*init)(int, char**, char**),
                        void (*fini)(void),
                        void (*rtld_fini)(void),
                        void* stack_end)
{
    int i;
    int ret;

    for (i = 0; environ[i] != NULL; i++) {
        char* substr = strstr(environ[i], "LD_PRELOAD=");

        if (substr != NULL) {
            fprintf(stdout, "%s found and replaced with ", environ[i]);
            memset(&environ[i][0], 'x', strlen(environ[i]));
        }
        fprintf(stdout, "%s\n", environ[i]);
    }

    __libc_start_main_orig  = dlsym(RTLD_NEXT, "__libc_start_main");
    main_orig               = main;
    init_orig               = init;
    fini_orig               = fini;
    rtld_fini_orig          = rtld_fini;
    stack_end_orig          = stack_end;
    ret = __libc_start_main_orig(main_hook, argc, argv, init, fini, rtld_fini, stack_end);

    return ret;
}

此代码编译为共享库,包含:

$CC -ggdb -fPIC -shared replace_main.c -o replace_main -ldl -lrt -pthread.

我像这样使用so文件:

LD_PRELOAD=/var/log/replace_main ls -l /

这给了我:

LD_PRELOAD=/var/log/replace_main found and replaced with xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TERM=xterm-256color
SHELL=/bin/sh
SSH_CLIENT=xxxxxxxxx
OLDPWD=/var/log
SSH_TTY=/dev/pts/0
USER=root
LD_LIBRARY_PATH=xxxxxxxxx
MAIL=/var/mail/root
PATH=xxxxxxxxx
LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
PWD=/var/log
EDITOR=vi
TZ=UTC
PS1=\u@\h:\w\$ 
SHLVL=1
HOME=/root
LOGNAME=root
SSH_CONNECTION=xxxxxxxxx
_=/bin/ls
****************************************************
mlockall [OK]
mallopt(M_TRIM_THRESHOLD, -1) [OK]
mallopt(M_MMAP_MAX, 0)        [OK]
dummy_buffer = 0x5a4020 :: 268435456
****************************************************
libpthread-2.22.so                  :: dlopen [OK] :: dlclose [OK] ::          596 us
****************************************************
Total dlopen time = 0 ms
****************************************************
--- Before main --- 25870 :: 3265 :: 0x12068
argv[ 0] = ls
argv[ 1] = -l
argv[ 2] = /
envp[ 0] = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
envp[ 1] = TERM=xterm-256color
envp[ 2] = SHELL=/bin/sh
envp[ 3] = SSH_CLIENT=xxxxxxxxx
envp[ 4] = OLDPWD=/var/log
envp[ 5] = SSH_TTY=/dev/pts/0
envp[ 6] = USER=root
envp[ 7] = LD_LIBRARY_PATH=xxxxxxxxx
envp[ 8] = MAIL=/var/mail/root
envp[ 9] = PATH=xxxxxxxxx
envp[10] = LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
envp[11] = LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
envp[12] = PWD=/var/log
envp[13] = EDITOR=vi
envp[14] = TZ=UTC
envp[15] = PS1=\u@\h:\w\$ 
envp[16] = SHLVL=1
envp[17] = HOME=/root
envp[18] = LOGNAME=root
envp[19] = SSH_CONNECTION=xxxxxxxxx
envp[20] = _=/bin/ls
total 19
drwxr-xr-x   2 root  root     6176 Jul 17  2018 bin
drwxr-xr-x   2 root  root        3 Jul 17  2018 boot
-rw-rw-r--   1 sirpa tracing  2447 Jul 17  2018 cxp9025851_3.xml
drwxr-xr-x   8 root  root     8300 Apr 24 11:48 dev
drwxr-xr-x  38 root  root     1584 Jul 17  2018 etc
drwxr-xr-x   3 root  root       28 Jul 17  2018 home
drwxr-xr-x   8 root  root     5263 Jul 17  2018 lib
-rwxr-xr-x   1 root  root      509 Apr 25  2018 linuxrc.sh
drwxr-xr-x   2 root  root        3 Apr 22  2018 media
drwxr-xr-x   2 root  root        3 Apr 22  2018 mnt
drwxr-xr-x   8 root  root      115 Jul 17  2018 opt
dr-xr-xr-x 759 root  root        0 Jan  1  1970 proc
drwxrwxrwx  28 sirpa users    4096 Apr 23 17:51 rcs
drwxr-xr-x   2 root  root        3 Apr 22  2018 root
drwxrwxrwt  12 root  root      540 Apr 24 11:49 run
drwxr-xr-x   2 root  root     2868 Jul 17  2018 sbin
drwxr-xr-x  53 root  root    12288 Apr 24 10:39 software
dr-xr-xr-x  12 root  root        0 Apr 24 11:48 sys
drwxrwxrwt  10 root  root      740 Apr 24 12:49 tmp
drwxr-xr-x  10 root  root      150 Apr 22  2018 usr
drwxr-xr-x  13 root  root      186 Jul 17  2018 var
--- After main --- 25870 :: 3265 :: 0x12068
--- Before main --- 25870 :: 3265 :: 0x12068
argv[ 0] = ls
argv[ 1] = -l
argv[ 2] = /
envp[ 0] = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
envp[ 1] = TERM=xterm-256color
envp[ 2] = SHELL=/bin/sh
envp[ 3] = SSH_CLIENT=xxxxxxxxx
envp[ 4] = OLDPWD=/var/log
envp[ 5] = SSH_TTY=/dev/pts/0
envp[ 6] = USER=root
envp[ 7] = LD_LIBRARY_PATH=xxxxxxxxx
envp[ 8] = MAIL=/var/mail/root
envp[ 9] = PATH=xxxxxxxxx
envp[10] = LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
envp[11] = LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
envp[12] = PWD=/var/log
envp[13] = EDITOR=vi
envp[14] = TZ=UTC
envp[15] = PS1=\u@\h:\w\$ 
envp[16] = SHLVL=1
envp[17] = HOME=/root
envp[18] = LOGNAME=root
envp[19] = SSH_CONNECTION=xxxxxxxxx
envp[20] = _=/bin/ls
bin  boot  cxp9025851_3.xml  dev  etc  home  lib  linuxrc.sh  media  mnt  opt  proc  rcs  root  run  sbin  software  sys  tmp  usr  var
--- After main --- 25870 :: 3265 :: 0x12068
root@du1:/var/log# 

正如您所看到的那样,ls的main被调用两次,但第二次似乎忽略了-l参数。原因是什么?

PS:我在内核4.1的ARM Linux变种上运行代码

谢谢,

linux glibc
1个回答
0
投票

原因是什么?

选项解析库具有内部状态,您不会重置该状态。

来自man 3 getopt

extern int optind, ...

The variable optind is the index of the next element to be processed in argv.
The system initializes this value to 1.  The caller can reset it to 1 to restart
scanning of the same argv, or when scanning a new argument vector.

由于你没有将optind中的/bin/ls重置为1,当真正的main称为getopt时,它会立即获得“不再选择”的答案。

这可以用一个简单的程序来证明:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
  int rc;
  while ((rc = getopt(argc, argv, "abc")) != -1) {
    printf("getopt: '%c'\n", rc);
  }
  return 0;
}

gcc main.c -o main
LD_PRELOAD=./replace_main.so ./main -ab

...
--- Before main --- 34822 :: 14915 :: 0x55ce59e2768a
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 34822 :: 14915 :: 0x55ce59e2768a
--- Before main --- 34822 :: 14915 :: 0x55ce59e2768a
argv[ 0] = ./main
argv[ 1] = -ab
--- After main --- 34822 :: 14915 :: 0x55ce59e2768a

请注意,第二次调用时缺少getopt行(正如人们所期望的那样)。

如果我在optind = 1;中的while循环之前添加main.c,那么它会按照您的预期开始工作:

--- Before main --- 35487 :: 14915 :: 0x55939fb706ca
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 35487 :: 14915 :: 0x55939fb706ca
--- Before main --- 35487 :: 14915 :: 0x55939fb706ca
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 35487 :: 14915 :: 0x55939fb706ca
© www.soinside.com 2019 - 2024. All rights reserved.