LINUX.ORG.RU

Компилирую, запускаю, получаю «trap invalid opcode»

 


0

1

Скачал исходники: https://github.com/mistalro/atariconv Поправил Makefile — заменил LIBS = -lcurses на LIBS = -lncurses -ltinfo, CFLAGS = -g на CFLAGS = -g -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 (что выдал pkg-config для ncurses). Запустил — собралось (GCC 14) с предупреждениями, что несколько int функций ничего не возвращают.

Запускаю программу без параметров — нормально показывает справку. Запускаю более сложную задачу (конвертировать файл) — падает с ошибкой «Недопустимая инструкция», в dmesg в соответствующий момент — «trap invalid opcode». Попробовал явно указывать архитектуру процессора — не помогло. Думал, ассемблерные вставки — не нашёл.

В итоге помогло заменить в мейкфайле GCC = g++ на GCC = gcc. Даже пропали предупреждения про int и return. Поэтому возникли вопросы:

  1. Я правильно понимаю, что g++ компилировал как C++, а gcc скомпилировал как Си? — Ответ: Да.

  2. Из-за чего возникала ошибка? Пыталось исполнять данные как код? — Ответ: G++ в конце не-void функций без return вставляет команду процессора UD2, вызывающую ошибку «неопределенный код операции», если программа дойдёт до этого места.

  3. Какой был смысл указывать дефолтный компилятор g++? Исходники последний раз меняли в 2015 году, в копирайтах указан 2010, а местами вообще 2000 год. С тех пор что-то принципиально поменялось?

UPD: Если в проблемные функции добавить в конце return 0;, то собирается G++ без предупреждений и работает. Корректность особо не проверял.

★★★★★

Последнее исправление: question4 (всего исправлений: 2)
Ответ на: комментарий от shdown

Ты бы хоть файл приложил, который конвертировал.

Содержимое архива https://www.atarimania.com/pgedump.awp?id=3819 Если прямая ссылка не работает, https://www.atarimania.com/game-atari-400-800-xl-xe-oregon-trail-_3819.html

Но как я понял, проблема будет с любым входным файлом.

question4 ★★★★★
() автор топика

В мане написано что g++ линкует с c++ библиотекой по умолчанию (простой gcc нет), а так же компилирует .c/.h файлы как C++. Расширение .cpp в любом случае компилируется как C++.

firkax ★★★★★
()
Ответ на: комментарий от question4
gdb --args <тут обычная командная строка запуска твоей проги>

Потом пишешь «run» в его командной строке, и после краша пишешь «disas» - он покажет асм-дамп места где упало. Ещё есть команда «bt» чтобы показать стек вызовов функций, который привёл к этому месту.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Спасибо.

(gdb) run
Starting program: /home/oleg/atari/atari Oregon\ Trail\ \(The\).bas
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Program received signal SIGILL, Illegal instruction.
0x0000555555555798 in ataribasic_readheader (aptr=0x55555556d2b0) at fileio.c:264
264     aptr->m_starp  = ataribasic_getpointer( aptr );
(gdb) disas
Dump of assembler code for function _Z21ataribasic_readheaderP13ataribasic_st:
   0x0000555555555704 <+0>:     endbr64
   0x0000555555555708 <+4>:     push   %rbp
   0x0000555555555709 <+5>:     mov    %rsp,%rbp
   0x000055555555570c <+8>:     sub    $0x8,%rsp
   0x0000555555555710 <+12>:    mov    %rdi,-0x8(%rbp)
   0x0000555555555714 <+16>:    mov    -0x8(%rbp),%rax
   0x0000555555555718 <+20>:    mov    %rax,%rdi
   0x000055555555571b <+23>:    call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x0000555555555720 <+28>:    mov    -0x8(%rbp),%rdx
   0x0000555555555724 <+32>:    mov    %eax,0x4(%rdx)
   0x0000555555555727 <+35>:    mov    -0x8(%rbp),%rax
   0x000055555555572b <+39>:    mov    %rax,%rdi
   0x000055555555572e <+42>:    call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x0000555555555733 <+47>:    mov    -0x8(%rbp),%rdx
   0x0000555555555737 <+51>:    mov    %eax,0x8(%rdx)
   0x000055555555573a <+54>:    mov    -0x8(%rbp),%rax
   0x000055555555573e <+58>:    mov    %rax,%rdi
   0x0000555555555741 <+61>:    call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x0000555555555746 <+66>:    mov    -0x8(%rbp),%rdx
   0x000055555555574a <+70>:    mov    %eax,0x10(%rdx)
   0x000055555555574d <+73>:    mov    -0x8(%rbp),%rax
   0x0000555555555751 <+77>:    mov    %rax,%rdi
   0x0000555555555754 <+80>:    call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x0000555555555759 <+85>:    mov    -0x8(%rbp),%rdx
   0x000055555555575d <+89>:    mov    %eax,0xc(%rdx)
   0x0000555555555760 <+92>:    mov    -0x8(%rbp),%rax
   0x0000555555555764 <+96>:    mov    %rax,%rdi
   0x0000555555555767 <+99>:    call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x000055555555576c <+104>:   mov    -0x8(%rbp),%rdx
   0x0000555555555770 <+108>:   mov    %eax,0x14(%rdx)
   0x0000555555555773 <+111>:   mov    -0x8(%rbp),%rax
   0x0000555555555777 <+115>:   mov    %rax,%rdi
   0x000055555555577a <+118>:   call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x000055555555577f <+123>:   mov    -0x8(%rbp),%rdx
   0x0000555555555783 <+127>:   mov    %eax,0x18(%rdx)
   0x0000555555555786 <+130>:   mov    -0x8(%rbp),%rax
   0x000055555555578a <+134>:   mov    %rax,%rdi
   0x000055555555578d <+137>:   call   0x5555555556c7 <_Z21ataribasic_getpointerP13ataribasic_st>
   0x0000555555555792 <+142>:   mov    -0x8(%rbp),%rdx
   0x0000555555555796 <+146>:   mov    %eax,(%rdx)
