20

私は Linux で純粋に静的にリンクされた PIE 実行可能ファイルの概念を試していますが、GNU binutils リンカ-pieが使用時に出力バイナリに PT_INTERP ヘッダーを追加することを主張するという問題に直面しています-static。この動作を禁止する方法はありますか? つまり、特定のヘッダーを出力ファイルに書き込まないように GNU ld に明確に指示する方法はありますか? おそらくリンカースクリプトで?

(動作しないという主張で答えないでください。私は、プログラムがまだ再配置処理を必要としていることをよく知っています-Bsymbolic。これを処理するための標準ですが、バイナリからヘッダーをScrt1.ohexedit しない限り、動的リンカがすでに起動して作業を行っていないと、呼び出すことができませんPT_INTERP。)

4

5 に答える 5

13

多分私はナイーブですが...デフォルトのリンカースクリプトを検索して編集し、.interpセクションでリンクしている行を削除するだけでは十分ではありませんか?

たとえば、私のマシンではスクリプトが/usr/lib/ldscriptsあり、問題の行はinterp : { *(.interp) }セクションにありSECTIONSます。

次のコマンドを実行すると、使用されるデフォルトのスクリプトをダンプできます。

$ ld --verbose ${YOUR_LD_FLAGS} | \
    gawk 'BEGIN { s = 0 } { if ($0 ~ /^=/) s = !s; else if (s == 1) print; }'

