Как мне получить полный код сборки из файла C?

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

Я использую язык C несколько лет, но у меня мало опыта работы с языком ассемблера.

Мне удалось вывести ассемблерный код с помощью опции -S в gcc. Однако полученный ассемблерный код содержал инструкции вызова, которые, в свою очередь, выполняли переход к другой функции, такой как _exp. Это не то, что я хотел, мне нужен был полностью функциональный ассемблерный код в одном файле, не зависящий от другого кода.

Возможно ли достичь того, что я ищу?

Чтобы лучше описать проблему, я показываю вам свой код здесь:

#include <math.h>
float sigmoid(float i){
    return 1/(1+exp(-i));
}

Платформа, над которой я работаю, - это 64-разрядная версия Windows 10, я использую компилятор cl.exe из MSbuild.

Моей первоначальной целью было увидеть, на самом низком уровне, как компьютеры вычисляют математические функции. Уровень, на котором я решил наблюдать за процессом вычисления, - это ассемблерный код, а математическая функция, которую я выбрал, была сигмоидной, определенной выше.

Возможно objdump?

David Wohlferd 03.06.2018 00:27

Как правило, это невозможно при вызове внешних функций. Код этих функций неизвестен компилятору C, и он не может сгенерировать для них сборку. Чего вы пытаетесь достичь?

fuz 03.06.2018 00:33

Скорее всего, код C содержал вызовы функций, которые отражены в ассемблере. Не видя кода C, конечно, трудно быть уверенным.

Jonathan Leffler 03.06.2018 00:34

Если вам нужны внешние функции, вы должны либо пройти обычную компоновку (и, таким образом, ограничиться дизассемблированием, которое сохраняет меньше информации), либо использовать оптимизацию всей программы ... а реализации libc, как правило, не поддерживают WPO.

o11c 03.06.2018 00:56

Хотя в идеале язык ассемблера имеет отношение один к одному с машинным кодом (для инструкций, довольно много, esp с кодом, сгенерированным компилятором, это не инструкции, а директивы, метки, псевдокод и т. д.). но, как вы, возможно, видели или увидите, это не всегда так. язык ассемблера определяется ассемблером, программа, не ожидается, будет универсальной для цели, поэтому вы столкнетесь с этим, разные ассемблеры имеют разный синтаксис. затем есть набор инструкций, перегруженные инструкции, такие как mov в x86, которые приводят к множеству различных возможных инструкций машинного кода.

old_timer 03.06.2018 05:33

а затем, если это связанная программа, существует множество / десятки / сотни различных файлов, которые были собраны, а затем связаны для создания вашей программы, в зависимости от того, сколько вызовов библиотеки вы делаете, если таковые имеются. Итак, как упоминал Дэвид выше, вы можете дизассемблировать, что при условии, что разборка хороша (для наборов инструкций переменной длины, таких как x86, не ожидается, что она будет идеальной), вы получите реальный машинный код, фактическую выбранную инструкцию, за исключением риска неудачная или вводящая в заблуждение разборка, разборка - лучшее, что есть, чтобы увидеть, что происходит.

old_timer 03.06.2018 05:35

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

old_timer 03.06.2018 05:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
1 202
2

Ответы 2

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

Обычно ваша программа C, ваша точка входа main () - это только процент кода. Существует загрузочная программа, которая содержит фактическую точку входа для операционной системы для запуска вашей программы, она выполняет некоторые действия, которые подготавливают ваше пространство виртуальной памяти для запуска вашей программы. Нули .bss и другие подобные вещи. это часто и должно быть написано на языке ассемблера (в противном случае вы получите проблему с курицей и яйцом), но не файл языка ассемблера, который вы увидите, если вы не найдете исходники для библиотеки C, вы часто получите объект как часть набор инструментов вместе с другими библиотеками компилятора и т. д.

