LINUX.ORG.RU

Как выйти из программы?

 , ,


0

1

Текст программы
$ cat test.s

.text

.global _start

_start:
    mov  rax, 0x60
    xor  rdi, rdi
    syscall
    ret
Что происходит:
начинается выполнение, вызывается syscall, он меняет состояние регистров, но возвращается обратно на ret
ret выполняет переход на адрес 0x0 и там наступает Segmentation Fault

Как сделать, чтобы это работало? Что проверить, что поменять?
Где прочитать про требования линукс-загрузчика к программе (хотя бы на какие слова искать)?

Ответ на: комментарий от anonymous

собираю командами

as -msyntax=intel -mnaked-reg -as --gdwarf2 test.s
ld a.out -e_start -o test
запускаю
gdb test
GNU gdb (Gentoo 8.1 p1) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...done.
(gdb) break _start
Breakpoint 1 at 0x400078: file test.s, line 6.
(gdb) run
Starting program: /home/user/asmtest/test 

Breakpoint 1, _start () at test.s:6
6	    mov  %rax, 0x60
(gdb) n
7	    xor  %rdi, %rdi
(gdb) n
8	    syscall
(gdb) n
_start () at test.s:9
9	    ret
(gdb) i r 
rax            0x0	0
rbx            0x0	0
rcx            0x400084	4194436
rdx            0x0	0
rsi            0x0	0
rdi            0x0	0
rbp            0x0	0x0
rsp            0x7fffffffd690	0x7fffffffd690
r8             0x0	0
r9             0x0	0
r10            0x0	0
r11            0x346	838
r12            0x0	0
r13            0x0	0
r14            0x0	0
r15            0x0	0
rip            0x400084	0x400084 <_start+12>
eflags         0x246	[ PF ZF IF ]
cs             0x33	51
ss             0x2b	43
ds             0x0	0
es             0x0	0
fs             0x0	0
gs             0x0	0
(gdb) n
0x0000000000000001 in ?? ()
(gdb) quit
A debugging session is active.

	Inferior 1 [process 25602] will be killed.

Quit anyway? (y or n) y

Einstok_Fair ★★ ()

если ты заглянешь в сорцы libc, то увидишь, что функция main не является ни точкой входа, ни точкой выхода из программы. настоящая точка выхода - функция __libc_start_main, в конце которой примерно такой код:

...
  exit(main(argc, argv, envp));
}

почитай референс по процу, там подробно говорится, что именно делает ret

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

почитай референс по процу, там подробно говорится, что именно делает ret

Как я уже писал выше, в первую очередь мне нужна дока по загрузчику программ в linux. Потому что именно он определяет как программа стартует и должна завершаться. Чтение референса на ret никак не прояснит вопрос со стандартом загрузчика.

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

он явно лишний в твоём коде, особенно после sys_exit

Видите ли, бомбино, изначально там не было sys_exit, там был один ret, но прога уже падала точно так же.

вот тебе лучшая дока

Это детали реализации. Ты хотя бы скажи, сколько там строк суммарно, включая все header-файлы. Я так думаю, что примерно десять тысяч строк. И разбирать это всё только для того, чтобы понять как написать одну команду - крайне неэффективно. Лучше найти спецификацию, где искомое написано явно.

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

там был один ret, но прога уже падала точно так же

прога падала, потому что ret берет значение с верхушки стека и кладет его в eip, после чего поток выполнения переходит по этому адресу. затем прога падала из-за некорректного номера sys_exit

лучшие доки - рефка по твоему процу и исходный код ядра.

ты что вообще сделать хочешь?

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

прога падала, потому что

Да нет же! Прога падала потому что я не знаю требований загрузчика к способу завершения выполнения кода.

ты что вообще сделать хочешь?

хочу эти требования узнать.

лучшие доки - рефка по твоему процу и исходный код ядра

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

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

Единственный корректный путь завершения процеса - вызов sys_exit. Всё. Про ret я написал выше. Я не представляю, что за документ тебе нужен и что ты там планируешь найти. И зачем вообще тебе это могло понадобиться.

anonymous ()
Ответ на: комментарий от Einstok_Fair

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

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

нуу, вообще в процессоре есть и другие механизмы, например прерывания и
https://en.wikipedia.org/wiki/Call_gate_(Intel)

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

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

в процессоре-то они могут и быть, но мы имеем дело с юзерспейсом, а из юзерспейса мы можем только дергать за системные вызовы и ничего более.

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

неубедительно, звучит как «мне кажется, что из юзерспейса мы можем только..», далее по тексту.

Может в Linux это так и есть (но точно неясно), а в другом ядре (на том же процессоре) могло бы быть не так?

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

Может в Linux это так и есть (но точно неясно), а в другом ядре (на том же процессоре) могло бы быть не так?

Конечно! В другом ядре вообще может что угодно. Юзерспейс, процессы, их порождение и завершение - это абстракции, реализованные ядром. В другой операционной системе другие правила, другие названия, другие абстракции и способ с ними взаимодействовать.

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

ну вот, значит и в линуксе тоже может/могло быть что-нибудь такое-эдакое. И тогда нужен способ выяснить как оно было во все времена и что от этого осталось в настоящем.

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

Ну та страница написана молодыми поколениями.
А вот в DOS 1.x было так:
"terminating the program with a RETF instruction, which would pop the saved segment value off the stack and jump to address 0 of the PSP, which contained an INT 20h instruction."
https://en.wikipedia.org/wiki/Program_Segment_Prefix
причём PSP заполнялся операционной системой.

В Linux ничто не мешало вместо PSP сделать какой-нибудь Gate в какой-нибудь таблице процессора.

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

и ? дос ниразу не юникс, напишите свою ОС и сделайте в ней по своей теории
а я вам показал что ответ есть в вики на ваш первоначальный вопрос
вы же развели здесь демагогию

anonymous ()
Ответ на: комментарий от Einstok_Fair

syscall, sysenter, int 0x80 - это механизмы как процессу попасть в ядро. Они определяют ABI kernel<->userspace.

Чтобы завершить процесс нужно вызвать конкретный код ядра по очистке ресурсов. Можно через системные вызовы (sys_exit & co), можно через необрабатываемого сигнала (SIGKILL, SIGSEGV). Семантика очистки ресурсов определяется POSIX (и реализацией, если вызовы нестандартные).

Но код работает на реальном железе. Можно и через iopl() привилегии поднять и через запись через регистр PCI hard reset вызвать.

anonymous ()