2015-07-11 4 views
2

Почему printf() выполняет sys_mmap(), а затем копирует содержимое строки в кусках (1024) в новое адресное пространство для sys_write()?Почему mmap выполняется во время вызовов printfs?

Показатель простой статической программы «привет» показан ниже.

> gcc -o hello -static hello.c 
> strace ./hello 


execve("./hello", ["./hello"], [/* 71 vars */]) = 0 
uname({sys="Linux", node="Kumar", ...}) = 0 
brk(0)         = 0x1ce8000 
brk(0x1ce91c0)       = 0x1ce91c0 
arch_prctl(ARCH_SET_FS, 0x1ce8880)  = 0 
readlink("/proc/self/exe", "/home/admin/hello", 4096) = 18 
brk(0x1d0a1c0)       = 0x1d0a1c0 
brk(0x1d0b000)       = 0x1d0b000 
access("/etc/ld.so.nohwcap", F_OK)  = -1 ENOENT (No such file or directory) 
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 28), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feda2130000 
write(1, "Hello", 5Hello)     = 5 
exit_group(0)       = ? 
+++ exited with 0 +++ 

Objdump из rodata

> objdump -s --start-address=0x4935a0 ./hello | head -5 

./hello:  file format elf64-x86-64 

Contents of section .rodata: 
4935a0 01000200 48656c6c 6f006c69 62632d73 ....Hello.libc-s 

Если подключить адрес sys_write() системного вызова на уровне ядра, мы видим, что адрес передается к нему из ттар - адресной области. Разве это не просто трата нового адресного пространства, учитывая, что строка уже выходит в разделе .rodata в первом загружаемом сегменте двоичного файла. Имеет ли он что-то отношение к NO write permissions и т. Д.? Тогда почему бы не заставить компилятор помещать строку в .data раздел (который также доступен для записи) на первом месте?

UPDATE:

ММАП-эд адрес действительно для sys_write() которая может быть проверена в более простой способ, когда мы делаем строку большего размера (скажем, ~ 1500 символов). GDB подтвердит адрес данных печатается [Обратите внимание на вторую точку останова]

(gdb) c 
Continuing. 
Hello World hhhhhhhhhhalhfafeuirafheuhrgiegieguehguergjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqwwwwwwwwwwwwwwwwwwwwww  pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuiiiiiiiiiiiiiiiiiiiiiiiiiiiiiwqiuwqiuwiquwiqhchasnvjnavjanvjdanvjdanvjdanjfanvjaddijuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuquweuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunnnnnnnnnnnnnnnnnnnnnnnnnnnzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,,,,,,,,,,,,,,,,,,,,,, 
Breakpoint 1, _IO_new_file_write (f=0x6b8300 <_IO_2_1_stdout_>, data=0x7ffff7ffc000, n=706) at fileops.c:1257 
1257 { 

ответ

5

Вы пробовали использовать отладчик?

$ gdb /tmp/hello 
... 
(gdb) b __mmap 
Breakpoint 1 at 0x4152e0  
(gdb) r 
Starting program: /tmp/hello 

Breakpoint 1, 0x00000000004152e0 in mmap64() 
(gdb) bt 
#0 0x00000000004152e0 in mmap64() 
#1 0x000000000045d73c in _IO_file_doallocate() 
#2 0x0000000000401fec in _IO_doallocbuf() 
#3 0x000000000042ca10 in _IO_new_file_overflow() 
#4 0x000000000042be9d in _IO_new_file_xsputn() 
#5 0x000000000040111d in puts() 
#6 0x00000000004004de in main() at hello.c:4 
(gdb) c 
Continuing. 
Hello, w 
[Inferior 1 (process 4294) exited with code 011] 

Так выделяет память для забуференного ввода-вывода, который FILE* использования. Обратите внимание, что использование printf только с постоянной строкой вызовет вызов puts, потому что GCC достаточно умен. И puts(string) на самом деле fputs(string, stdout), где stdout FILE*.

Использование сырой записи, однако не берет на себя такое поведение:

#include <unistd.h> 

int main() { 
    write(1, "Hello, w\n", sizeof("Hello, w\n")); 
} 
+0

Спасибо большое! Но я не совсем понял это. Вы хотите сказать, что mmap-ed адрес stdout (FILE *)? –

+0

Нет, 'mmap' используется из' malloc', вызываемого из 'fopen' или' printf' или 'puts'; 'malloc' используется для выделения буфера данных' stdout'. –

+0

[См. Мое обновление] Новая смонтированная область действительно предназначена для хранения данных. Обратите внимание, что происходит, когда строка становится больше. Параметр data указывает на адрес MMAP. Поэтому мой вопрос остается в силе. –