Оптимизация GCC вызывает странные проблемы - как мне отладить? - PullRequest
0 голосов
/ 16 августа 2011

У меня проблемы с оптимизацией GCC в программе на Си. Это код x86_64 (C и встроенная сборка, хотя в файле .o нет проблем с asm), работающий на OS X 10.6.

Вот как это выглядит при работе с -O0 против -O2:

$ gcc -m64 -std=gnu99 -o bin/ctr keyschedule.c aes.c ctr.c debug.c misc.c -O0 -Wall
$ bin/ctr -e testing/plaintext -o testing/cipher
encrypt_file called with inpath: testing/plaintext
encrypt_file called with outpath: testing/cipher
file_size called with argument 0x7fff5fbff84b
nonce = 6e32b6797e849081
(... no more output, but it works as it should)

$ gcc -m64 -std=gnu99 -o bin/ctr keyschedule.c aes.c ctr.c debug.c misc.c -O2 -Wall
$ bin/ctr -e testing/plaintext -o testing/cipher
encrypt_file called with inpath: testing/plaintext
encrypt_file called with outpath: testing/cipher
file_size called with argument 0x7fff5f000000
Segmentation fault

... вместе с сообщением об аварийном завершении OS X:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00007fff5f000000
Crashed Thread:  0  Dispatch queue: com.apple.main-thread

Thread 0 Crashed:  Dispatch queue: com.apple.main-thread
0   libSystem.B.dylib               0x00007fff88d3bad8 perror + 52
1   ctr                             0x0000000100001339 file_size + 73 (ctr.c:32)
2   ctr                             0x00000001000017be encrypt_file + 158 (ctr.c:72)
3   ctr                             0x0000000100001bd1 main + 289 (ctr.c:242)
4   ctr                             0x0000000100000bd4 start + 52

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

int main(int argc, char *argv[]) {
...
    if (strcmp(argv[1], "-e") == 0)
        encrypt_file(argv[2], argv[4], key); // It crashes with static paths as well
...
}

void encrypt_file(const char *inpath, const char *outpath, const unsigned char *key) {
    printf("encrypt_file called with inpath: %s\n", inpath);
    printf("encrypt_file called with outpath: %s\n", outpath);

    // Create a pointer to the correct function to use for this CPU
    void (*aes_encrypt)(const unsigned char *, unsigned char *, const unsigned char *);
    if (test_aesni_support()) {
        aes_encrypt = aes_encrypt_aesni;
    }
    else {
        aes_encrypt = aes_encrypt_c;
    }

    unsigned char expanded_keys[176] = {0};
    aes_expand_key(key, expanded_keys);

    off_t size = file_size(inpath);
    if (size <= 0) {
        fprintf(stderr, "Cannot encrypt a file of size zero!\n");
        exit(1);
    }
    ....
}

off_t file_size(const char *path) {
    printf("file_size called with argument %p\n", path);
    struct stat st;
    if (stat(path, &st) != 0) {
        perror(path);
        exit(1);
    }

    return (st.st_size);
}

Я подозреваю, что в моем коде что-то есть (а не предполагается, что ошибка в GCC), но я ничего не могу найти.

Если приведенный выше код выглядит нормально (я не могу себе представить, что проблема в любом другом коде, так как он совершенно не связан с падением), что я должен делать, чтобы отследить это?

GDB:

#0  0x00007fff88d3bad8 in perror ()
(gdb) bt
#0  0x00007fff88d3bad8 in perror ()
#1  0x0000000100001339 in file_size (path=0x7fff5f000000 "") at ctr.c:31
#2  0x00000001000017be in encrypt_file (inpath=0x7fff5f000000 "", outpath=0x7fff5fbff860 "testing/cipher", key=0x7fff5fbff6b0 "-~??9?9>?W\n\021\001?N\026") at ctr.c:72
#3  0x0000000100001bd1 in main (argc=<value temporarily unavailable, due to optimizations>, argv=0x7fff5fbff718) at ctr.c:242
(gdb) frame 2
#2  0x00000001000017be in encrypt_file (inpath=0x7fff5f000000 "", outpath=0x7fff5fbff860 "testing/cipher", key=0x7fff5fbff6b0 "-~??9?9>?W\n\021\001?N\026") at ctr.c:72
72      off_t size = file_size(inpath);
(gdb) l
67      }
68  
69      unsigned char expanded_keys[176] = {0};
70      aes_expand_key(key, expanded_keys);
71  
72      off_t size = file_size(inpath);
73      if (size <= 0) {
74          fprintf(stderr, "Cannot encrypt a file of size zero!\n");
75          exit(1);
76      }

