IT이야기

리눅스 커널에 있는 함수의 포인터에서 함수의 이름을 가져오는 방법?

cyworld 2022. 4. 25. 22:01
반응형

리눅스 커널에 있는 함수의 포인터에서 함수의 이름을 가져오는 방법?

C에 있는 함수의 포인터에서 함수의 이름을 가져오는 방법?

편집: 실제 사례: 리눅스 커널 모듈을 작성하고 커널 기능을 호출하는 경우.이러한 기능들 중 일부는 포인터들이며 나는 커널 소스에 있는 그 함수의 코드를 검사하고 싶다.하지만 나는 그것이 어떤 기능을 가리키는지 모른다.시스템이 고장나면(커널 패닉) 기능이 있는 콜스택을 화면에 출력하기 때문에 가능한 일이라고 생각했다.하지만, 내가 틀렸나봐...제가요?

나는 왜 모든 사람들이 그것이 불가능하다고 말하는지 놀랍다.리눅스에서는 정전기 이외의 기능을 할 수 있다.

나는 이것을 이루기 위한 적어도 두 가지 방법을 알고 있다.

역추적 인쇄를 위한 GNU 기능은 다음과 같다.backtrace()그리고backtrace_symbols()(보기)man) 당신의 경우에는 필요 없다.backtrace()이미 기능 포인터를 가지고 있기 때문에 그냥 전달하면 된다.backtrace_symbols().

예(작업 코드):

#include <stdio.h>
#include <execinfo.h>

void foo(void) {
    printf("foo\n");
}

int main(int argc, char *argv[]) {
    void    *funptr = &foo;

    backtrace_symbols_fd(&funptr, 1, 1);

    return 0;
}

컴파일 대상gcc test.c -rdynamic

출력:./a.out(foo+0x0)[0x8048634]

이진 이름, 함수 이름, 함수 시작으로부터의 포인터 오프셋 및 포인터 값을 제공하므로 구문 분석할 수 있다.

또 다른 방법은 사용하는 것이다.dladdr() 한 번 내 으로는 (다만장) 아마마.print_backtrace()사용하다dladdr().dladdr()돌아온다Dl_info함수 이름이 에 있는 구조dli_sname하다 - 필드 . 여기서 코드 예를 제공하지는 않지만 명백하다 - 참조man dladdr자세한 것은

NB! 두 접근방식 모두 정전기 방지 기능이 필요하다!

음, 한 가지 더 방법이 있다 - 디버그 정보를 사용하여libdwarf하지만 2진법이 필요하기 때문에 그렇게 하기가 쉽지 않아서 나는 그것을 추천하지 않아.

그것은 추가적인 도움 없이는 직접적으로 가능하지 않다.

다음 작업을 수행할 수 있음:

  1. 프로그램 매핑 함수의 테이블을 이름에 대한 포인터로 유지 관리

  2. 실행 파일의 기호 테이블이 있는지 검사하십시오.

그러나 후자는 딱딱하고 휴대성이 없다.방법은 운영체제의 바이너리 포맷(ELF, a.out, .exe 등)과 링커에 의해 행해지는 모든 재배치에 따라 달라진다.

편집: 이제 실제 사용 사례가 무엇인지 설명했으므로, 답은 사실 그렇게 어렵지 않다.커널 기호 테이블 사용 가능/proc/kallsyms할 수 있는 따라서, 수리는 API:

#include <linux/kallsyms.h>

const char *kallsyms_lookup(unsigned long addr, unsigned long *symbolsize,
                            unsigned long *ofset, char **modname, char *namebuf)

void print_symbol(const char *fmt, unsigned long addr)

간단한 디버그 목적을 위해 후자는 아마도 당신이 필요로 하는 것을 정확히 할 것이다 - 그것은 주소를 가져가고 포맷을 하고 그것을 보낼 것이다.printk또는 사용할 수 있음printk…과 함께%pF형식 지정 지정자

리눅스 커널에서는 "%p"를 직접 사용할 수 있다.인쇄물의 F" 형식!

void *func = &foo;
printk("func: %pF at address: %p\n", func, func);

리눅스에서는 다음과 같은 것이 통한다.

  • 다음을 이용하여 함수 주소를 인쇄하다.%p
  • 그럼 해 봐.nm <program_path> | grep <address> 없이)0x접두사)
  • 함수 이름을 보여줘야 해.

해당 기능이 같은 프로그램(동적으로 연결된 라이브러리 같은 것에 있지 않음)에 있을 때만 작동한다.

로드된 공유 라이브러리의 로드 주소를 알 수 있으면 인쇄된 번호에서 주소를 빼내고, 라이브러리에서 nm를 사용하여 함수 이름을 확인할 수 있다.

