Что происходит, если в программе сборки нет системного вызова exit?
В программе сборки .text
загружен в 0x08048000
, .data
а также .bss
раздел приходит после этого.
Что будет, если я не положу exit
системный вызов в .text
раздел? Приведет ли это к .data
а также .bss
интерпретировать как код, вызывающий "непредсказуемые" результаты? Когда программа завершится - возможно, после выполнения каждой "инструкции"?
Я могу легко написать программу без exit
системный вызов, но если тестирование .data
а также .bss
исполниться - это то, чего я до сих пор не знаю, потому что, думаю, мне нужно было бы знать настоящий машинный код, который генерируется изнутри, чтобы высмеивать это.
Я думаю, что этот вопрос больше касается того, как процессор и операционная система будут обрабатывать такой сценарий, чем язык ассемблера.
2 ответа
Процессор не знает, где заканчивается ваш код. Он точно выполняет одну инструкцию за другой, пока выполнение не будет перенаправлено в другое место (например, с помощью перехода, вызова, прерывания, системного вызова или подобного). Если ваш код заканчивается без перехода в другое место, процессор продолжает выполнять все, что находится в памяти после вашего кода. Что именно происходит, довольно непредсказуемо, но в конечном итоге ваш код обычно дает сбой, потому что он пытается выполнить недопустимую инструкцию или пытается получить доступ к памяти, к которой у него нет доступа. Если ни того, ни другого не происходит и скачок не происходит, в конечном итоге процессор пытается выполнить несопоставленную память или память, помеченную как "не исполняемая", как код, что вызывает нарушение сегментации. В Linux это вызывает SIGSEGV
или же SIGBUS
, Когда они не обрабатываются, они завершают ваш процесс и при необходимости создают дампы ядра.
Чтобы устранить эту проблему, см. раздел Ошибка сегментации Nasm на RET в _start, где описаны правильные способы выполнения системного вызова выхода в 32- или 64-битном коде x86 для Linux или Windows. (Точка входа не является функцией в Linux; указатель стека указывает наargc
, а не обратный адрес, поэтому тоже не работает.) Для других ISA или ОС проверьте их руководства или посмотрите существующие примеры выхода.
См. также Какова правильная константа для системного вызова выхода?ре:#include <sys/syscall.h>
в.S
файл, чтобы получить константы, такие какSYS_exit
( илиSYS_exit_group
) в Unix-подобных ОС.
Или в ассемблерах, которые не могут напрямую использовать заголовки C, найдитеasm/unistd.h
в Linux; для x86-64 против -32 см.unistd_64.h
против.unistd_32.h
. (И/или ознакомьтесь с этими вопросами и ответами, чтобы узнать об инструментах для создания.inc
файлы из заголовков C, содержащие только константы, что также полезно для получения таких вещей, какO_RDWR
илиMAP_PRIVATE
константы для аргументов системного вызова.)
Если вы используете какие-либо функции libc, особенно
printf
, вам следует
call exit
для очистки буферов stdio. Илиret
отmain
, нет_start
и пусть код запуска CRT вызываетexit
. Смотрите также
- В Linux x64 в чем разница между системным вызовом, int 0x80 и ret для выхода из программы?
- Реализация системного вызова exit() - в современном GNU/Linux,
call _exit()
используетexit_group
системный вызов. Толькоpthread_exit
на самом деле использует__NR_exit
.
Как объясняет @fuz , выполнение будет продолжаться с любыми следующими байтами в памяти, где ваш исполняемый файл загружается/сопоставляется. ЦП не знает, где закончился ваш источник, он просто извлекает и декодирует байты из памяти.
Часто в конце есть несколько нулевых байтов заполнения..text
раздел. На 32-битной версии x86 декодируется какadd [eax], al
, добавление места назначения в памяти. Егоadd [rax], al
в 64-битном режиме. Это приведет к ошибке 1 , если RAX не указывает на страницу, доступную для записи.
RISC-V специально выбрал свои коды операций, поэтому00 00 00 00
(и00 00
сжатые инструкции) будут недействительными инструкциями, которые выдают ошибку, определенно не NOP, поэтому области заполнения нулями не могут работать как салазки NOP для эксплойтов, отправляющих выполнение рядом, а не точно в те байты, которые они хотят выполнить. Некоторые другие RISC действительно выполняют нулевые байты в качестве инструкции NOP или безошибочной инструкции ALU.
Если выполнение превысит любые или ненулевые байты мусора, находящиеся в памяти, в конечном итоге оно перейдет на несопоставленную или неисполняемую страницу. Это также приведет к ошибке недопустимой страницы, как и при доступе к данным по неверному указателю, поэтому вы также получаете SIGSEGV в Unix-подобных системах.
(Или на примитивных процессорах без защиты памяти указатель инструкции может переноситься. Например, в 8086 выборка кода из CS:IP оборачивает IP, не затрагивая CS, поэтому выполнение неявно зацикливается на области размером 64 КБ, если это все прямой код без переходов. .)
Если вам интересно, запустите отладчик и посмотрите на дизассемблирование сбойной инструкции и шестнадцатеричный дамп ее машинного кода, если вы распознаете в ней данные ASCII или00
прокладка. ( Также не помещайте данные на путь выполнения .)
Сноска 1: аппаратное исключение -> программное обеспечение
Исключением процессора x86 является#PF
, ошибка страницы. ЦП запустит обработчик ошибок страниц ядра, который проверяет, следует ли разрешить процессу доступ к этому виртуальному адресу.
Если это так, он может скопировать при записи или выделить новую страницу или просто настроить запись в таблице страниц для «подключения» страницы из кэша страниц в адресное пространство процесса, то есть незначительная или программная ошибка страницы. Или выполните ввод-вывод, чтобы получить страницу с диска (серьезная или жесткая ошибка страницы).
Но в данном случае мы говорим о странице, которую процесс не сопоставил, поэтому это ошибка недопустимой страницы. Обработчик ошибок страниц ядра выдаст сигнал ошибки сегментации, если это Unix-подобная ОС, или сделает что-то подобное для других ОС, таких как Windows.
Действием по умолчанию для сигнала является завершение вашего процесса, если он не настроил обработчик для этого сигнала. Не то чтобы вам следовало бы; если только вы не знаете, почему ваш процесс должен повышатьSIGSEGV
, обычно это не подлежит восстановлению .
Обратите внимание, что «ошибка сегментации» не связана с сегментами x86, такими как CS и DS, поскольку современные ОС используют подкачку для защиты памяти, а не сегменты x86. 32-битный или 64-битный процесс в Linux x86 работает с базовым значением CS = 0/лимит = неограниченный. Название историческое.