Я учился использовать ptrace и столкнулся со странной проблемой:
Я написал программу:
#include <cstdio>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
int main()
{
long x=(long)mmap(0,-235,2,34,-1,0);
printf("Child: x=%ld (",x);
for(int i=31;i>=0;i--) printf((x&(1<<i))?"1":"0");
printf(")\n");
printf("Child errno: %s\n",strerror(errno));
return 0;
}
Это просто делает системный вызов mmap с неверным параметром. Затем он печатает возвращаемое значение (также в двоичном формате) и errno.
Здесь у меня есть вывод этой программы после ее выполнения:
Child: x=-1 (11111111111111111111111111111111)
Child errno: Cannot allocate memory
И я запускаю его с помощью strace:
execve("./nic.e", ["./nic.e"], [/* 35 vars */]) = 0
uname({sys="Linux", node="dom", ...}) = 0
brk(0) = 0x9237000
brk(0x9237cd0) = 0x9237cd0
set_thread_area({entry_number:-1 -> 6, base_addr:0x9237830, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
brk(0x9258cd0) = 0x9258cd0
brk(0x9259000) = 0x9259000
mmap2(NULL, 4294967061, PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7798000
write(1, "Child: x=-1 (1111111111111111111"..., 47Child: x=-1 (11111111111111111111111111111111)
) = 47
write(1, "Child errno: Cannot allocate mem"..., 36Child errno: Cannot allocate memory
) = 36
exit_group(0) = ?
И strace сообщает, что этот неправильный mmap возвращает -1 с ошибкой ENOMEM.
До сих пор все в порядке.
Вот мой код с ptrace (я вырезал все, что не очень нужно):
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/reg.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int main(int argc,char**argv)
{
int pid=fork();
if(!pid)
{
ptrace(PTRACE_TRACEME,0,NULL,NULL);
execve("nic.e",NULL,NULL);
exit(1);
}
while(true)
{
int status;
waitpid(pid,&status,0);
if(WIFEXITED(status)) return 0;
int signal;
if(WIFSTOPPED(status))
{
signal=WSTOPSIG(status);
}
if(WIFSIGNALED(status)) return 0;
if(signal==SIGTRAP)
{
user_regs_struct regs;
ptrace(PTRACE_GETREGS,pid,NULL,®s);
if(regs.orig_eax==__NR_mmap2)
{
static bool mmap_back=false;
if(!mmap_back) mmap_back=true;
else
{
mmap_back=false;
long x=regs.eax;
printf("mmap return: %ld (",x);
for(int j=31;j>=0;j--) printf((x&(1<<j))?"1":"0");
printf(")\n");
}
}
}
ptrace(PTRACE_SYSCALL,pid,NULL,NULL);
}
return 0;
}
Он должен печатать то же, что печатает дочерний элемент, — повторять значения системных вызовов mmap2.
Но вот вывод:
mmap return: -12 (11111111111111111111111111110100)
mmap return: -1216753664 (10110111011110011101000000000000)
Child: x=-1 (11111111111111111111111111111111)
Child errno: Cannot allocate memory
Почему mmap вернул -12? Я неправильно фиксирую возвращаемое значение?
В 32-разрядной версии x86 linux% eax содержит либо возвращаемое значение, либо значение отрицательной ошибки в случае ошибки.
Смотрите, например 4,4 или же 3.3 и 3.4.
Возвращаемое значение -12 из системного вызова означает, что функция завершилась сбоем, и значение errno должно быть равно 12, что по крайней мере в моей системе соответствует ENOMEM
,
Похоже, что Strace услужливо переводит это для вас. Если вы хотите, чтобы ваше приложение работало в строгом режиме, вы должны выполнить тест и перевод, аналогичные приведенным в 4,4:
if ((unsigned long)(x) >= (unsigned long)(-2048)) {
printf("syscall failed. errno = %ld\n", -(res));
}
Других решений пока нет …