2015-05-17 2 views
2

Я пытаюсь получить небольшую часть программы MIPS с приветственным миром, запущенной в симуляторе Gem 5. Программа была скомпилирована с gcc 4.9.2 и glibc 2.19 (построена crosstool-ng) и хорошо работает в qemu, но она разбилась с ошибкой страницы (пытается получить доступ к адресу 0) в gem5.Инструкции по странной нагрузке, создаваемые mipsel-gcc при компиляции glibc

Код довольно прост:

#include <stdio.h> 
int main() 
{ 
    printf("hello, world\n"); 
    return 0; 
} 

file ./test результат:

./test: ELF 32-бит LSB исполняемым, MIPS, MIPS-я версия 1, статически связаны между собой, для GNU/Linux 3.15.4, не разделяется

После некоторой отладки с помощью gdb я выяснил, что ошибка страницы вызвана _dl_setup_stack_chk_guard функция в glibc. Он принимает указатель на пустоту, называемый _dl_random, который передается функцией __libc_start_main, которая является NULL. Однако, насколько мне известно, эти функции никогда не разыгрывают указатель, но были созданы команды для загрузки значений из памяти указателей _dl_random. Некоторые куски кода могут помочь понять:

в функции __libc_start_main (макро THREAD_SET_STACK_GUARD не установлен):

/* Initialize the thread library at least a bit since the libgcc 
    functions are using thread functions if these are available and 
    we need to setup errno. */ 
    __pthread_initialize_minimal(); 

    /* Set up the stack checker's canary. */ 
    uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); 
# ifdef THREAD_SET_STACK_GUARD 
    THREAD_SET_STACK_GUARD (stack_chk_guard); 
# else 
    __stack_chk_guard = stack_chk_guard; 
# endif 

в функции _dl_setup_stack_chk_guard (всегда встраиваются):

static inline uintptr_t __attribute__ ((always_inline)) 
_dl_setup_stack_chk_guard (void *dl_random) 
{ 
    union 
    { 
    uintptr_t num; 
    unsigned char bytes[sizeof (uintptr_t)]; 
    } ret = { 0 }; 

    if (dl_random == NULL) 
    { 
     ret.bytes[sizeof (ret) - 1] = 255; 
     ret.bytes[sizeof (ret) - 2] = '\n'; 
    } 
    else 
    { 
     memcpy (ret.bytes, dl_random, sizeof (ret)); 
#if BYTE_ORDER == LITTLE_ENDIAN 
     ret.num &= ~(uintptr_t) 0xff; 
#elif BYTE_ORDER == BIG_ENDIAN 
     ret.num &= ~((uintptr_t) 0xff << (8 * (sizeof (ret) - 1))); 
#else 
# error "BYTE_ORDER unknown" 
#endif 
    } 
    return ret.num; 
} 

разборку код:

0x00400ea4 <+228>: jal 0x4014b4 <__pthread_initialize_minimal> 
    0x00400ea8 <+232>: nop 
    0x00400eac <+236>: lui v0,0x4a 
    0x00400eb0 <+240>: lw v0,6232(v0) 
    0x00400eb4 <+244>: li a0,-256 
    0x00400eb8 <+248>: lwl v1,3(v0) 
    0x00400ebc <+252>: lwr v1,0(v0) 
    0x00400ec0 <+256>: addiu v0,v0,4 
    0x00400ec4 <+260>: and v1,v1,a0 
    0x00400ec8 <+264>: lui a0,0x4a 
    0x00400ecc <+268>: sw v1,6228(a0) 
  • 0x4a1858 (0x4a0000 + 6232) является адресом _dl_random
  • 0x4a1854 (0x4a0000 + 6228) является адресом __stack_chk_guard

ошибка страницы происходит при 0x00400eb8. Я не совсем понимаю, как генерируются команды 0x00400eb8 и 0x00400ebc. Может ли кто-нибудь пролить свет на него, пожалуйста? Благодарю.

+0

Очевидно, компилятор считает, что 'dl_random' не может быть' NULL'. Проверьте источник '_dl_random'. – Jester

+0

@Jester Спасибо за ваш ответ. Вот объявление '_dl_random':' extern void * _dl_random attribute_hidden attribute_relro'. Копая дальше, я понял, что 'attribute_relro' фактически ссылается на' __attribute__ ((раздел (.data.rel.ro))). Может ли это означать, что компилятор уверен, что '_dl_random' указывает на раздел данных? – skies457

+0

Это просто означает, что '_dl_random' сам находится в разделе' .data.rel.ro', он ничего не говорит о том, на что указывает. Не совсем понятно, почему компилятор предположил, что это не NULL. –

ответ

1

Вот как я нашел корень этой проблемы и мое предложение для решения.

Я думаю, что полезно погрузиться в the Glibc source code, чтобы увидеть, что на самом деле происходит. Начиная с _dl_random или __libc_start_main оба являются ОК.

Поскольку значение _dl_random неожиданно NULL, нам нужно найти способ инициализации этой переменной и where it is assigned. С помощью инструментов анализа кода мы можем найти _dl_random в Glibc присваивается значимым значением в функции _dl_aux_init, и эта функция вызывается __libc_start_min.

_dl_aux_init выполняет итерации по своему параметру - auxvec - и действует, соответствующее auxvec[i].at_type. AT_RANDOM - это случай для назначения _dl_random. Поэтому проблема заключается в том, что не существует элемента AT_RANDOM, чтобы назначить _dl_random.

Поскольку программа хорошо работает в пользовательском режиме qemu, корень этой проблемы находится в поставщике системной среды, скажем, gem5, который несет ответственность за построение auxvec. Имея это ключевое слово, мы можем обнаружить, что auxv построен в gem5/src/arch/<arch-name>/process.cc.

Текущая auxv для MIPS строится, как показано ниже:

// Set the system page size 
    auxv.push_back(auxv_t(M5_AT_PAGESZ, MipsISA::PageBytes)); 
    // Set the frequency at which time() increments 
    auxv.push_back(auxv_t(M5_AT_CLKTCK, 100)); 
    // For statically linked executables, this is the virtual 
    // address of the program header tables if they appear in the 
    // executable image. 
    auxv.push_back(auxv_t(M5_AT_PHDR, elfObject->programHeaderTable())); 
    DPRINTF(Loader, "auxv at PHDR %08p\n", elfObject->programHeaderTable()); 
    // This is the size of a program header entry from the elf file. 
    auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize())); 
    // This is the number of program headers from the original elf file. 
    auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount())); 
    //The entry point to the program 
    auxv.push_back(auxv_t(M5_AT_ENTRY, objFile->entryPoint())); 
    //Different user and group IDs 
    auxv.push_back(auxv_t(M5_AT_UID, uid())); 
    auxv.push_back(auxv_t(M5_AT_EUID, euid())); 
    auxv.push_back(auxv_t(M5_AT_GID, gid())); 
    auxv.push_back(auxv_t(M5_AT_EGID, egid())); 

Теперь мы знаем, что делать. Нам просто нужно предоставить доступное значение адреса _dl_random с тегами MT_AT_RANDOM. ARM-арка Gem5 реализует это уже (code). Возможно, мы можем взять это в качестве примера.

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