5

関数のサイズを計算する方法はありますか? 関数へのポインタがあり、memcpy を使用して関数全体をコピーする必要があります。いくつかのスペースをmallocし、memcpyの3番目のパラメーターであるサイズを知る必要があります。私はそれ sizeof(function)がうまくいかないことを知っています。何か提案はありますか?

4

15 に答える 15

23

関数はCのファースト クラス オブジェクトではありません。つまり、別の関数に渡すことも、関数から返すことも、メモリの別の部分にコピーすることもできません。

ただし、関数ポインターはこれらすべてを満たすことができ、ファーストクラスのオブジェクトです。関数ポインタは単なるメモリ アドレスであり、通常はマシン上の他のポインタと同じサイズです。

于 2009-11-11T17:12:58.780 に答える
8

あなたの質問に直接答えるものではありませんが、カーネルコードからユーザー空間へのコールバックを実装しないでください。

カーネル空間にコードを挿入することも、優れた回避策ではありません

ユーザー/カーネル バリアは、プロセス間のバリアのように表現することをお勧めします。char デバイスを介して、明確に定義されたプロトコル間で、コードではなくデータをやり取りします。本当にコードを渡す必要がある場合は、カーネル モジュールにラップするだけです。.soベースのプラグイン システムと同様に、動的にロード/アンロードできます。

余談memcpy()ですが、最初はあなたがカーネルに渡したいと誤解していました。これは非常に特別な機能であることを思い出してください。これは C 標準で定義されており、非常に単純で、範囲が非常に広いため、コンパイラによって組み込みとして提供されるのに最適なターゲットです。

strlen()strcmp()およびGCCの他のものと同じように。

とはいえ、それが組み込みであるという事実は、それへのポインターを取得する能力を妨げるものではありません。

于 2009-11-11T19:39:43.850 に答える
5

sizeof()関数を取得する方法があったとしても、メモリ内の別の領域にコピーされたバージョンを呼び出そうとすると、失敗する可能性があります。コンパイラに特定のメモリ位置へのローカルジャンプまたはロングジャンプがある場合はどうなりますか。関数をメモリ内で移動して、実行されることを期待することはできません。OSはそれを行うことができますが、それを行うために必要なすべての情報を備えています。


オペレーティングシステムがこれをどのように行うかを尋ねるつもりでしたが、今考えてみると、OSが何かを移動すると、通常はページ全体が移動し、アドレスがページ/オフセットに変換されるようにメモリを処理します。OSでさえメモリ内で単一の関数を移動するかどうかはわかりません。


OSが関数をメモリ内で移動する場合でも、関数自体を宣言するか、コンパイル/アセンブルして、そのようなアクションを許可する必要があります。通常は、コードが再配置可能であることを示すプラグマを使用します。すべてのメモリ参照は、それ自体のスタックフレーム(別名ローカル変数)に関連している必要があります。または、CPUが直接またはOSの要求で適切なセグメント値を選択できるように、ある種のセグメント+オフセット構造を含める必要があります。アプリの作成にリンカが含まれている場合は、新しい関数アドレスを考慮してアプリを再リンクする必要がある場合があります。

各アプリケーションに独自の32ビットアドレス空間を与えることができるオペレーティングシステムがありますが、それは個々の関数ではなく、プロセス全体とすべての子スレッドに適用されます。

他の場所で述べたように、関数がファーストクラスのオブジェクトである言語が本当に必要です。そうでないと、運が悪くなります。

于 2009-11-11T17:15:42.513 に答える
3

関数をコピーしますか? これは一般的に C では可能ではないと思います。ハーバード アーキテクチャのマイクロコントローラーがあり、コード (つまり「関数」) が ROM にあるとします。この場合、それを行うことはまったくできません。また、関数レベルだけでなく、ファイルで最適化を行うコンパイラとリンカーもいくつか知っています。これにより、C 関数の一部が互いに混合されたオペコードが生成されます。

私が可能なと考える唯一の方法は次のとおりです。

  • 関数のオペコードを生成します (たとえば、それ自体をコンパイル/アセンブルすることによって)。

  • そのオペコードを C 配列にコピーします。

  • この関数を呼び出すには、その配列を指す適切な関数ポインタを使用します。

  • これで、その配列に対して、典型的な「データ」に共通するすべての操作を実行できます。

しかし、これとは別に、関数の内容をコピーする必要がないように、ソフトウェアの再設計を検討しましたか?

于 2009-11-11T17:32:56.863 に答える
3

I don't quite understand what you are trying to accomplish, but assuming you compile with -fPIC and don't have your function do anything fancy, no other function calls, not accessing data from outside function, you might even get away with doing it once. I'd say the safest possibility is to limit the maximum size of supported function to, say, 1 kilobyte and just transfer that, and disregard the trailing junk.

If you really needed to know the exact size of a function, figure out your compiler's epilogue and prologue. This should look something like this on 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

Disassemble your compiler's output to check, and take the corresponding assembled sequences to search for. Epilogue alone might be enough, but all of this can bomb if searched sequence pops up too early, e.g. in the data embedded by the function. Searching for the next prologue might also get you into trouble, i think.

Now please ignore everything that i wrote, since you apparently are trying to approach the problem in the wrong and inherently unsafe way. Paint us a larger picture please, WHY are you trying to do that, and see whether we can figure out an entirely different approach.

于 2009-11-11T19:22:21.810 に答える
2

同様の議論がここで行われました:

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

別の提案された解決策は、関数をまったくコピーするのではなく、コピー先の場所に関数を定義することでした。

