コンパイルされたC関数から生成されたアセンブリを見ると、すべての関数本体がラップされていることがわかります。
pushq %rbp
movq  %rsp, %rbp
; body
leave
ret
http://en.wikipedia.org/wiki/X86_instruction_listingsleaveは、命令を(AT&T構文で)80186に相当するものとしてリストしています。
movq  %rbp, %rsp
popq  %rpb
つまりleave、最初の2行の逆です。呼び出し元のスタックフレームを保存し、独自のスタックフレームを作成して、最後に巻き戻します。
クロージングは、ここで得retたものの逆であり、 http://www.unixwiz.net/techtips/win32-callconv-asm.htmlは、これらのペアの命令中に発生する命令ポインターレジスタの非表示のプッシュとポップを示しています。call
このアセンブリwrapfunはコンパイラによって関数用に作成されているため、void関数ポインタ呼び出しが単独で機能しない理由。funラッパーを作成して、呼び出し元が設定したスタックフレームを、独自のスタックフレームを邪魔することなく、の呼び出しに直接渡すことができるようにする必要があります。つまり、Cの呼び出し規約を遵守し、同時に違反します。
Cプロトタイプを考えてみましょう
int wrapfun(int x, int y);
アセンブリ実装とペアになっています(AT&T x86_64)
  .file "wrapfun.s"
  .globl wrapfun
  .type   wrapfun, @function
wrapfun:
  call    funptr
  jmp     *%rax
  .size   wrapfun, .-wrapfun
fun基本的に、スタックを私のスタックとまったく同じように見せたいので、通常のスタックポインタとベースポインタの操作をスキップします。を呼び出すとfunptr、独自のスタックスペースが作成され、結果がレジスタに保存されRAXます。独自のスタックスペースがなく、呼び出し元IPがスタックの一番上にうまく座っているため、ラップされた関数に無条件でジャンプして、最後までジャンプさせることができますret。このようにして、関数ポインターが呼び出されると、呼び出し元によってセットアップされたスタックが表示されます。
ローカル変数を使用したり、パラメーターをに渡したりする必要がある場合はfunptr、いつでもスタックを設定し、呼び出しの前にスタックを破棄できます。
wrapfun:
  pushq   %rbp
  movl    %rsp, %rbp ; set up my stack
  call    funptr
  leave              ; tear down my stack
  jmp     *%rax
または、コンパイラが前後に何をするかについての知識を利用して、このロジックをインラインアセンブリに埋め込むこともできます。
void wrapfun()
{
    void* p = funptr();
    __asm__(
        "movq -8(%rbp), %rax\n\t"
        "leave\n\t"
        "popq %rbx\n\t"
        "call *%rax\n\t"
        "pushq %rbx\n\t"
        "pushq %ebp\n\t"    // repeat initial function setup
        "movq %rsp, %rbp"   // so it can be torn down correctly
    );
}
このアプローチには、魔法の前にCローカル変数を簡単に宣言できるという利点があります。宣言された最後のローカル変数はRBP-sizeof(var)にあり、スタックを破棄する前にRAXに保存します。もう1つの考えられる利点は、C前処理装置を使用して、たとえば、個別のソースファイルを必要とせずにインライン32ビットまたは64ビットアセンブリを実行できることです。
編集:欠点は、IPをレジスタに保存する必要がRBXあるため、呼び出し元が使用しないようにすることで、アプリケーションの移植性が制限されることです。
要するに、答えはイエスです。手を少し汚したいのであれば、署名を知らなくても関数をラップすることは間違いなく可能です。移植性についての約束はありません;)。