Затем, если вы выполняете какие-либо вызовы C или создаете код, который приводит к вызову библиотеки компилятора (выполнение разделения на платформе, которая не поддерживает деление, выполнение операций с плавающей запятой на платформе, которая не имеет плавающей запятой и т. д.), Это еще один объект, полученный из какой-либо другой C или сборка, которая является частью библиотеки или исходных кодов компилятора и не является чем-то, что вы увидите во время процесса компиляции / сборки / связывания (цепочка в цепочке инструментов).

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

Если не baremetal, то, конечно, есть уровень операционной системы, который вы наверняка не увидите как часть исходного кода, в конечном итоге вызовы библиотеки C, которые нуждаются в системе, будут иметь место, где они это делают, все скомпилированы в object / lib перед их использованием, а исходные коды сборки для стороны операционной системы являются частью какого-то другого источника и процесса сборки где-то еще.

Теперь я понимаю, что, если я не работаю на платформе baremetal, как правило, невозможно получить полный код сборки, который не выполняет вызов другой функции. Спасибо за ваш ответ.

Kinnefix Kim 07.06.2018 08:42

_exp - это стандартная функция математической библиотеки double exp(double); очевидно, вы находитесь на платформе, которая добавляет перед именами символов C. ведущее подчеркивание.

Учитывая .s, который вызывает некоторые библиотечные функции, создайте его так же, как и файл .c, который вызывает библиотечные функции:

gcc foo.S -o foo  -lm

По умолчанию вы получите динамический исполняемый файл.


Но если вы действительно хотите, чтобы код все был в одном файле без внешних зависимостей, вы можете связать свой .c со статическим исполняемым файлом и разобрать его.

gcc -O3 -march=native foo.c -o foo -static -lm
objdump -drwC -Mintel foo > foo.s

Нет никакой гарантии, что реализация _exp в libm.a (статическая библиотека) идентична той, которую вы получили бы в libm.so или libm.dll или чем-то еще, потому что это другой файл. Это особенно верно для такой функции, как memcpy, где уловки динамического компоновщика часто используются для выбора оптимальной версии (для вашего процессора) во время выполнения.

Я попробовал ваш метод, но в коде сборки все еще есть инструкция вызова. Платформа, над которой я работаю, - cygwin, x86_64. Я должен был предоставить код C, который я использую. Я обновлю свой вопрос, как только получу доступ к моему компьютеру.

Kinnefix Kim 04.06.2018 01:57

Конечно, он содержит инструкции call между функциями. Но я думаю, вы имеете в виду внешнюю DLL? Вы не можете создавать статические исполняемые файлы Windows, потому что не существует стабильного ABI системного вызова. Единственный стабильный способ использования API Win32 / NT - это вызовы функций DLL.

Peter Cordes 04.06.2018 02:03

Думаю, я не уточнил, чего пытаюсь достичь. Прошу прощения :(. Я добавлю еще несколько объяснений своей проблемы.

Kinnefix Kim 07.06.2018 08:23

@KinnefixKim: Если вы статически связали libm, этот ответ сработает. В противном случае вы можете дизассемблировать реализацию _exp в своей математической библиотеке или выполнить ее пошагово, выполняя свой код внутри отладчика. См. Также Как убрать "шум" из вывода сборки GCC / clang?.

Peter Cordes 07.06.2018 09:21

@KinnefixKim: Или, в частности, об экспоненциальной функции, см. Самая быстрая реализация экспоненциальной функции с помощью SSE для обсуждения эффективных реализаций x86. Вопросы и ответы по SO о низкоуровневых реализациях exp будут в основном касаться SIMD, потому что обычные математические библиотеки уже обрабатывают скаляр. Вы можете скомпилировать C со встроенными функциями в asm, но каждая из этих функций _mm_ является внутренней для одной машинной инструкции. Настоящая уловка для exp заключается в использовании формата экспоненты / мантиссы IEEE float и полиномиального приближения.

Peter Cordes 07.06.2018 09:25

Другие вопросы по теме