C memcpy () функция - PullRequest
       63

C memcpy () функция

5 голосов
/ 11 ноября 2009

Есть ли метод для расчета размера функции? У меня есть указатель на функцию, и я должен скопировать всю функцию, используя memcpy. Я должен распределить немного пространства и знать 3-й параметр memcpy - size. Я знаю, что sizeof(function) не работает. Есть ли у вас какие-либо предложения?

Ответы [ 15 ]

23 голосов
/ 11 ноября 2009

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

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

8 голосов
/ 11 ноября 2009

Он не дает прямого ответа на ваш вопрос, но вы не должны реализовывать обратные вызовы из кода ядра в пространство пользователя .

Внедрение кода в пространство ядра - это , а не отличный обходной путь .

Лучше представлять барьер пользователя / ядра как межпроцессный барьер. Передайте данные, а не код, назад и вперед между четко определенным протоколом через символьное устройство. Если вам действительно нужно передать код , просто поместите его в модуль ядра. Затем вы можете динамически загружать / выгружать его, подобно .

системе плагинов на основе .so.

Что касается примечания , сначала я неправильно понял, что вы действительно хотите передать memcpy() ядру. Вы должны напомнить, что это очень специальная функция . Он определен в стандарте C, довольно прост и имеет довольно широкий охват, поэтому это идеальная цель , которая должна быть предоставлена ​​компилятором как

встроенная .

Так же, как strlen(), strcmp() и другие в GCC .

Тем не менее, тот факт, что встроенный модуль не мешает вам взять указатель на него.

5 голосов
/ 11 ноября 2009

Даже если бы был способ получить функцию sizeof (), она все равно может потерпеть неудачу при попытке вызвать версию, которая была скопирована в другую область в памяти. Что делать, если компилятор имеет локальные или длинные переходы в определенные области памяти. Вы не можете просто переместить функцию в память и ожидать ее запуска. ОС может сделать это, но у нее есть вся информация, необходимая для этого.


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


Даже в случае, когда ОС перемещает функцию в памяти, сама функция должна быть объявлена ​​или иным образом скомпилирована / собрана, чтобы разрешить такое действие, обычно через прагму, которая указывает, что код перемещаем. Все ссылки на память должны относиться к его собственному стековому фрейму (или локальным переменным) или включать в себя некую структуру сегмент + смещение, чтобы ЦП, непосредственно или по указанию ОС, мог выбрать соответствующее значение сегмента. Если в создании приложения участвовал компоновщик, приложение может повторно связаны с учетной записью для нового адреса функции.

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

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

3 голосов
/ 11 ноября 2009

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

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

:your_func_epilogue
mov esp, ebp
pop ebp
ret
:end_of_func

;expect a varying length run of NOPs here

:next_func_prologue
push ebp
mov ebp, esp

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

Теперь, пожалуйста, не обращайте внимания на все, что я написал, поскольку вы, очевидно, пытаетесь подойти к проблеме неправильно и по своей сути небезопасно. Нарисуйте нам более широкую картину, пожалуйста, ПОЧЕМУ вы пытаетесь это сделать, и посмотрите, сможем ли мы найти совершенно другой подход.

3 голосов
/ 11 ноября 2009

Вы хотите скопировать функцию? Я не думаю, что это возможно в C вообще. Предположим, у вас есть микроконтроллер Harvard-Architecture, в котором код (другими словами «функции») находится в ПЗУ. В этом случае вы не можете сделать это вообще. Также я знаю несколько компиляторов и компоновщиков, которые выполняют оптимизацию файлов (не только на уровне функций). Это приводит к коду операции, где части функций C смешиваются друг с другом.

Единственный способ, который я считаю возможным, может быть:

  • Сгенерируйте код операции вашей функции (например, скомпилировав / собрав ее самостоятельно).

  • Скопируйте этот код операции в массив C.

  • Используйте правильный указатель функции, указывающий на этот массив, для вызова этой функции.

  • Теперь вы можете выполнять все операции, общие для типичных «данных», с этим массивом.

Но кроме этого: Рассматривали ли вы редизайн своего программного обеспечения, чтобы вам не нужно было копировать содержимое функций?

2 голосов
/ 11 ноября 2009

Подобное обсуждение было сделано здесь:

http://www.motherboardpoint.com/getting-code-size-function-c-t95049.html

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

Если у вас GCC> = 4.4, вы можете попробовать отключить оптимизации для вашей функции, в частности, используя #pragma:

http://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html#Function-Specific-Option-Pragmas

Другим предлагаемым решением было вовсе не копировать функцию, а определить ее в том месте, куда вы хотите скопировать ее.

Удачи!

1 голос
/ 12 ноября 2009

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

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

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

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

1 голос
/ 11 ноября 2009

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

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

0 голосов
/ 21 ноября 2009

Я думаю, что одно решение может быть, как показано ниже.

Например: если вы хотите узнать размер функции func () в программе a.c и иметь индикаторы до и после функции.

Попробуйте написать сценарий perl, который скомпилирует этот файл в объектный формат (cc -o), чтобы убедиться, что операторы препроцессора не удалены. Они понадобятся вам позже, чтобы рассчитать размер из объектного файла.

Теперь найдите ваши два индикатора и определите размер кода между ними.

0 голосов
/ 16 ноября 2009

Я сделал это на Nintendo GBA, где я скопировал некоторые низкоуровневые функции рендеринга из флэш-памяти (16-битный доступ к медленной памяти) в высокоскоростную оперативную память (32-битный доступ, как минимум, вдвое быстрее). Это было сделано путем непосредственного получения адреса функции после функции, которую я хотел скопировать, size = (int) (NextFuncPtr - SourceFuncPtr). Это работало хорошо, но, очевидно, не может быть гарантировано на всех платформах (не работает на Windows наверняка).

...