Обе команды mov
и nop
являются инструкциями. Команда - это то, что исполняет процессор и составляет машинную программу. Если вы не знакомы с этой концепцией, может быть полезно прочитать учебное пособие по программированию на ассемблере.
Какие инструкции использует функция, в основном не зависит от количества аргументов и локальных переменных, которые она имеет. Наличие nop
и некоторых mov
инструкций ничего не говорит вам об аргументах и переменных функции.
Что вам говорит, так это то, что операнды имеют эти инструкции. Если вы не знаете, что такое операнды или как инструкции x86 используют свои операнды, я должен еще раз попросить вас обратиться к учебнику, поскольку это выходит за рамки этого вопроса.
Общий подход к идентификации аргументов функции проверяет, какие регистры, сохраненные вызывающим абонентом, используются функцией без предварительного присвоения им значения. Хотя это не является надежным способом, обычно это лучший heuristi c.
В вашей функции используются сохраненные вызывающим регистры регистры rdi
, rsi
и rax
, Из них только оригинальное значение rdi
влияет на функцию. Что касается rsi
и rax
, функция перезаписывает их исходное значение, даже не глядя на него. Таким образом, они вряд ли будут аргументами функции (rax
никогда не используется для аргумента функции в соглашении о вызовах SysV). Следовательно, функция, вероятно, имеет один аргумент в rdi
. Я не вижу доступа к слотам стека, выделенным вызывающей стороной, поэтому маловероятно, что там будут скрыты какие-либо дополнительные аргументы.
Возможно, функция написана так, чтобы иметь аргументы в rsi
или некоторые другие регистры и эти аргументы просто остались неиспользованными. Мы никогда не узнаем наверняка без дополнительной информации (например, символы отладки, дизассемблирование сайта вызова и т. Д. c.).
Что касается локальных переменных: в общем, нет способа восстановить, какие локальные переменные a C функция, используемая, когда она была скомпилирована в сборку, потому что компилятор может оптимизировать локальные переменные до такой степени, что их существование невозможно распознать. Также могут быть добавлены дополнительные локальные переменные для различных целей.
Однако в вашем конкретном случае c вполне вероятно, что функция была скомпилирована с отключенными оптимизациями. В этом случае многие C компиляторы компилируют код C очень простым и предсказуемым образом, где один слот стека выделяется для каждой локальной переменной, и каждый доступ к локальной памяти генерирует одну загрузку или память в этот слот стека.
Однако все еще невозможно с абсолютной уверенностью сказать, какие типы могли иметь эти переменные, или если два соседних слота стека являются двумя отдельными переменными, одной переменной особенно большого типа (например, long double
) или переменной структуры или типа массива. Мы снова никогда не узнаем.
В вашем примере два слота стека по 8 байт каждый выделяются инструкцией sub $0x10, %rsp
. Поскольку компилятор должен выделять слоты стека с шагом 16 байт для выравнивания, это означает, что исходная функция имеет как минимум одну переменную (64-битного типа), но может иметь целых девять (остальные - типа char
) .
Поскольку доступ к одному из слотов стека (-0x8(%rbp)
) заканчивается, мы можем только сказать, что функция имеет хотя бы одну переменную. Поскольку доступ осуществляется с шириной 64 бита, вполне вероятно, что указанная переменная имеет тип шириной 64 бита. Функция может иметь дополнительные неиспользуемые локальные переменные или ее переменная может быть структурой с несколькими членами или массивом, каждый из которых доступен только для первого члена. Мы не можем сказать наверняка.
Также возможно, что не существует локальной переменной, и компилятор решил использовать -0x8(%rbp)
, чтобы пролить какое-то выражение по какой-то причине (ему нравится делать бессмысленные разливы, подобные этой, когда оптимизация выключены), но это маловероятно.
Итак, подведем итог: как правило, невозможно судить по машинному коду точно, как выглядит функция C, но вы часто можете сделать обоснованное предположение, которое продвинет вас довольно далеко.
Следовательно, как правило, более полезно думать о том, «как может выглядеть функция C с этим машинным кодом?» а не «как выглядела функция C, которая генерировала этот машинный код?» как никогда нельзя быть уверенным.