スクリプトを少し変更しgawkて行を削除できinterpます (またはgrep -v、そのスクリプトを使用してプログラムをリンクするだけです。

于 2012-05-11T03:46:22.237 に答える
12

私は解決策を見つけたかもしれないと思います.pyバイナリを作成する-shared代わりに使用するだけです. -pie動作を修正するには、いくつかの追加のリンカー オプションが必要ですが、カスタム リンカー スクリプトの必要性を回避しているようです。つまり、-sharedリンカー スクリプトは、静的なパイ バイナリをリンクするために既に本質的に正しいものです。

これで動作するようになったら、使用している正確なコマンドラインで回答を更新します。

更新:うまくいきます!コマンドラインは次のとおりです。

gcc -shared -static-libgcc -Wl,-static -Wl,-Bsymbolic \
    -nostartfiles -fPIE Zcrt1.s Zcrt2.c /usr/lib/crti.o hello.c /usr/lib/crtn.o

ここで、Zcrt1.s は、通常の作業を行う前に Zcrt2.c 内の関数を呼び出す Scrt1.s の修正バージョンであり、Zcrt2.c 内のコードは、argv および環境配列の直後で補助ベクトルを処理して、DYNAMIC セクションを見つけます。次に、再配置テーブルをループし、すべての相対タイプの再配置 (存在する必要があるもののみ) を適用します。

これで、これらすべてを (ちょっとした作業で) スクリプトまたは gcc 仕様ファイルにまとめることができます...

于 2012-05-27T23:25:12.183 に答える
4

これはそのちっぽけなボックスに収まらないため、以前のメモを拡張します(これは単なるアイデアまたは議論であるため、賞金を受け取ったり報酬を与えたりする義務を感じないでください)、おそらくこれを行う最も簡単でクリーンな方法は、突き刺すことですビルド後のステップを追加してPT_INTERP、結果のバイナリからヘッダーを取り除きますか?

ヘッダーを手動で編集するよりも簡単で、潜在的にすべてを移動する必要がある場合はPT_INTERPPT_NULL. 既存のツール (ある種のスクリプト可能な 16 進数の検索と置換) を介してファイルに単純にパッチを適用する方法を見つけることができるかどうか、またはそれを行うために小さなプログラムを作成する必要があるかどうかはわかりません。libbfd (GNU Binary File Descriptor ライブラリ) は、後者の場合にあなたの友人になるかもしれないことを私は知っています。

オプションを介してこれを実行することがなぜ重要なのか理解できないと思いldます。可能であれば、それが望ましい理由がわかります。しかし、いくつかの(確かに軽い)グーグルはそのような機能がないことを示しているため、個別に事後的に行う方が面倒ではないかもしれません. (おそらく、フラグを に追加することは、をldに置き換えるスクリプトを作成するよりも簡単ですが、開発者にそれをアップストリームにプルするよう説得することは別の問題です。)PT_INTERPPT_NULL


どうやら(これがすでに見たことがある場合は修正してください)コマンドを使用しldてリンカースクリプトのELFヘッダーに関する動作をオーバーライドし、特定のヘッダータイプを指定しないように指定することができます任意のセグメントに含まれます。構文は定かではありませんが、次のようになると思います。PHDRS:none

PHDRS
{
  headers PT_PHDR PHDRS ;
  interp PT_INTERP ;
  text PT_LOAD FILEHDR PHDRS ;
  data PT_LOAD ;
  dynamic PT_DYNAMIC ;
}

SECTIONS
{
  . = SIZEOF_HEADERS;
  .interp : { } :none
  ...
}

ld docsから、次のようにリンカー スクリプトをオーバーライドできます--library-path

--library-path=searchdir

ld がアーカイブ ライブラリと ld 制御スクリプトを検索するパスのリストにパス searchdir を追加します。このオプションは何度でも使用できます。ディレクトリは、コマンド ラインで指定された順序で検索されます。コマンド ラインで指定されたディレクトリは、デフォルト ディレクトリの前に検索されます。オプションが表示される順序に関係なく、すべての -L オプションがすべての -l オプションに適用されます。検索されるデフォルトのパスのセット (`-L' で指定されていない場合) は、ld が使用しているエミュレーション モードに依存し、場合によってはそれがどのように構成されたかにも依存します。セクション環境変数を参照してください。パスは、SEARCH_DIR コマンドを使用してリンク スクリプトで指定することもできます。この方法で指定されたディレクトリは、リンカー スクリプトがコマンド ラインに表示されるポイントで検索されます。

また、Implicit Linker Scripts のセクションから:

リンカーがオブジェクト ファイルまたはアーカイブ ファイルとして認識できないリンカー入力ファイルを指定すると、リンカー スクリプトとしてファイルを読み込もうとします。ファイルをリンカー スクリプトとして解析できない場合、リンカーはエラーを報告します。

暗黙的に定義されたリンカー スクリプトとは対照的に、ユーザー定義のリンカー スクリプトの値を暗示しているように見えるものは、デフォルト スクリプトの値置き換えます。

于 2012-05-11T03:31:05.763 に答える
3

私は GNU ld の専門家ではありませんが、ドキュメントで次の情報を見つけました。

特別な secname `/DISCARD/' を使用して、入力セクションを破棄できます。`/DISCARD/' という名前の出力セクションに割り当てられたセクションは、最終的なリンク出力には含まれません。

これがお役に立てば幸いです。

アップデート:

(これはソリューションの最初のバージョンであり、INTERP セクションがヘッダー PT_INTERP と共に削除されているため機能しません。)

main.c:

int main(int argc, char **argv)                                                                                                                               
{                                                                                                                                                             
    return 0;                                                                                                                                                 
}

main.x:

SECTIONS {                                                                                                                                                    
    /DISCARD/ : { *(.interp) }                                                                                                                                
}

ビルド コマンド:

$ gcc -nostdlib -pie -static -Wl,-T,main.x main.c
$ readelf -S a.out | grep .interp

オプション -Wl,-T,main.x なしのビルド コマンド:

$ gcc -nostdlib -pie -static main.c 
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000000218
$ readelf -S a.out | grep .interp
  [ 1] .interp           PROGBITS        00000134 000134 000013 00   A  0   0  1

更新 2:

この解決策のアイデアは、元のセクション 'INTERP' (リンカー スクリプト ファイル内の . interp) の名前を .interp1 に変更することです。つまり、セクションの内容全体が .interp1 セクションに配置されます。したがって、デフォルトのリンカー スクリプト設定が失われることを心配することなく、INTERP セクション (現在は空) を安全に削除できます。したがって、ヘッダー INTERP_PT も削除されます。

SECTIONS {
    .interp1 : { *(.interp); } : NONE
    /DISCARD/ : { *(.interp) }
}

セクション INTERP の内容がファイル (.interp1 として) に存在するが、INTERP_PT ヘッダーが削除されていることを示すために、readelf + grep の組み合わせを使用します。

$ gcc -nostdlib -pie -Wl,-T,main.x main.c
$ readelf -l a.out | grep interp
   00     .note.gnu.build-id .text .interp1 .dynstr .hash .gnu.hash .dynamic .got.plt 
$ readelf -S a.out | grep interp
  [ 3] .interp1          PROGBITS        0000002e 00102e 000013 00   A  0   0  1
于 2012-05-17T21:31:14.813 に答える