2017-01-03 24 views
-2

Я пытаюсь написать функцию strlen в сборе с использованием 64-разрядного GAS. Мне нужно получить строку ввода от пользователя и напечатать его длину. Это мой код:scanf a string и print strlen в сборочном газе 64-разрядный

.lcomm d2, 255 
.data 
pstring1: .ascii "%s\0\n" 

.text 
.globl main 
main: 
    movq %rsp, %rbp 

    subq $8, %rsp 
    movq $d2, %rsi 
    movq %rsi,%rbx   
    movq $pstring1, %rdi 
    movq $0,%rax 
    call scanf 

    movq $1, %rax 
    movq $d2, %rsi 
    movq $pstring1, %rdi 
    call printf #print to check if scanf worked write 

    add $8, %rsp 

    movq 8(%rsp), %rcx 
    movq %rcx, d2 
    call pstrlen 
    popq %rbx 
    ret 

    ########## 
pstrlen: 

    movq %rsp, %rbx 
    movq 16(%rbp),%rdx 
    xor %rax, %rax   
    jmp if 

then: 
    incq %rax 
    movq $length,%rax 
if: 
    movq %rdx, %rcx 
    cmp 0, %rcx 
    jne then 
end: 
    pop %rbp 
    ret 

Если кто-то может объяснить, давая пример того, как работать со строками и передать параметры функции в 64-битном ГАЗОВОЙ сборки было бы идеально, так как я ничего не могу найти подходящий онлайн ,

+0

