strace shows wrong system call

Всем привет

Хочу закрыть этот баг, но не могу разобраться в чем дело.
https://bugzilla.novell.com/show_bug.cgi?id=352419

Дело в том, что на 32bit машинах вторым системным вызовом идет fork(), а на 64bit - sys_open().

Однако этот код запускает fork() на обеих платформах:

 #include <stdio.h>
 #include <unistd.h>
 
 int main ()
 {
     printf("--------------------\n");
     __asm__("movl $2, %eax; int $0x80");
     printf("[i am %d]\n", getpid());
 }

Однако... если мы посмотрим вывод strace, то увидим, что на 64bit действительно запускается open(), а на 32bit fork()

Debian, x86_64:

> gcc asm.c -o asm && ./asm
--------------------
[i am 4628]
[i am 4629]
 
> strace ./asm
execve("./asm", ["./asm"], [/* 41 vars */]) = 0
brk(0)                                  = 0x823000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bbfa2000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=111792, ...}) = 0
mmap(NULL, 111792, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa5bbf86000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\355\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1432968, ...}) = 0
mmap(NULL, 3541032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa5bba26000
mprotect(0x7fa5bbb7e000, 2093056, PROT_NONE) = 0
mmap(0x7fa5bbd7d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157000) = 0x7fa5bbd7d000
mmap(0x7fa5bbd82000, 18472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa5bbd82000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bbf85000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bbf84000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bbf83000
arch_prctl(ARCH_SET_FS, 0x7fa5bbf84700) = 0
mprotect(0x7fa5bbd7d000, 16384, PROT_READ) = 0
mprotect(0x7fa5bbfa4000, 4096, PROT_READ) = 0
munmap(0x7fa5bbf86000, 111792)          = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 5), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bbfa1000
write(1, "--------------------\n", 21--------------------
)  = 21
open(0xffffffff, O_RDONLY|O_SYNC|O_NOFOLLOW|O_CLOEXEC|0xbbf00000) = 4635
getpid()                                = 4634
[i am 4635]
write(1, "[i am 4634]\n", 12)           = ? ERESTARTSYS (To be restarted)
--- SIGCHLD (Child exited) @ 0 (0) ---
write(1, "[i am 4634]\n", 12[i am 4634]
)           = 12
exit_group(12)                          = ?

CentOS, x86:

> gcc asm.c -o asm && ./asm
--------------------
[i am 5163]
[i am 5162]
 
> strace ./asm
execve("./asm", ["./asm"], [/* 22 vars */]) = 0
brk(0)                                  = 0x9a32000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=40948, ...}) = 0
mmap2(NULL, 40948, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7ef8000
close(3)                                = 0
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\340_\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1686224, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7ef7000
mmap2(NULL, 1410500, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x110000
mmap2(0x263000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x152) = 0x263000
mmap2(0x266000, 9668, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x266000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7ef6000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7ef66c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x263000, 8192, PROT_READ)     = 0
mprotect(0x79e000, 4096, PROT_READ)     = 0
munmap(0xb7ef8000, 40948)               = 0
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) = 0xb7f01000
write(1, "--------------------\n", 21--------------------
)  = 21
fork()                                  = 5192
[i am 5192]
getpid()                                = 5191
write(1, "[i am 5191]\n", 12[i am 5191]
)           = 12
exit_group(12)

У меня в openSUSE (x86_64), в файле /usr/src/linux/include/asm-x86/unistd_64.h, стоит второй вызов - sys_open(), так почему же все таки запускается fork()? Есть у кого какие соображения на данный счет?

Это похоже

Я кстати

Я кстати вычитал в AMD64 ABI Draft что :
1. User-level applications use as integer registers for passing the sequence
%rdi, %rsi, %rdx, %rcx, %r8 and %r9. The kernel interface uses %rdi,
%rsi, %rdx, %r10, %r8 and %r9.
2. A system-call is done via the syscall instruction. The kernel destroys
registers %rcx and %r11.
3. The number of the syscall has to be passed in register %rax.
4. System-calls are limited to six arguments, no argument is passed directly on
the stack.
5. Returning from the syscall, register %rax contains the result of the
system-call. A value in the range between -4095 and -1 indicates an error,
it is -errno.
6. Only values of class INTEGER or class MEMORY are passed to the kernel.
Т.е. проблема в использовании старого механизма (legacy int0x80 call gate, TIF_32BIT thread flags is set?)
на "новой" платформе.

Это не баг, это

Это не баг, это фича. В X86_64 существует 4 способа вызвать системный сервис: int80, sysenter, syscall32 и syscall. Так вот, первые 3 из них ведут к интерфейсам, эмулирующим x86_32 в 64-битном окружении (т.е. IA32e, CONFIG_IA32_EMULATION). Соответственно, для них используются таблица ia32_sys_call_table, в то время как для нативного 64-битного режима syscall'а используется таблица sys_call_table. В этом и отличие -- первые 3 ведут к таблице, схожей с 32х битной. Последняя - ведёт к 64-битной таблице.

Для CONFIG_X86_32 смотреть сюда:
arch/x86/kernel/entry_32.S (символы ia32_sysenter_target для sysenter, system_call для int80)

Для CONFIG_X86_64 смотреть сюда:
arch/x86/kernel/entry_64.S (символ system_call для syscall)

Кроме того, при CONFIG_X86_64 и CONFIG_IA32_EMULATION смотреть ещё сюда:
arch/x86/ia32/ia32entry.S (символы ia32_sysenter_target для sysenter, ia32_cstar_target для syscall32 и ia32_syscall для int80)

... и никакой магии :)

stace

Это интересно, я не знал про специальный "syscall" вызов.

Но вопрос все же в том, почему strace показывает не тот вызов? Работа strace основанна на ptrace(), т.е. он должен точно показывать какие вызовы делает процесс. Факт в том, что запускается fork(), и мы это видим, но... strace словно заглядывает в не в ту таблицу syscall'ов и ищет по id-вызова.

Хочется разобраться до конца.

Ну так если int80

Ну так если int80 используется, то в нумерации системных вызовов нет разницы. В 32-битном режиме будет использоваться таблица sys_call_table, а в 64-битном -- ia32_sys_call_table. Номера в них совпадают. Посмотрите, в arch/x86/ia32/ia32entry.S есть точка входа ia32_syscall, это обработчик для int 80 в режиме эмуляции.