53

/lib/libc.so.6したがって、glibcは通常の実行可能ファイルのようにシェルで実行できることを誰もが知っているでしょう。その場合、glibcはバージョン情報を出力して終了します。これは、.soでエントリポイントを定義することで実行されます。場合によっては、これを他のプロジェクトにも使用すると面白いかもしれません。残念ながら、ldの-eオプションで設定できる低レベルのエントリポイントは少し低レベルです。動的ローダーが使用できないため、適切なライブラリ関数を呼び出すことができません。このため、glibcは、このエントリポイントでネイキッドシステムコールを介してwrite()システムコールを実装します。

私の質問は、他の.soの関数にアクセスできるように、そのエントリポイントから完全なダイナミックリンカーをブートストラップする方法を誰かが考えられるかどうかです。

4

3 に答える 3

55

更新2:任意のGLIBCで機能するAndrew G Morganの少し複雑なソリューションを参照してください(このソリューションはlibc.so.6(永久に)それ自体でも使用されるため、次のように実行できます./libc.so.6(そのように呼び出されるとバージョン情報が出力されます))。

アップデート1:これは新しいGLIBCバージョンでは機能しなくなりました:

./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable

2009年からの元の回答:

オプションを使用して共有ライブラリを構築-pieすると、必要なものがすべて提供されるように見えます。

/* pie.c */
#include <stdio.h>
int foo()
{
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return 42; 
}
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}


/* main.c */
#include <stdio.h>

extern int foo(void);
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}


$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so


$ ./pie.so
in main pie.c:9
in foo pie.c:4
$ ./a.out
in main main.c:6
in foo pie.c:4
$

PS glibcwrite(3)は、他に呼び出す場所がないため、システムコールを介して実装されます(これはすでに最低レベルです)。これは、を実行できることとは何の関係もありませんlibc.so.6

于 2009-09-20T17:13:56.280 に答える
6

私はこれに対するサポートをに追加することを探していましたpam_cap.so、そしてこの質問を見つけました。@EmployedRussianが自分の投稿のフォローアップで述べているように、受け入れられた回答はある時点で機能しなくなりました。これを再び機能させる方法を理解するのに時間がかかったので、ここに実際の例があります。

この実例には、対応するいくつかのテストでどのように機能するかを示す5つのファイルが含まれています。

まず、この些細なプログラムを考えてみましょう(それを呼び出しますempty.c):

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

それをコンパイルすると、次のようにシステム上の動的シンボルがどのように解決されるかを確認できます。

$ gcc -o empty empty.c
$ objcopy --dump-section .interp=/dev/stdout empty ; echo
/lib64/ld-linux-x86-64.so.2
$ DL_LOADER=/lib64/ld-linux-x86-64.so.2

その最後の行は、後で使用するためのシェル変数を設定します。

これが私のサンプル共有ライブラリを構築する2つのファイルです:

/* multi.h */
void multi_main(void);
void multi(const char *caller);

/* multi.c */
#include <stdio.h>
#include <stdlib.h>
#include "multi.h"

void multi(const char *caller) {
    printf("called from %s\n", caller);
}

__attribute__((force_align_arg_pointer))
void multi_main(void) {
    multi(__FILE__);
    exit(42);
}

const char dl_loader[] __attribute__((section(".interp"))) =
    DL_LOADER ;

(更新2021-11-13:強制的な調整は、コードがSSEと互換性を持つようにするため__i386__glibc SIGSEGVのものです。これがないと、クラッシュのデバッグが困難になります。)

次のようにコンパイルして実行できます。

$ gcc -fPIC -shared -o multi.so -DDL_LOADER="\"${DL_LOADER}\"" multi.c -Wl,-e,multi_main
$ ./multi.so
called from multi.c
$ echo $?
42

つまり、これは.soスタンドアロンのバイナリとして実行できるものです。次に、共有オブジェクトとしてロードできることを検証します。

/* opener.c */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    void *handle = dlopen("./multi.so", RTLD_NOW);
    if (handle == NULL) {
        perror("no multi.so load");
        exit(1);
    }
    void (*multi)(const char *) = dlsym(handle, "multi");
    multi(__FILE__);
}

つまり、共有オブジェクトを動的にロードし、そこから関数を実行します。

$ gcc -o opener opener.c -ldl
$ ./opener
called from opener.c

最後に、この共有オブジェクトに対してリンクします。

/* main.c */
#include "multi.h"

int main(int argc, char **argv) {
    multi(__FILE__);
}

次のようにコンパイルして実行します。

$ gcc main.c -o main multi.so
$ LD_LIBRARY_PATH=./ ./main
called from main.c

(は標準のシステムライブラリの場所にないため、ランタイムが環境変数multi.soを使用して共有オブジェクトファイルを検索する場所をオーバーライドする必要があることに注意してください。)LD_LIBRARY_PATH

于 2021-07-11T18:46:10.970 に答える
1

ld -eエントリポイントへのポイントがありdlopen()、関数ファミリを使用して残りのダイナミックリンカを検索してブートストラップすると思います。もちろん、dlopen()それ自体が静的にリンクされていることを確認する必要があります。または、それを取得するために十分な独自のリンカースタブを実装する必要がある場合があります(mmap()libc自体が行っているようなシステムコールインターフェイスを使用します)。

そのどれも私には「いい」とは思えません。実際、ジョブのサイズを評価するのに十分なglibcソース(およびld-linux一例としてのソースコード)を読むことを考えるだけで、私にはかなりうんざりしているように聞こえます。それはまた、携帯性の悪夢かもしれません。ld-linuxLinuxの実装方法と、OpenSolaris、FreeBSDなどでのリンケージの実行方法には大きな違いがあるかもしれません。(知らない)。

于 2009-09-20T03:26:38.817 に答える