2017-01-09 1 views
5

Я узнал о макете памяти пользовательского пространства Linux в системах x86_64 и хотел напечатать некоторые адреса из некоторых разделов. Я использовал этот код: RustПочему мой исполняемый файл Rust отображается на такие высокие адреса (около стека) вместо 0x400000?

fn main() { 
    let x = 3;  // should be stored on stack 
    let s = "hello"; // should be in the .data section 

    println!("stack ≈ {:p}", &x); 
    println!(".text ≈ {:p}", main as *const()); 
    println!(".data ≈ {:p}", s); 


    use std::io; 

    let mut f = std::fs::File::open("/proc/self/maps").unwrap(); 
    let out = io::stdout(); 
    io::copy(&mut f, &mut out.lock()).unwrap(); 
} 

Этот код также печатает файл /proc/self/maps на стандартный вывод. Я скомпилировал этот файл mem.rs просто с rustc mem.rs. Он напечатал:

stack ≈ 0x7ffffbf82f2c 
.text ≈ 0x7f45b7c0a2b0 
.data ≈ 0x7f45b7c4d35b 

7f45b6800000-7f45b6c00000 rw-- 00000000 00:00 0 
7f45b6de0000-7f45b6f9a000 r-x- 00000000 00:00 664435    /lib/x86_64-linux-gnu/libc-2.19.so 
7f45b6f9a000-7f45b6fa2000 ---- 001ba000 00:00 664435    /lib/x86_64-linux-gnu/libc-2.19.so 
[ ... more .so files] 
7f45b7a22000-7f45b7a23000 r--- 00022000 00:00 663920    /lib/x86_64-linux-gnu/ld-2.19.so 
7f45b7a23000-7f45b7a24000 rw-- 00023000 00:00 663920    /lib/x86_64-linux-gnu/ld-2.19.so 
7f45b7a24000-7f45b7a25000 rw-- 00000000 00:00 0 
7f45b7aa0000-7f45b7aa2000 rw-- 00000000 00:00 0 
7f45b7ab0000-7f45b7ab2000 rw-- 00000000 00:00 0 
7f45b7ac0000-7f45b7ac1000 rw-- 00000000 00:00 0 
7f45b7ad0000-7f45b7ad1000 rw-- 00000000 00:00 0 
7f45b7ae0000-7f45b7ae2000 rw-- 00000000 00:00 0 
7f45b7c00000-7f45b7c5f000 r-x- 00000000 00:00 1134580   /home/lukas/tmp/mem 
7f45b7e5e000-7f45b7e62000 r--- 0005e000 00:00 1134580   /home/lukas/tmp/mem 
7f45b7e62000-7f45b7e63000 rw-- 00062000 00:00 1134580   /home/lukas/tmp/mem 
7f45b7e63000-7f45b7e64000 rw-- 00000000 00:00 0 
7ffffb784000-7ffffb785000 ---- 00000000 00:00 0     [stack] 
7ffffb785000-7ffffbf84000 rw-- 00000000 00:00 0 
7ffffc263000-7ffffc264000 r-x- 00000000 00:00 0     [vdso] 

По крайней мере, адрес я отпечатанный на моем, кажется, соответствует тому, что maps говорит. Но когда я исполню cat /proc/self/maps в терминале, я получаю этот выход:

00400000-0040b000 r-x- 00000000 00:00 107117      /bin/cat 
0060a000-0060b000 r--- 0000a000 00:00 107117      /bin/cat 
0060b000-0060c000 rw-- 0000b000 00:00 107117      /bin/cat 
0071c000-0073d000 rw-- 00000000 00:00 0       [heap] 
7f7deb933000-7f7debc30000 r--- 00000000 00:00 758714    /usr/lib/locale/locale-archive 
7f7debc30000-7f7debdea000 r-x- 00000000 00:00 664435    /lib/x86_64-linux-gnu/libc-2.19.so 
7f7debdea000-7f7debdf2000 ---- 001ba000 00:00 664435    /lib/x86_64-linux-gnu/libc-2.19.so 
[ ... more .so files ...] 
7f7dec222000-7f7dec223000 r--- 00022000 00:00 663920    /lib/x86_64-linux-gnu/ld-2.19.so 
7f7dec223000-7f7dec224000 rw-- 00023000 00:00 663920    /lib/x86_64-linux-gnu/ld-2.19.so 
7f7dec224000-7f7dec225000 rw-- 00000000 00:00 0 
7f7dec250000-7f7dec252000 rw-- 00000000 00:00 0 
7f7dec260000-7f7dec261000 rw-- 00000000 00:00 0 
7f7dec270000-7f7dec272000 rw-- 00000000 00:00 0 
7ffff09e8000-7ffff11e8000 rw-- 00000000 00:00 0     [stack] 
7ffff1689000-7ffff168a000 r-x- 00000000 00:00 0     [vdso] 

