x86 - GDT - Может ли G CC обрабатывать разные сегменты (для кода и данных)? - PullRequest
0 голосов
/ 06 марта 2020

У меня есть общий вопрос c о GDT и G CC. Начали писать ядро ​​ОС с целью изучения. В настоящее время я использую G CC для компиляции кода. В нескольких руководствах по настройке GDT одни и те же базовые адреса (0) и пределы (0xFFFFF) используются для сегмента кода и данных. Моя первая мысль о x86 newb ie была такова: если я использую разные базовые адреса и ограничения, это может быть дополнительной защитой.

Вот что я пробовал (сокращенно):

Сценарий компоновщика:

ENTRY(_start)

SECTIONS {
    . = 1M;

    _kern_start = .;

    .text  ALIGN(4096) :  {
        _kern_text_start = .;
        *(.multiboot)
        *(.text)
        _kern_text_end = .;

    }

    .rodata ALIGN(4096) : {
        _kern_rodata_start = .;
        *(.rodata)
        _kern_rodata_end = .;
    }




    .data  ALIGN(4096):  {
        _kern_data_start = .;
        *(.data)
        _kern_data_end = .;
    }



    .bss ALIGN(4096) :  {
        _kern_bss_start = .;
        *(.bss)
        _kern_bss_end = .;
    }




    .stack ALIGN(4096) :  {
        _kern_stack_start = .;
        *(.stack)
        _kern_stack_end = .;
    }



    .heap  ALIGN(4096) :  {
        _kern_heap_start = .;
        *(.heap)
        _kern_heap_end = .;
    }

    _kern_end = .;
}

Я добавил символы для каждого раздела, затем написал простые функции Assembler , чтобы получить начальный адрес и размер для каждого раздела, который я вызывал в C:

Функции ассемблера (как пример):

FUNCTION(_kern_text_get_addr)
    pushl %ebp
    movl %esp, %ebp
    movl $_kern_text_start, %eax
    leave
    ret

FUNCTION(_kern_text_get_size)
    pushl %ebp
    movl %esp, %ebp
    movl $_kern_text_start, %ebx
    movl $_kern_text_end, %eax
    sub %ebx, %eax
    leave
    ret

Я использовал различные разделы для настройки кода и данных ( не показан в следующем фрагменте кода) в сегменте GDT :

uint32_t base;
uint32_t limit;

base = _kern_text_get_addr();
limit = _kern_text_get_size() / 4096;

/* Kernel Code */
gdt_set_entry(&gdt[GDT_KERN_CODE], base, limit, GDT_ACCESS_EXEC | 
                                                GDT_ACCESS_SEGMENT | 
                                                GDT_ACCESS_RING0 | 
                                                GDT_ACCESS_PRESENT, 
                                                GDT_FLAG_SIZE | 
                                                GDT_FLAG_GRAN);

Загрузка с помощью инструкции ассемблера lgdt работает. Но когда я делаю sh сегментный регистр с длинным прыжком, я получаю ошибку общей защиты ( # GP ). Итак, я изучил сгенерированный машинный код. Проблема заключается в том, что, когда я компилирую его с G CC с опциями по умолчанию, адрес перехода команды прыжка в длину не является правильным. Для перехода в нужное место требуется какой-то перевод адреса. Также сегменты данных используют неправильные адреса. Хорошо, я могу вместо этого использовать пейджинг, но даже если вопрос звучит глупо:

Возможно ли использовать разные сегменты для кода и данных с G CC или другими словами, может G CC обрабатывать разные сегменты? Я знаю, что в G CC есть аргумент PIC, но еще не пробовал.

1 Ответ

4 голосов
/ 06 марта 2020

За одним незначительным исключением G CC может обрабатывать отдельные сегменты кода и данных без проблем. Единственное, что я знаю об этих перерывах, - это батуты, созданные, когда берется адрес вложенной функции. Эти батуты создаются в стеке, но выполняются как код, поэтому не будут работать, если код должен находиться в другом сегменте, чем данные. Поскольку вложенные функции являются редко используемым расширением G CC, на практике это не должно вызывать проблем. Вы все еще можете заставить его работать, предоставляя средства для динамического выделения и инициализации памяти в сегменте кода во время выполнения.

Однако в этом нет никакого преимущества. Вам придется разделить 32-битное линейное адресное пространство 4G на два отдельных непересекающихся сегмента кода и данных, чтобы получить какое-либо преимущество в безопасности. Тем не менее, вы можете получить то же преимущество безопасности, используя биты защиты страницы без выполнения без необходимости фрагментации линейного адресного пространства. Вот почему все современные операционные системы используют модель плоской сегментации, в которой как код, так и сегменты данных имеют основание 0 и ограничение 4G для 32-разрядного кода. По той же причине 64-разрядные процессоры x86 не дают вам другого выбора, кроме как использовать плоскую модель в 64-разрядном режиме.

Позиционирующий независимый код не зависит от сегментации и любых проблем, с которыми вы сталкиваетесь инструкция прыжка в длину (дальнего перехода) не будет решена с использованием отдельных сегментов кода и данных.

...