幸運を!

于 2009-11-11T17:31:14.323 に答える
1

リンカがグローバル最適化を行わない場合は、関数ポインタと次の関数のアドレスの差を計算するだけです。

関数をコピーすると、コードが再配置可能にコンパイルされていない場合は呼び出せないものが生成されることに注意してください(つまり、コード内のすべてのアドレスは相対的である必要があります。たとえば、ブランチ。グローバルは移動しないため、機能します)。

于 2009-11-11T17:17:06.353 に答える
1

非同期ジョブが終了したときにユーザー空間に通知できるように、カーネルドライバーからユーザー空間へのコールバックが必要なようです。

通常のユーザー空間ライブラリがおそらく行う方法であるため、それは賢明に聞こえるかもしれませんが、カーネル/ユーザー空間インターフェースの場合、それはまったく間違っています。関数コードをカーネルにコピーできたとしても、適切に位置に依存しないようにしたとしても、カーネルとユーザー空間のコードは根本的に異なるコンテキストで実行されるため、まだ間違っています。問題を引き起こす可能性のある違いのほんの一例として、スワップアウトされたページが原因でカーネル コンテキストでページ フォールトが発生した場合、ページがスワップインされるのではなく、カーネル oops が発生します。

正しいアプローチは、非同期ジョブが終了したときにカーネルがファイル記述子を読み取り可能にすることです (あなたの場合、このファイル記述子はほぼ確実にドライバーが提供するキャラクターデバイスです)。ユーザー空間プロセスは、select/ poll、またはread- を使用してこのイベントを待機できます。必要に応じて、ファイル記述子を非ブロックに設定し、基本的にすべての標準 UNIX ツールを使用してこのケースに対処できます。結局のところ、これがネットワーク ソケットの非同期性 (および他のほとんどすべての非同期ケース) の処理方法です。

発生したイベントに関する追加情報を提供する必要がある場合はread、読み取り可能なファイル記述子を呼び出すときに、ユーザー空間プロセスで利用できるようにすることができます。

于 2009-11-11T21:08:45.540 に答える
0

最適化を無効にし、関数の順序を維持するためにコンパイラーに依存するよりもクリーンな方法は、その関数(またはコピーが必要な関数のグループ)を独自のセクションに配置することです。これはコンパイラとリンカに依存し、コピーされる関数間で呼び出す場合は相対アドレス指定も使用する必要があります。なぜこれを行うのかと尋ねる人にとっては、実行中のコードを更新する必要がある組み込みシステムでの一般的な要件です。

于 2009-11-12T04:26:17.840 に答える
0

関数は、コピーできる単なるオブジェクトではありません。相互参照/記号などはどうですか? もちろん、標準の Linux の「binutils」パッケージのようなものを使用して、バイナリを苦しめることはできますが、それでよろしいでしょうか?

ちなみに、単に memcpy() の実装を置き換えようとしている場合は、LD_PRELOAD メカニズムを調べてください。

于 2009-11-11T17:15:17.787 に答える
0

あなたが望むことを達成する方法を思いつくことができますが、それは言語の恐ろしい乱用であるため、あなたには言いません.

于 2009-11-11T21:13:57.577 に答える
0

私はニンテンドー GBA でこれを行い、いくつかの低レベルのレンダリング関数をフラッシュ (16 ビット アクセスの遅いメモリ) から高速ワークスペース RAM (32 ビット アクセス、少なくとも 2 倍の速さ) にコピーしました。これは、サイズ = (int) (NextFuncPtr - SourceFuncPtr) という、コピーしたい関数の直後に関数のアドレスを取得することによって行われました。これはうまく機能しましたが、明らかにすべてのプラットフォームで保証されているわけではありません (Windows では確実に機能しません)。

于 2009-11-16T16:47:51.887 に答える
0

私の提案は次のとおりです。

カーネル スペースにコードを挿入することは非常に大きなセキュリティ ホールであり、ほとんどの最新の OS は自己変更コードを完全に禁止しています

于 2009-11-12T04:47:14.973 に答える
0

私が知る限り、元の投稿者は実装固有の何かをしたいので、移植性がありません。これは、C 標準ではなく、関数へのポインターのキャストに関して C++ 標準が述べていることから外れていますが、ここではそれで十分なはずです。

一部の環境では、一部のコンパイラを使用して、投稿者がやりたいと思われることを実行できる場合があります (つまり、関数へのポインタが指すメモリのブロックを別の場所にコピーします。 malloc、そのブロックを関数へのポインターにキャストし、直接呼び出します)。ただし、移植性はありませんが、問題にならない可能性があります。そのメモリブロックに必要なサイズを見つけることは、それ自体が環境とコンパイラに依存し、非常に難解なものが必要になる場合があります (たとえば、メモリをスキャンして戻りオペコードを探したり、逆アセンブラを介してメモリを実行したりします)。繰り返しますが、実装固有であり、移植性が非常に低いです。繰り返しますが、元のポスターには関係ないかもしれません.

潜在的な解決策へのリンクはすべて、実装固有の動作を利用しているように見えます。意図したとおりに機能するかどうかはわかりませんが、OPに適している可能性があります。

この馬を殴り殺したので、なぜOPがこれをやりたいのか知りたい. ターゲット環境で動作する場合でも、非常に脆弱です (たとえば、コンパイラ オプション、コンパイラ バージョン、コード リファクタリングなどの変更で壊れる可能性があります)。こういう魔法が必要な仕事をしなくてよかった(仮にそうだったとしても)……

于 2009-11-12T05:55:13.887 に答える