Вызов main дважды ведет себя по-разному - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть следующий код, который должен вызывать основную функцию дважды.

#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: я запускаю код на варианте ARM Linux с ядром 4.1

Спасибо

1 Ответ

0 голосов
/ 26 апреля 2019

В чем причина?

Библиотека разбора параметров имеет внутреннее состояние, и вы не сбрасываете это состояние.

С 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
...