당신은 죽을 수는 없지만 원한다면 이 문제에 대해 다른 접근법을 시행할 수 있다.원하는 대로 설정할 수 있는 설명 문자열뿐만 아니라 함수를 가리키는 대신 구조체 포인터를 만들 수 있다.나는 또한 디버깅 포즈빌리티를 추가했다. 왜냐하면 당신은 아마도 이 vars가 영원히 프린트 되는 것을 원하지 않을 것이기 때문이다.

// Define it like this
typedef struct
{
  char        *dec_text;
  #ifdef _DEBUG_FUNC
  void        (*action)(char);
  #endif
} func_Struct;

// Initialize it like this
func_Struct func[3]= {
#ifdef _DEBUG_FUNC
{"my_Set(char input)",&my_Set}};
{"my_Get(char input)",&my_Get}};
{"my_Clr(char input)",&my_Clr}};
#else
{&my_Set}};
{&my_Get}};
{&my_Clr}};
#endif 

// And finally you can use it like this
func[0].action( 0x45 );
#ifdef _DEBUG_FUNC
printf("%s",func.dec_text);
#endif

지적할 수 있는 기능 목록이 너무 크지 않거나 이미 작은 기능 그룹이 의심되는 경우, 주소를 인쇄하여 실행 중에 사용한 것과 비교할 수 있다.Ex:

typedef void (*simpleFP)();
typedef struct functionMETA {
    simpleFP funcPtr;
    char * funcName;
} functionMETA;

void f1() {/*do something*/}
void f2() {/*do something*/}
void f3() {/*do something*/}

int main()
{
    void (*funPointer)() = f2; // you ignore this
    funPointer(); // this is all you see

    printf("f1 %p\n", f1);
    printf("f2 %p\n", f2);
    printf("f3 %p\n", f3);

    printf("%p\n", funPointer);

    // if you want to print the name
    struct functionMETA arrFuncPtrs[3] = {{f1, "f1"}, {f2, "f2"} , {f3, "f3"}};

    int i;
    for(i=0; i<3; i++) {
        if( funPointer == arrFuncPtrs[i].funcPtr )
            printf("function name: %s\n", arrFuncPtrs[i].funcName);
    }
}

출력:

f1 0x40051b
f2 0x400521
f3 0x400527
0x400521
function name: f2

이 접근법은 정적 기능에도 적용될 것이다.

대체적으로 어떻게 할 방법이 없다.

해당 코드를 DLL/Shared Library에 컴파일하면 모든 진입점을 기록하여 가지고 있는 포인터와 비교할 수 있을 것이다.아직 시도해보지는 않았지만, DLLs/Shared Libs에 대한 경험이 있어서 효과가 있을 것으로 예상한다.이것은 심지어 상호 평면적인 작업을 위해 구현될 수도 있다.

디버그 기호로 컴파일하기 위해 이미 언급된 다른 사용자가 실행 중인 응용 프로그램에서 디버거의 기능과 유사하게 분석하는 방법을 찾을 수 있다.그러나 이것은 절대적으로 독점적이며 휴대할 수 없다.

  1. 사용하다kallsyms_lookup_name()의 주소를 찾다kallsyms_lookup.

  2. 다음을 가리키는 함수 포인터 사용kallsyms_lookup말하자면.

시각적 누출 감지기를 통해 콜스택 인쇄가 어떻게 작동하는지 확인하십시오.그러나 이것은 당신이 윈도우를 사용하고 있다고 가정한다.

질문이 정확히 무엇을 요구하는지는 아니지만 여기서 답을 읽은 후에 나는 나의 비슷한 문제에 대한 해결책을 다음과 같이 생각한다.

/**
* search methods */
static int starts(const char *str, const char *c);
static int fuzzy(const char *str, const char *c);

int (*search_method)(const char *, const char *);

/* asign the search_method and do other stuff */
[...]

printf("The search method is %s\n", search_method == starts ? "starts" : "fuzzy")

프로그램에 이 기능이 많이 필요한 경우 XMacro의 문자열과 함께 메서드 이름을 정의하고#define X(name, str) ... #undef X함수 이름에서 해당 문자열을 가져오는 코드.

알니탁의 대답은 커널 모듈에서 함수 이름을 출력할 수 있는 해결 방법을 찾고 있을 때 내게 큰 도움이 된다.하지만 내가 공급하고 싶은 한가지가 있는데, 그것은 당신이 %pF 대신 %pS를 사용하여 함수 이름을 인쇄하기를 원할 수 있다는 것이다. 왜냐하면 %pF는 5.10.x와 같은 새로운 커널 버전에서는 더 이상 작동하지 않기 때문이다.

그럴수는 없어요.함수 이름은 컴파일되고 연결될 때까지 함수에 첨부되지 않는다.모두 그 시점의 메모리 주소로 된 것이지 이름이 아니다.

반사 거울이 없으면 자신의 모습을 알 수 없을 것이다.C#처럼 반성이 가능한 언어를 사용해야 할 것이다.

참조URL: https://stackoverflow.com/questions/351134/how-to-get-functions-name-from-functions-pointer-in-linux-kernel

반응형