2015-09-30 3 views
1

У меня есть код сборки, который вызывает другой, используя callq. При вызове retq программа вылетает с ошибкой сегментации.Ошибка сегментации сегментации во время retq

.globl main 
main:     # def main(): 
    pushq %rbp  # 
    movq %rsp, %rbp # 

    callq input  # get input 
    movq %rax, %r8 

    callq r8_digits_to_stack 
    # program is not getting here before the segmentation fault 
    jmp  exit_0 

# put the binary digits of r8 on the stack, last digit first (lowest) 
# uses: rcx, rbx 
r8_digits_to_stack: 
    movq %r8, %rax  # copy for popping digits off 

    loop_digits_to_stack: 
     cmpq $0, %rax # if our copy is zero, we're done! 
     jle  return 

     movq %rax, %rcx # make another copy to extract digit with 
     andq $1, %rcx # get last digit 
     pushq %rcx  # push last digit to stack 
     sarq %rax  # knock off last digit for next loop 
     jmp  loop_digits_to_stack 

# return from wherever we were last called 
return: 
    retq 

# exit with code 0 
exit_0: 
    movq $0, %rax # return 0 
    popq %rbp 
    retq 

Где input является функцией С, которая возвращает ввод с клавиатуры, чтобы %rax.

Я предполагаю, что это может иметь какое-то отношение к тому факту, что я манипулирую стеком, например, в этом случае?

+0

Спасибо за ссылку, я скоро обновлю! – Langston

+0

удалил мой старый комментарий, вы действительно сказали, где ваш код неисправен. –

ответ

1

Я думаю, что один из ваших обратных путей не pop rbp. Оставьте только

pushq %rbp 
movq %rsp, %rbp 

pop  %rbp 

всего. По умолчанию gcc - .

Или исправьте путь с обратным нулем, а также нажмите rbp.

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

