где находятся макросы _syscallN в <linux / unistd.h>? - PullRequest
4 голосов
/ 22 мая 2010

Раньше было так, что если вам нужно было сделать системный вызов непосредственно в Linux без использования существующей библиотеки, вы могли бы просто включить <linux/unistd.h>, и это определило бы макрос, подобный этому:

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
  : "=a" (__res) \
  : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3))); \
if (__res>=0) \
  return (type) __res; \
errno=-__res; \
return -1; \
}

Тогда вы могли бы просто вставить где-нибудь в свой код:

_syscall3(ssize_t, write, int, fd, const void *, buf, size_t, count);

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

Кажется, что эта система была заменена чем-то (я предполагаю, что страница "[vsyscall]", которую получает каждый процесс) более надежна.

Так каков правильный (пожалуйста, конкретный) способ для программы выполнять системный вызов непосредственно на новых ядрах Linux? Я понимаю, что должен использовать libc, и пусть он сделает всю работу за меня. Но давайте предположим, что у меня есть достойная причина для желания узнать, как это сделать: -).

1 Ответ

4 голосов
/ 24 мая 2010

ОК, так что я углубился в это, так как я не получил большой ответ здесь и нашел некоторую полезную информацию.Первый, когда приложение запускается в linux, в дополнение к традиционным параметрам argc, argv, envp.Передан еще один массив с дополнительными данными, который называется auxv.Подробнее см. здесь .

Одна из этих пар ключ / значение имеет ключ, эквивалентный AT_SYSINFO.Определяется либо в /usr/include/asm/auxvec.h, либо в /usr/include/elf.

. Значение, связанное с этим ключом, является точкой входа в функцию системного вызова (на странице "vdso" или "vsyscall", отображаемой в каждом процессе.

Вы можете просто заменить традиционные инструкции int 0x80 или syscall вызовом по этому адресу, и он на самом деле сделает системный вызов. К сожалению, это уродливо. Так что ребята из libc придумали хорошее решение.Когда они выделяют TCB и присваивают его сегменту gs, они помещают значение AT_SYSINFO в некоторое фиксированное смещение в TCB (к сожалению, оно не фиксируется в разных версиях, поэтому нельзя полагаться насмещение всегда является одной и той же константой.) Поэтому вместо традиционного int 0x80 вы можете просто сказать call *%gs:0x10, который вызовет подпрограмму системного вызова, найденную в разделе vdso.

Я полагаю, цельЭто облегчает написание libc. Это позволяет ребятам libc написать один блок кода для обработки системных вызовов и больше не беспокоиться об этом. Ребята из ядра могутИзмените, как системные вызовы выполняются в любой момент времени, им просто нужно изменить содержимое страницы vdso, чтобы использовать новый механизм, и это хорошо.На самом деле, вам не нужно даже перекомпилировать ваш libc!Тем не менее, для нас, людей, пишущих встроенную сборку и пытающихся поиграть с вещами под капотом, это делает боль в заднице.

К счастью, старый способ все еще работает, если вы действительно хочу сделать что-то вручную: -).

РЕДАКТИРОВАТЬ: одна вещь, которую я заметил своими экспериментами, это то, что AT_SYSINFO, похоже, не передается программе на моем x86_64коробка (AT_SYSINFO_EHDR есть, но я пока не знаю, как ее использовать).Поэтому я не уверен на 100%, как в этой ситуации определяется адрес функции системного вызова.

...