Как любезно, пожалуйста, исправить форматирование, если вы видите, что выходит перепутались. Нажмите ссылку [edit] (http://stackoverflow.com/posts/41452817/edit) под сообщением и используйте кнопку «образец кода» на панели инструментов. Что касается вашей проблемы, лучше прокомментируйте свой код, опишите действительное и ожидаемое поведение и научитесь использовать отладчик. – Jester

+0

Я не проверял себя, насколько это хорошо, но просто чтобы сделать ваше заявление * «ничего подходящего онлайн» * смешно, например: https://www.youtube.com/playlist?list=PLKK11Ligqiti8g3gWRtMjMgf1KoKDOvME (одна целая часть посвященный газу, и вы должны стараться смотреть на других, вероятно, тоже, из названий он выглядит как хорошие основы, вступительные в то, что вы должны понимать, прежде чем пытаться изучить синтаксис газа x86-64). – Ped7g

+0

извините! Я попытался добавить commetd, но они просто ушли, и как-то я не могу больше редактировать, некоторые ошибки. –

ответ

0

На принципиальном уровне вы используете .lcomm d2, 255 для выделения 255 байтов для строковых данных. Один байт - 8 бит, 1 бит - 0 или 1. Таким образом, максимальное значение одного байта равно 2 -1 при обработке двоичного значения без знака. Что для меня наиболее распространенным способом, как я думаю о байтах (как число 0..255), но эти 8 бит могут представлять и другие значения, например, используется иногда подписанный 8-бит (-128..+127) или конкретные биты, функциональность для доступа к конкретному коду. (Эта часть хорошо)

Затем вы используете scanf с "%s\0\n" определения (он будет компилировать как байты '%', 's', 0, 10 ... не уверен, что 10 хорошо для там после нулевого терминатора). Вместо этого я использовал бы .asciiz "%254s", чтобы злоумышленник не ввел более 255 байтов ввода в это зарезервированное пространство d2. (обратите внимание, что это .asciiz с z в конце, поэтому он добавит нулевой байт по своему усмотрению)

Затем вы используете printf. Скорее предоставим другую строку форматирования отдельно для вывода, на этот раз, как formatOut: .asciiz "%s\n".

Наконец-то вы хотите strlen.

Это значит, что я вернусь обратно на вход. Если вы работаете в обычной 64-битной ОС (linux), ваша строка ввода очень вероятна в кодировке UTF-8 (если ваша ОС не установлена ​​в другом конкретном локали, то я не уверен, какой Locale будет принимать scanf).

Кодирование UTF-8 кодируется по переменной длине, поэтому вы должны решить, будет ли ваш strlen возвращать количество символов или количество занятых байтов.

Для простоты я предполагаю, что для вас достаточно количества байтов (не символов), и если ваши входные строки будут состоять только из основных символов ASCII 7b ([0-9A-Za-z [email protected]#$%^&*,.;'\<>?:"|{}] и т. Д. ... проверьте любую таблицу ASCII ... нет акцента разрешенные символы (например, á), что приведет к созданию многобайтового кода UTF8), тогда количество байтов будет равно числу символов (кодировка UTF-8 является совместимой с 7b ASCII).

Это означает, например, для ввода "Hell 1234" память по адресу d2 будет содержать эти значения (шестнадцатеричные) 48 65 6C 6C 20 31 32 33 34 00. Еще раз, если вы проверите таблицу ASCII, вы поймете, что, например, байтом 0x20 является символ пробега и т. Д. И строка «nul terminated», последнее значение ноль является частью строки, но это не отображается, вместо этого он используется различными функциями C как «конец маркера строки».

Итак, что вы хотите сделать в strlen, - это загрузить некоторые регистры с адресом d2, скажем rdi. И затем сканирование байта байтом (байт, поскольку кодировка ASCII работает в режиме «1 char = 1 байт», и мы будем игнорировать коды переменной длины UTF-8), пока вы не достигнете нулевого значения в памяти, а между тем подсчитайте, сколько байтов для этого потребовалось.Если вы подумаете над этой идеей немного, чтобы сделать ее «коротким» для процессора, и вы будете использовать SCASB для сканирования (вы также можете записать ее «вручную» с обычным mov/cmp/inc/jne/jnz, если хотите), вы можете завершить это:

rdi = d2 address 
rdx = rdi ; (copy of d2 address) 
ecx = 255 ; maximum length of string 
al = 0 ; value to test against 
repne scasb ; repeat SCASB instruction until zero is found 
; here rdi points at the zero byte 
; (or it's d2+255 if the zero terminator is missing) 
rdi -= rdx ; rdi = length of string 
; return result as you wish 

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

Затем вы можете написать инструкции, которые дают разумные расчеты на основе этих данных.

В вашем случае вычисление «length_of_string = количество ненулевых байтов в 7b закодированной в ASCII строке, хранящейся в памяти по адресу d2» (я имею в виду после успешной части кода scanf).

Учитывая, как выглядит ваш источник, мне кажется, что вы не понимаете, что такое инструкция по процессору x86, и вы просто скопируете их из некоторых примеров. Это скоро вызовет у вас неприятности.

Например, cmp 0, %rcx проверяет, является ли rcx (8 байтов «широким» значением) равна нулю. И вы загрузили rcx со значением от rdx, что было чем-то из стека (возможно, d2 адрес), поэтому rcx никогда не будет равен нулю.

И даже если вы на самом деле загружает символьные значения из памяти в rcx, вы нагрузили бы 8 из них в то же время, так что вы пропустили бы значение 0, как это будет только один байт внутри некоторого мусора, как 0xCCCCCCCC00343332 (Я использую 0xCC для неопределенной памяти после d2 буфера, например, может быть любое значение).

Так что код не имеет никакого смысла. Если вы хотя бы частично понимаете, что такое регистры процессора и какие инструкции, например, mov/inc/cmp/..., то у вас есть шанс создать рабочий код, просто используя отладчик, чтобы проверить почти каждые 1-2 новых инструкций, добавленных в исходный код, если он манипулирует правильные значения и исправить их, пока не получите правильное значение.

Для этого необходимо, чтобы у вас было четкое представление о том, что такое «правильное поведение»! (как в этом случае "выборка байтов по байтам от d2 адрес, один за другим, увеличение счетчика длины и поиск нулевого байта). Таким образом, вы можете узнать, когда код делает то, что вам нужно, или нет.


Я хотел бы указать на этот ответ: сами инструкции, хотя и важны, менее важны, чем ваше видение используемых данных/структур/алгоритмов. Ваш вопрос звучит так, будто вы понятия не имеете, что такое «C string "в сборке x86 или какой алгоритм использовать. Это делает невозможным просто« угадать »некоторые инструкции в исходном коде, а затем проверить, правильно ли вы догадались. Потому что вы не можете сказать, что вы хотите. Вот почему я сказал, что вы должны проверить также не связанные с газом ресурсы сборки x86 для самых основ, что такое бит/байт/compu ter memory/etc ... вверх до тех пор, пока вы не поймете, какие числовые значения обрабатываются, например, для создания «строк».

После того, как вы будете иметь хорошее представление о том, что он должен сделать, это будет легко для вас, чтобы поймать в отладочных вещи, как поменялись местами аргументы (например: movq %rcx, d2 - почему вы положили 8 байт из rcx в память по адресу d2 Что? перезаписывает входную строку) и тому подобное, поэтому вам не нужно понимать инструкции и синтаксис газа на 100% хорошо, достаточно, чтобы что-то создать, а затем несколько итераций, чтобы «исправить» его.Как проверить представление регистра + памяти, реализуя rcx не изменилась, но вместо того, чтобы строковые данные были повреждены => попробовать это другой способ ...


О, и я совсем забыл ... Вам нужно найти документацию для вашей 64-битной платформы ABI, чтобы вы знали, что это правильный способ передать аргументы в функции C.

Например, в Linux эти уроки могут помочь: http://cs.lmu.edu/~ray/notes/gasexamples/

И поиск здесь слово «ABI» для дополнительных ресурсов: https://stackoverflow.com/tags/x86/info

+0

большое спасибо за этот ответ! Только один маленький вопрос, как я делаю scanf для всей строки? Я понял, как получить одиночный символ или int, но все же возникла проблема с строкой scanf как более одного символа. –

+0

Как я понимаю, наиболее распространенным способом является использование .asciz «% c \ n», но все же он получает только первый символ, а не всю строку. Есть ли другой вариант, потому что мне нужно выделить для строки 255 байт без использования динамического выделения. –

+0

Эта строка формата ''% 254s'' говорит ['scanf'] (http://www.cplusplus.com/reference/ cstdio/scanf /) для чтения целого единственного слова (с использованием 255 байт буфера не более (254 + nul terminator)). Чтобы получить целую строку, вы можете использовать 'gets', а точнее [' fgets'] (http://www.cplusplus.com/reference/cstdio/fgets/), чтобы вы могли указать максимальную длину ввода ('fgets' can эксплуатироваться злоумышленником). Пожалуйста, используйте обычные справочники C (C++) для любых проблем с функциями C. '% c' - одиночный символ в строке форматирования. – Ped7g