Последний результат соответствует все, что я читал об этой теме: разделы из исполняемого файла отображается в нижней части виртуального адресного пространства (начало около 0x400000).

Я выполнил и скомпилировал все в подсистеме Linux для Windows (в основном, Ubuntu 14.04). Я знаю, это новое и прочее, но я уверен, что это не проблема с подсистемой (скажите, пожалуйста, если это так!). Ржавчина 1,14, что вопросы (я сомневаюсь),

Я также попробовал то же самое с программой C (простите, что я, вероятно, плохо C):

#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <stdlib.h> 

int main(int argc, char **argv) { 
    FILE *test_file; 
    char buf[4096]; 
    if ((test_file = fopen ("/proc/self/maps", "r")) != NULL) { 
     while (!feof (test_file)) { 
      fgets (buf, sizeof (buf), test_file); 
      puts (buf); 
     } 
    } 

    return 0; 
} 

Он выводит что-то похожее на cat:

00400000-00401000 r-x- 00000000 00:00 1325490     /home/lukas/tmp/a.out 
00600000-00601000 r--- 00000000 00:00 1325490     /home/lukas/tmp/a.out 
00601000-00602000 rw-- 00001000 00:00 1325490     /home/lukas/tmp/a.out 

Почему исполняемый файл Rust отображается на большие адреса возле стека?

+2

Для чего это стоит, Running код Rust в Ubuntu 16.04 Докер образ, я получаю 'стек ≈ 0x7ffc2a01636c'; '.text ≈ 0x559f58ae4270'; '.data ≈ 0x559f58b2735b'. Это не так близко к стеку. – Shepmaster

ответ

6

Используя rustc -Z print-link-args addr.rs, вы можете увидеть, какой вызов компоновщика использует компилятор Rust. Поскольку текущий компоновщик - это cc, мы можем напрямую использовать эти опции для программы C. Не обращая внимания на несущественные аргументы и удаление другим один за одним, я был оставлен с этим компилятором вызова:

gcc -fPIC -pie addr.c -o addr-c 

компиляции кода C, как это производит подобные адреса как Руст-скомпилированный исполняемый файл, указывающий, что один или оба из тех, варианты являются вероятным виновником. Это меняет вопрос на «почему -fPIC и/или -pie соответствуют таким высоким адресам?»

Я нашел другой вопрос and answer, который, кажется, чтобы пролить свет на что:

PIE двоичный файл связан только как разделяемая библиотека, и поэтому его адрес загрузки по умолчанию (.p_vaddr первого LOAD сегмента) является нуль. Ожидается, что что-то переместит эту бинарную страницу с нулевой страницы и загрузит ее на какой-то случайный адрес.

Использование readelf -e на исполняемом Rust, мы можем видеть, что первый LOAD сегмент действительно есть виртуальный адрес нуля:

Program Headers: 
    Type   Offset    VirtAddr   PhysAddr 
       FileSiz   MemSiz    Flags Align 
    LOAD   0x0000000000000000 0x0000000000000000 0x0000000000000000 
       0x000000000005e6b4 0x000000000005e6b4 R E 200000 
    LOAD   0x000000000005ead0 0x000000000025ead0 0x000000000025ead0 
       0x00000000000039d1 0x00000000000049e8 RW  200000 

Я думаю, что это то меняет вопрос «почему эти случайные выбранных адресов ", но я не уверен в этом ответе.^_^A hunch говорит мне, что ASLR вступает в игру. Это other answer, кажется, подтверждает, что из:

PIE является поддержка ASLR в исполняемых файлах.

ASLR - это техника безопасности, помогающая упростить программы против определенных типов атак, поэтому имеет смысл, что Rust с ее безопасным подходом попытается включить что-то подобное по умолчанию. Действительно, адреса изменить малый бит каждый вызов:

[email protected]:/# ./addr | grep 'r-xp' | grep 'addr' 
5587cea9d000-5587ceafc000 r-xp 00000000 00:21 206      /addr 
561d8aae2000-561d8ab41000 r-xp 00000000 00:21 206      /addr 
555c30ffd000-555c3105c000 r-xp 00000000 00:21 206      /addr 
55db249d5000-55db24a34000 r-xp 00000000 00:21 206      /addr 
55e988572000-55e9885d1000 r-xp 00000000 00:21 206      /addr 
560400e1b000-560400e7a000 r-xp 00000000 00:21 206      /addr 
+0

Не будет ли ASLR выдавать другой адрес за каждое исполнение? –

+0

@ MatthieuM. Мне было интересно узнать об объеме ASLR. Я получаю несколько разные адреса каждый раз, но на моей машине он всегда начинается с 0x7f (никогда не что-то вроде 0x55, как в комментарии Shepmaster выше). В любом случае, отличный ответ, Shepmaster! –

+0

@ MatthieuM. Я получаю разные адреса с каждым исполнением, которое я обновил. – Shepmaster

Смежные вопросы