=> 0x0000555555555798 <+148>:   ud2
End of assembler dump.
(gdb) bt
#0  0x0000555555555798 in ataribasic_readheader (aptr=0x55555556d2b0) at fileio.c:264
#1  0x00005555555578ea in ataribasic_listfile (aptr=0x55555556d2b0) at process.c:53
#2  0x0000555555557c37 in process_file (pfilename=0x7fffffffd56e "Oregon Trail (The).bas") at main.c:90
#3  0x0000555555557e5a in process_arguments (argc=2, argv=0x7fffffffd088) at main.c:183
#4  0x0000555555557e95 in main (argc=2, argv=0x7fffffffd088) at main.c:194
(gdb) 

И что из этого можно извлечь?

Сходу вижу, что ataribasic_readheader() — одна из функций типа int, но без return. Она состоит из 7 вызовов ataribasic_getpointer(). Вижу, после последнего вызова ud2. Что это такое? Специальная команда, которая вызывает падение?

question4 ★★★★★
() автор топика
Ответ на: комментарий от pasquale

Иногда компиляторы добавляют неверные опкоды, чтобы не дать выполниться коду с неопределенным поведением.

Когда это происходит? В конце функций без return, где он требуется по мнению компилятора? Что будет, если сменить возвращаемый тип на void?

И какие это опкоды? ud2? Что-то ещё?

question4 ★★★★★
() автор топика

Воспроизвёл. Падение в функции ataribasic_readheader. Компилятор вставил ud2 в конец функции, потому что она возвращает int, но по факту там нет return.

[~/repo/atariconv/src]-[%] valgrind ./atari ~/Downloads/The_Oregon_Trail.zip
==554591== Memcheck, a memory error detector
==554591== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==554591== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
==554591== Command: ./atari /home/v/Downloads/The_Oregon_Trail.zip
==554591== 
==554591== valgrind: Unrecognised instruction at address 0x40015f0.
==554591==    at 0x40015F0: ataribasic_readheader(ataribasic_st*) (fileio.c:264)
==554591==    by 0x400350D: ataribasic_listfile(ataribasic_st*) (process.c:53)
==554591==    by 0x4003845: process_file(char*) (main.c:90)
==554591==    by 0x4003B20: process_arguments(int, char**) (main.c:183)
==554591==    by 0x4003B57: main (main.c:194)

objdump -S ./atari, отрывок:

0000000000001560 <_Z21ataribasic_readheaderP13ataribasic_st>: 
[...]
    15f0:       0f 0b                   ud2

00000000000015f2 <_Z23ataribasic_readvarnamesP13ataribasic_st>:

shdown
()
Ответ на: комментарий от question4

Ну вот, научился.

В K&R void нет и по умолчанию функция возвращает int, если явно нет return то это не запрещено (страница 68, строка 8). Судя по всему в плюсах с этим строже, поэтому тебе пропихивают инструкцию чтобы ты задумался.

Вообще плюсы и си – два разных языка, так что компилять их надо с соответствующими командами. Ну а что там g++ по умолчанию – так это скорее для линкера чтобы плюсовая библиотека и рантайм присобачивались к эльфу.

luke ★★★★★
()
Ответ на: комментарий от luke

В плюсах строже, но плохой компилятор молча вставил краш вместо того чтобы написать варнинг про отсутствующий return. Или варнинг он таки писал, но его никто не читал? Я вот везде -Werror ставлю.

firkax ★★★★★
()
Ответ на: комментарий от firkax

А там точно return 0 нужно или может это единственный return и надо просто тип на void заменить?

В некоторых можно заменить на void, но для некоторых слишком много надо править. По этой же причине я не уверен, что добавление return 0 не внесёт ошибок.

question4 ★★★★★
() автор топика
Ответ на: комментарий от question4

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

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

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

Мда.

ataribasic_readvarvalue() - без return

Код который её использует:

error = ataribasic_readvarvalues( aptr );

if (aptr->options.m_fixvartable )
        {
        ataribasic_varnamefromtype( aptr );
        }

if ( aptr->options.m_showvariables )
        {
        ataribasic_varname_display( aptr );
        }

if ( error )
        {
        ataribasic_error( aptr );
        }

«обработчик ошибок»

void ataribasic_error( ATARIBASIC *aptr )
{
printf( "Error:\n");
}

Итог: если из той функции рандомно вернулся не ноль - то спустя ещё несколько строк кода прога выведет на экран строку «Error:». Если же вернётся 0 - не выведет. Другой разницы нет.

Впрочем, конкретно там, скорее всего, возвращаемое значение скорее всего «пробросится» из последней вызванной функции где return есть. Но делать так не надо. При этом в комментарии написано что функция возвращает количество переменных.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)