Я рекомендую не загружать обратный адрес в регистр и заменять более поздний retjmp *%rdx или что-то в этом роде. Это позволит отказаться от логики прогнозирования адреса/возврата адреса в современных процессорах и вызвать срыв так же, как неверный прогноз отрасли. (См. http://agner.org/optimize/). ЦП ненавидят несоответствующий вызов/ret. Я не могу найти конкретную страницу, чтобы связать это прямо сейчас.

См. https://stackoverflow.com/tags/x86/info для других полезных ресурсов, включая документацию ABI о том, как функции обычно принимают аргументы.


Вы можете скопировать адрес возврата вниз ниже массива вы просто толкнул, а затем запустить ret, чтобы вернуться с% РСП модифицирован. Но, если вам не нужно вызывать длинную функцию из нескольких сайтов, возможно, лучше просто вставить ее в один или два вызова.

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

put args in some registers 
    lea .ret_location(%rip), %rbx 
    jmp my_weird_helper_function 
.ret_location: # in NASM/YASM, labels starting with . are local labels, and don't show up in the object file. 
     # GNU assembler might only treat symbols starting with .L that way. 
    ... 


my_weird_helper_function: 
    use args, potentially modifying the stack 
    jmp *%rbx # return 

Вам нужна действительно веская причина использовать что-то вроде этого. И вам придется обосновать/объяснить это с большим количеством комментариев, потому что не, что читатели будут ожидать. Прежде всего, что вы собираетесь делать с этим массивом, который вы вложили в стек? Вы собираетесь найти его длину, вычитая rsp и rbp или что-то еще?

Интересно, что хотя push должен изменить rsp, а также сделать магазин, он имеет одну пропускную способность на каждый процессор на всех последних процессорах. У процессоров Intel есть механизм стека, чтобы заставить операторы стека не ждать, пока rsp будет вычислен в двигателе внешнего порядка, когда он будет изменен только push/pop/call/ret. (микширование push/pop с mov 4(%rsp), %rax или что-то другое, вставляемое в дополнительные файлы для синхронизации RPS-сервера с смещением стекового двигателя.) Процессоры Intel/AMD могут делать только одно хранилище за такт в любом случае, но Intel SnB и более поздние могут выходить дважды за такт.

Таким образом, push/pop фактически не является ужасным способом реализации структуры данных стека, особенно. на Intel.


Кроме того, ваш код структурирован причудливо. main() разделен на r8_digits_to_stack. Это нормально, но вы не воспользовались тем, что проваливались из одного блока в другой блок, поэтому он просто стоит вам jmp в main без каких-либо преимуществ и огромного недостатка читаемости.

Давайте сделаем вид, что ваш цикл является частью main, так как я уже говорил о том, как супер-странно иметь функцию возврата с измененным% rsp.

Ваша петля может быть и проще. Стройте вещи с помощью jcc до верха, когда это возможно.

Существует небольшое преимущество в том, чтобы избегать верхних 16 регистров: 32-разрядные insns с классическими регистрами не нуждаются в префиксном байте REX. Так что давайте сделаем вид, что у нас есть только начальное значение в% rax.

digits_to_stack: 
# put each bit of %rax into its own 8 byte element on the stack for maximum space-inefficiency 

    movq %rax, %rdx # save a copy 

    xor %ecx, %ecx # setcc is only available for byte operands, so zero %rcx 

    # need a test at the top after transforming while() into do{}while 
    test %rax, %rax # fewer insn bytes to test for zero this way 
    jz .Lend 

    # Another option can be to jmp to the test at the end of the loop, to begin the first iteration there. 

.align 16 
.Lpush_loop: 
    shr $1, %rax # shift the low bit into CF, set ZF based on the result 
    setc %cl  # set %cl to 0 or 1, based on the carry flag 
    # movzbl %cl, %ecx # zero-extend 
    pushq %rcx 
     #.Lfirst_iter_entry 
     # test %rax, %rax # not needed, flags still set from shr 
    jnz .Lpush_loop 
.Lend: 

Эта версия до сих пор своего рода сосет, потому что на семействах процессоров Intel P6/SnB, используя более широкий регистр после записи меньшую часть приводит к замедлению. (срыв на pre-SnB или дополнительный uop на SnB и позже). Другие, включая AMD и Silvermont, не отслеживают частичные регистры отдельно, поэтому запись в% cl имеет зависимость от предыдущего значения% rcx. (запись в 32-битные регистры нуля 32, что позволяет избежать проблемы зависимостей с частичной регистрацией.) movzx для нулевого расширения от байта до долгого времени будет делать то, что Sandybridge делает неявным, и дать ускорение для старых процессоров.

Это не будет работать в течение одного цикла на итерации Intel, но может на AMD. mov/and $1 не плохо, но and влияет на флаги, что затрудняет цикл только на основе shr установки флагов.

Обратите внимание, что ваша старая версия sarq %rax сдвигает знаковые биты, не обязательно нули, поэтому с отрицательным вводом ваша старая версия будет циклом inf (и segfault, когда вы закончите пространство стека (push попытается записать в неотображенный страница)).

+0

О, я пытаюсь использовать 'return' для возврата из вызова' r8_digits_to_stack' и 'exit_0' для выхода из программы. Разве это не правильный способ сделать это? – Langston

+0

Невозможно, чтобы функция помещала вещи в стек и использовала другую функцию позже? Извините за недостаток знаний, спасибо за вашу помощь! – Langston

+0

@ Langston: Хорошо, я только скомплектовал ваш код. Да, используйте 'ret' для выхода из main (или сделать системный вызов' exit', если вы пишете автономный код asm, который не использует файлы запуска C. ('Gcc -nostartfiles' или просто' as && ld')). Но см. Обновление моего ответа о том, как вы используете стек. Я думаю, что когда вы пытаетесь «ret» в конце 'r8_digits_to_stack', указатель стека не указывает на обратный адрес. Нет, это не нормально. –

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