Valgrind:

$ valgrind bin/ctr -e testing/plaintext -o testing/cipher
==1165== Memcheck, a memory error detector
==1165== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==1165== Using Valgrind-3.7.0.SVN and LibVEX; rerun with -h for copyright info
==1165== Command: bin/ctr -e testing/plaintext -o testing/cipher
==1165== 
encrypt_file called with inpath: testing/plaintext
encrypt_file called with outpath: testing/cipher
file_size called with argument 0x7fff5f000000
==1165== Syscall param stat64(path) points to unaddressable byte(s)
==1165==    at 0x2BB52: stat$INODE64 (in /usr/lib/libSystem.B.dylib)
==1165==    by 0x10000179D: encrypt_file (ctr.c:73) // this is the call to file_size() in encrypt_file()
==1165==    by 0x100001BB0: main (ctr.c:243)
==1165==  Address 0x7fff5f000000 is not stack'd, malloc'd or (recently) free'd
==1165== 
file_size: Bad address
==1165== 
==1165== HEAP SUMMARY:
==1165==     in use at exit: 4,184 bytes in 2 blocks
==1165==   total heap usage: 2 allocs, 0 frees, 4,184 bytes allocated
==1165== 
==1165== LEAK SUMMARY:
==1165==    definitely lost: 0 bytes in 0 blocks
==1165==    indirectly lost: 0 bytes in 0 blocks
==1165==      possibly lost: 0 bytes in 0 blocks
==1165==    still reachable: 4,096 bytes in 1 blocks
==1165==         suppressed: 88 bytes in 1 blocks
==1165== Rerun with --leak-check=full to see details of leaked memory
==1165== 
==1165== For counts of detected and suppressed errors, rerun with: -v
==1165== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Редактировать: Заменено encrypt_file () на версию без ограничений (до момента сбоя), добавлена ​​информация GDB и заменены выходные данные аварийного репортера OS X выходными данными из запуска двоичного файла -g.

Редактировать 2: добавлен вывод valgrind.

Ответы [ 2 ]

3 голосов
/ 16 августа 2011

Похоже, что ваша переменная inpath перезаписывается (частично);Я подозреваю, что если вы удалите вызов на aes_expand_key, чтобы он успешно справился с проблемой.

Не видя aes_expand_key, я собираюсь догадаться, что у него есть встроенная сборка, и вы не правильносохранение хотя бы одного регистра, который должен сохраняться в соответствии с ABI вашей целевой платформы.В случае -O0 inpath, вероятно, остается в стеке и поэтому не подвержено влиянию, в то время как в случае -O2 оно, вероятно, хранится в регистре, младшие 32 бита которого разбиваются на aes_expand_key.

0 голосов
/ 16 августа 2011

Звучит так, будто у вас где-то происходит что-то вроде памяти. Аргумент path в file_size явно является недопустимым указателем, когда он туда попадает.

Если то, что вы опубликовали для encrypt_file, - это все, что происходит в этой функции до вызова file_size, то виновник почти наверняка aes_expand_key - вы уверены, что передаете буфер правильного размера, равный 176 байт? Если этот буфер слишком мал, aes_expand_key перезапишет другие данные в стеке, включая локальную переменную inpath. Попробуйте увеличить размер expanded_keys; проверьте документацию по aes_expand_key, чтобы узнать, насколько большой он должен быть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...