Какова процедура работы getpid в glibc?

Ситуация как ниже:

Я пытаюсь сделать проект, который взламывает ядро ​​в github. Версия ядра - linux-3.18.6.

QEMU используется для моделирования окружающей среды.

В моем приложении я пытаюсь понять процедуру syscall, следуя им. Способ достижения моей цели аналогичен программе оболочки. Я просто создаю несколько команд для запуска относительного системного вызова. Может быть, это просто через картину. некоторые команды

Код прост в следующем:

1 Используйте API getpid.

int Getpid(int argc, char **argv)
{
    pid_t pid;
    pid = getpid();
    printf("current process's pid:%d\n",pid);
    return 0;
}

2 Используйте int $0x80 напрямую.

int GetpidAsm(int argc, char **argv)
{
    pid_t pid;
    asm volatile(
    "mov $20, %%eax\n\t"
    "int $0x80\n\t"
    "mov %%eax, %0\n\t"
    :"=m"(pid)
    );
    printf("current process's pid(ASM):%d\n",pid);
    return 0;
}

Поскольку мое приложение просто запускается в процессе с pid 1, поэтому каждый раз, когда я набираю команду getpid, оно возвращает 1. Конечно, это правда.

Странно то, что когда я использую gdb для отладки процесса syscall, он останавливается на berakpoint sys_getpid только один раз, когда я набираю getpid для выполнения. Когда я делаю это снова и снова, он просто выводит без остановки.

Очевидно, что использование int $0x80 абсолютно правильно, насколько я понимаю.

Чтобы решить проблему, я провел небольшое исследование. Я загружаю исходный код glibc (glibc-2.25), чтобы увидеть, как API getpid оборачивается в $0x80. К сожалению, его там не было, или я просто не нашел нужную позицию.

некоторый код в glibc.

pid_t getpid(void)
{
  pid_t (*f)(void);
  f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid");
  if (f == NULL)
    error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror ());
  return (pid2 = f()) + 26;
}

Если я получил неправильный код, пожалуйста, скажите мне, ткс.

Как указывает код, определение getpid не содержится в glibc. Прочитав некоторые данные, кто-то сказал VDSO...

Обратите внимание, что, AFAIK, значительная часть стоимости простых системных вызовов переходит от пространства пользователя к ядру и обратно. Следовательно, для некоторых системных вызовов (вероятно, gettimeofday, getpid ...) VDSO может избежать даже этого (и технически может избежать реального вызова системы).

В человеке getpid pgae:

Различия между библиотекой и ядром C Начиная с версии 2.3.4 glibc, функция-оболочка glibc для getpid() кэширует PID, чтобы избежать дополнительных системных вызовов, когда процесс вызывает getpid() повторно. Обычно это кэширование невидимо, но его правильная работа зависит от поддержки функций-оболочек для fork(2), vfork(2) и clone(2): если приложение обходит оболочки glibc для этих системных вызовов с помощью syscall(2)), тогда вызов getpid() в потомке вернет неправильное значение (если быть точным: вернет PID родительского процесса). См. Также clone (2) для обсуждения случая, когда getpid() может возвращать неправильное значение даже при вызове clone (2) через функцию-обертку glibc.

Хотя есть много объяснений, я не могу понять, как работает API getpid.

В отличие от этого, время API легко понять. Определение времени:

time_t
time (time_t *t)
{
  INTERNAL_SYSCALL_DECL (err);
  time_t res = INTERNAL_SYSCALL (time, err, 1, NULL);
  /* There cannot be any error.  */
  if (t != NULL)
    *t = res;
  return res;
}

затем,

#define INTERNAL_SYSCALL(name, err, nr, args...)            \
    internal_syscall##nr ("li\t%0, %2\t\t\t# " #name "\n\t",    \
                  "IK" (SYS_ify (name)),            \
                  0, err, args)

Наконец, это встроенный asm, обычный способ использования исходного кода ядра.

#define internal_syscall1(v0_init, input, number, err, arg1)        \
({                                  \
    long _sys_result;                       \
                                    \
    {                               \
    register long __s0 asm ("$16") __attribute__ ((unused))     \
      = (number);                           \
    register long __v0 asm ("$2");                  \
    register long __a0 asm ("$4") = (long) (arg1);          \
    register long __a3 asm ("$7");                  \
    __asm__ volatile (                      \
    ".set\tnoreorder\n\t"                       \
    v0_init                             \
    "syscall\n\t"                           \
    ".set reorder"                          \
    : "=r" (__v0), "=r" (__a3)                  \
    : input, "r" (__a0)                     \
    : __SYSCALL_CLOBBERS);                      \
    err = __a3;                         \
    _sys_result = __v0;                     \
    }                               \
    _sys_result;                            \
})

Может кто-нибудь объяснить, как работает API getpid? Почему getpid просто попадает в системный вызов sys_getpid только один раз? Некоторые ссылки вызывают восхищение, если это возможно.

Спасибо за вашу помощь.

1 ответ

Решение

Прежде всего, обратите внимание, что исходный код glibc практически невозможен для навигации.

В документации говорится, что getpid() кэширует свой результат, как вы заметили. Код, который вы нашли, выглядит так

pid_t getpid(void)
{
  pid_t (*f)(void);
  f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid");
  if (f == NULL)
    error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror ());
  return (pid2 = f()) + 26;
}

это просто обертка. Это выглядит getpid символ и вызывает эту функцию. Эта функция - то, что вам нужно найти. Он связан с функцией __getpid(), которую вы найдете в sysdeps/unix/sysv/linux/getpid.c файл, а также показан в нижней части этого поста.

Теперь - возможно, вы просматриваете исходный код glibc, который не соответствует вашему текущему glibc - в этом коммите произошли большие изменения, касающиеся именно кэширования getpid() в ноябре 2016 года, насколько я могу судить, что изменение будет частью glibc-2.25 выпущен в феврале 2017

Более старую реализацию getpid(), которая кэшировала свое значение, чтобы избежать вызова системного вызова getpid() более одного раза, можно увидеть здесь: http://repo.or.cz/glibc.git/blob/93eb85ceb25ee7aff432ddea0abf559f53d7a5fc:/sysdeps/unix/sysv/linux/getpid.c и выглядит так

static inline __attribute__((always_inline)) pid_t
really_getpid (pid_t oldval)
{
  if (__glibc_likely (oldval == 0))
    {
      pid_t selftid = THREAD_GETMEM (THREAD_SELF, tid);
      if (__glibc_likely (selftid != 0))
    return selftid;
    }

  INTERNAL_SYSCALL_DECL (err);
  pid_t result = INTERNAL_SYSCALL (getpid, err, 0);

  /* We do not set the PID field in the TID here since we might be
     called from a signal handler while the thread executes fork.  */
  if (oldval == 0)
    THREAD_SETMEM (THREAD_SELF, tid, result);
  return result;
}
#endif

pid_t
__getpid (void)
{
#if !IS_IN (libc)
  INTERNAL_SYSCALL_DECL (err);
  pid_t result = INTERNAL_SYSCALL (getpid, err, 0);
#else
  pid_t result = THREAD_GETMEM (THREAD_SELF, pid);
  if (__glibc_unlikely (result <= 0))
    result = really_getpid (result);
#endif
  return result;
}

libc_hidden_def (__getpid)
weak_alias (__getpid, getpid)
libc_hidden_def (getpid)
Другие вопросы по тегам