Я пытаюсь воспроизвести поведение strace в C, используя ptrace.
Я хотел бы проверить, вернул ли системный вызов ошибку (и какую ошибку1), но не знаю, как это сделать?
Вот пример вывода strace, который я пытаюсь воспроизвести:
execve("./exit", ["./exit"], 0x7ffff059f250 /* 62 vars */) = 0
read(42, NULL, 0) = -1 EBADF (Bad file descriptor)
exit(0) = ?
+++ exited with 0 +++
Я пытался сравнить возвращаемое значение системного вызова2 с -1, но они не равны.
Я вижу в реализации системного вызова GLIBC (sysdeps/unix/sysv/linux/syscall.c), что используется макрос INTERNAL_SYSCALL_ERROR_P, который, похоже, является макросом для внутреннего использования GLIBC. Я нашел две реализации этого макроса:
sysdeps/unix/sysv/linux/sysdep.h:#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long int)(val) > -4096UL)
sysdeps/unix/sysv/linux/x86_64/x32/times.c:#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long long int)(val) >= -4095LL)
Поскольку первый находится в заголовке, напрямую связанном с системой (в отличие от второго), я бы склонен сказать, что именно он используется syscall. Но я не знаю, являются ли они дженериками и смогу ли я их использовать.
1Чтобы восстановить код ошибки, инвертирование возвращаемого значения системного вызова (-rax), похоже, работает.
2С помощью ptrace(PTRACE_GETREGS, ...) мы можем получить дочерние регистры и сохранить их в struct user_regs_struct (определенном в sys/user.h). Получив доступ к члену rax этой структуры, мы получаем возвращаемое значение системного вызова.
Что означает «Я пытался сравнить возвращаемое значение системного вызова с -1»? Вы не можете сравнивать возвращаемое значение syscall ни с чем, пока оно каким-то образом не станет вам видимым. Вы имеете в виду, что пытались отследить программу, и в этой программе у вас был код для сравнения возвращаемого значения вызова с -1? Вы имеете в виду, что посмотрели вывод strace и искали в нем «-1»? Что-то другое?
@EricPostpischil Возвращаемое значение системного вызова хранится в регистре rax. То есть я сравнил это с -1. Фактически, у меня есть программа, которая отслеживает другую с помощью ptrace. Я не уверен, что понял ваш вопрос, правильно ли я на него ответил?
Итак, реальная проблема заключается в том, что у вас есть программа, которая пытается использовать ptrace, и эта программа ведет себя не так, как вы ожидаете. Для этого вам необходимо предоставить минимально воспроизводимый пример программы, о которой идет речь. Я вижу, что вы уже ввели ответ, и он подтверждает это: вы, очевидно, получали правильное значение, возвращаемое в члене rax структуры, но не интерпретировали его должным образом. Предоставление минимально воспроизводимого примера позволило бы другим людям воспроизвести и диагностировать проблему.





Думаю, я нашел решение.
syscall, похоже, не устанавливает флаг переноса.
В противном случае, как мы видим в этом руководстве по системным вызовам, если rax отрицательное значение (ниже 0), это указывает на ошибку.
Поскольку rax в struct user_regs_struct не имеет знака, нам просто нужно привести его к знаку, и мы сможем узнать, вернулся ли системный вызов и произошла ли ошибка, а также какая ошибка (-rax, чтобы получить код ошибки).
int main(int argc, const char *argv[])
{
struct user_regs_struct regs;
// strace logic ...
if ((long long)regs.rax < 0)
printf(" = -1 %s (%s)\n", strerrorname_np((int)-regs.rax), strerror((int)-regs.rax));
else
printf(" = %llu\n", regs.rax);
// ...
}
При ошибке системного вызова (под x86) IIRC устанавливает флаг переноса.