Какова процедура работы 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)