gcc/g++사용자 정의 동명 함수 로 C 라 이브 러 리 함수 덮어 쓰기
4897 단어 c
사실 이 문 제 는 이전에 생각해 본 적 이 있 는데,매번 끝까지 따 지지 않 았 다.그 이 유 는 어떤 Linux C 프로 그래 밍 책 이 든 기본적으로 신뢰 할 수 있 는 의미 의 signal 함 수 를 사용 하여 해당 하 는 라 이브 러 리 함 수 를 덮어 쓰기 때문이다.예 를 들 어 에서 다음 과 같이 정의 되 었 다.
SIGALRM
이외 의 신호 가 중 단 된 시스템 호출 을 자동 으로 재 부팅 하고 다른 신 호 를 막 지 않 는 다.(신호 마스크 가 비어 있 지만 POSIX 는 포 획 된 신호 가 신호 처리 함수 가 실행 되 는 동안 항상 차단 되 는 것 을 보증 합 니 다)그러나 책 에서 라 이브 러 리 함 수 를 어떻게 덮어 쓰 는 지 에 대한 정 의 는 언급 되 지 않 았 습 니 다.서로 다른 컴 파일 러 에 있어 서 방법 이 다 르 기 때문에 여 기 는gcc
에 만 있 습 니 다.정적 링크 VS 동적 링크
주:결론 을 직접 보고 싶 으 면 본 부분의 내용 을 무시 할 수 있 습 니 다.
쉽게 말 하면 링크 는 대상 파일 을 다시 찾 을 수 있 는 최종 실행 가능 한 대상 파일 로 조합 하 는 것 입 니 다(다음은 모두'프로그램'이라는 단어 로 대체 합 니 다).대상 파일 에 기호 표 가 있 는데 그 중에서 해석 되 지 않 은 기호 참조 가 있 습 니 다.예 를 들 어 원본 파일 에서 함 수 를 설명 하 였 으 나 구체 적 인 정 의 를 내리 지 않 았 습 니 다.이 때 링크 기 는 다른 대상 파일 에서 해당 하 는 기호 정의 가 있 는 지 찾 습 니 다.
예 를 들 어 다음 원본 파일 이 있 습 니 다.
// main.c
void foo();
int main() {
foo();
return 0;
}
main.c
에는foo
성명 만 포함 되 어 있 고 정의 가 없 기 때문에 main.c 를 직접 컴 파일 하면 오류 가 발생 할 수 있 습 니 다.foo.c
컴 파일 된 정적 라 이브 러 리libfoo.a
를 제공 하면(컴 파일 과정 은 다음 과 같다)// foo.c
#include
void foo() { puts("foo"); }
$ gcc -c foo.c
$ ar -rcs libfoo.a foo.o
그러면 링크 를 진행 할 수 있 습 니 다.gcc 컴 파일 과정 은 다음 과 같 습 니 다.
$ gcc main.c libfoo.a
이 과정 에서 먼저 소스 코드
main.c
를 컴 파일 하여 대상 파일 을 다시 찾 을 수 있 습 니 다.그 중에서 기호 표 에는 해석 되 지 않 은 기호 참조foo
가 포함 되 어 있 습 니 다.이 때 링크 기 가 기록 한 다음 에 뒤의 대상 파일(정적 라 이브 러 리)에서foo
이 포함 되 어 있 는 지 확인 하고 찾 으 면 일치 하 며 정 의 를 찾 지 않 습 니 다.예 를 들 어 현재 다른 정 의 를 내 렸 습 니 다
foo
함수 의 라 이브 러 리libfoo2.a
.소스 코드 는 다음 과 같 고 컴 파일 과정 은 같 습 니 다libfoo.a
.// foo2.c
#include
void foo() { puts("foo2"); }
현재 각각 다른 순서에 따라 링크,실행 프로그램,관찰 결 과 를 진행 합 니 다.
$ gcc main.c libfoo.a libfoo2.a
$ ./a.out
foo
$ gcc main.c libfoo2.a libfoo.a
$ ./a.out
foo2
방금 의 결론 을 입증 하 였 으 며,어떤 뒤의 행위 도 앞의 행 위 를 덮어 쓰 지 않 았 다.
OK,그럼 문제 가 생 겼 습 니 다.
stdio.h
에는puts
함수 의 성명 만 있 고 정의 가 없습니다.이것 이 바로 동적 라 이브 러 리 입 니 다.ldd
명령 으로 프로그램 이 호출 한 동적 라 이브 러 리 를 볼 수 있 습 니 다.$ ldd a.out
linux-vdso.so.1 => (0x00007fff78b02000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe770f5a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe771324000)
libc.so.6
즉,C 표준 라 이브 러 리(동적 라 이브 러 리)는 특정한 디 렉 터 리 에 두 고 gcc 의-l 옵션 을 통 해 링크 의 동적 라 이브 러 리 를 지정 합 니 다.기호 정의 의 구체 적 인 내용 은 최종 프로그램 에 넣 지 않 고 기호 정의 가 있 는 동적 라 이브 러 리 경 로 를 기록 하여 프로그램 이 실 행 될 때 찾 습 니 다.프로그램의 부 피 를 간소화 한 것 이 장점 이 며,동적 링크 의 함 수 를 처음 호출 할 때 시간 이 걸 리 는 것 이 단점 이다.링크 할 때 C 표준 라 이브 러 리 는 추가 옵션 없 이 동적 링크 를 할 수 있 습 니 다.
-static
옵션 을 추가 할 때 만 동적 링크 를 하지 않 고 C 표준 라 이브 러 리 의 정적 라 이브 러 리 를 정적 으로 연결 합 니 다.더 자세 한 부분 은(즉 CSAPP)제7 장 을 참고 할 수 있다.
라 이브 러 리 함 수 는 일반적으로 동적 링크 를 진행 합 니 다.
라 이브 러 리 함 수 를 어떻게 덮어 씁 니까?
gcc 옵션
no-builtin
을 사용 하면 gcc 의 manpage 에서 관련 설명(여기 붙 이지 않 음)을 볼 수 있 습 니 다.대체적으로 gcc 는 일부 내장 함수 에 대해 바 텀 최적화 가 있 고 자신 이 같은 기능 을 실현 하 는 것 보다 부피 가 작고 속도 가 빠 른 바 텀 코드 를 만 들 수 있 습 니 다.이 옵션 을 사용 하면 기본적으로 시스템 의 최적화 함 수 를 사용 하지 않 고 사용자 정의 함 수 를 사용 합 니 다.예 를 들 어 우 리 는 printf 를 정의 하 는 데 서 왔 다.
// printf.c
#include
#include
int printf(const char* format, ...) {
write(STDOUT_FILENO, "my printf
", 10);
write(STDOUT_FILENO, format, strlen(format));
return 0;
}
// main.c
#include
int main() {
printf("hello
");
return 0;
}
서로 다른 컴 파일 방식 의 결 과 를 관찰 하 다.
$ gcc -c printf.c
$ gcc main.c printf.o -fno-builtin
$ ./a.out
my printf
hello
$ gcc main.c printf.o
$ ./a.out
hello
signal
와 같은 최적화 되 지 않 은 함수(시스템 호출 포장 일 뿐)에 대해 직접 정적 링크 를 하면 된다.// signal.c
#include
#include // signal sigaction
typedef void Sigfunc(int);
Sigfunc* signal(int signo, Sigfunc* func) {
printf("%d
", signo);
return func;
}
// main.c
#include
int main() {
signal(SIGINT, SIG_DFL);
return 0;
}
$ gcc -c signal.c
$ gcc main.c signal.o
$ ./a.out
2
또한 매크로 정의 방식 으로 라 이브 러 리 함 수 를 교체 할 수 있 습 니 다.예 를 들 어
#define printf my_printf
int my_printf(const char* format, ...)
{
//
}
그러나 이러한 방법 을 추천 하지 않 습 니 다.매크로 교 체 는 컴 파일 하기 전에 이 루어 졌 기 때문에 최종 프로그램의 기호 정 보 는
printf
이 아니 라my_printf
이 고stdio.h
에서printf
에 대한 성명 도 의 미 를 잃 었 습 니 다.실제 호출 된 것 은my_printf
이기 때 문 입 니 다.이전 방법 을 사용 하면 기 존 코드 를 수정 하지 않 아 도 되 는 토대 에서 라 이브 러 리 함수 에 대한 재 작성 버 전 을 호출 할 수 있 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Docker를 사용한 React 및 .NET Core 6.0 샘플 프로젝트 - 1부이 기사에서는 Entity Framework Core Code First 접근 방식을 사용하는 ASP.NET Core 6.0 WEP API의 CRUD(만들기, 읽기, 업데이트 및 삭제) 작업에 대해 설명합니다. 웹 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.