Microsoft MVP성태의 닷넷 이야기
Linux: 122. eBPF - __attribute__((preserve_access_index)) 사용법 [링크 복사], [링크+제목 복사],
조회: 1199
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 16개 있습니다.)
Linux: 86. Golang + bpf2go를 사용한 eBPF 기본 예제
; https://www.sysnet.pe.kr/2/0/13769

Linux: 94. eBPF - vmlinux.h 헤더 포함하는 방법 (bpf2go에서 사용)
; https://www.sysnet.pe.kr/2/0/13783

Linux: 95. eBPF - kprobe를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13784

Linux: 96. eBPF (bpf2go) - fentry, fexit를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13788

Linux: 100.  eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)
; https://www.sysnet.pe.kr/2/0/13801

Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
; https://www.sysnet.pe.kr/2/0/13810

Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
; https://www.sysnet.pe.kr/2/0/13815

Linux: 106. eBPF (bpf2go) - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
; https://www.sysnet.pe.kr/2/0/13817

Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
; https://www.sysnet.pe.kr/2/0/13819

Linux: 109. eBPF (bpf2go) - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
; https://www.sysnet.pe.kr/2/0/13824

Linux: 110. eBPF (bpf2go) - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
; https://www.sysnet.pe.kr/2/0/13825

Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
; https://www.sysnet.pe.kr/2/0/13893

Linux: 116. eBPF (bpf2go) - BTF Style Maps 정의 구문과 데이터 정렬 문제
; https://www.sysnet.pe.kr/2/0/13894

Linux: 117. eBPF (bpf2go) - Map에 추가된 요소의 개수를 확인하는 방법
; https://www.sysnet.pe.kr/2/0/13895

Linux: 122. eBPF - __attribute__((preserve_access_index)) 사용법
; https://www.sysnet.pe.kr/2/0/14016

Linux: 123. eBPF (bpf2go) - BPF_PROG_TYPE_SOCKET_FILTER 예제 - SEC("socket")
; https://www.sysnet.pe.kr/2/0/14017




eBPF - __attribute__((preserve_access_index)) 사용법

아래의 글에 보면,

eBPF application development: Beyond the basics
; https://developers.redhat.com/articles/2023/10/19/ebpf-application-development-beyond-basics

The BPF program attaches to the net_dev_queue kernel tracepoint to intercept all packets sent or received by any program running on the host. The tracepoint context includes a pointer to the struct sk_buff that holds the packet data. We are only interested in accessing thesk_buff->data and sk_buff->len fields so we can use CO-RE to access them. The BPF program defines its own private version of struct sk_buff that contains only the fields we need. The struct is annotated with the preserve_access_index attribute so that the CO-RE relocation can happen when the BPF program gets loaded.


"Accessing kernel data structures" 절에서 "__attribute__((preserve_access_index)"에 대한 설명이 나옵니다. 정리해 보면, 필요한 커널 구조체가 있을 때 원래의 그 타입이 소유한 모든 필드를 정의할 필요 없이 관심 있는 필드만 정의할 수 있게 해주는 attribute입니다. (어차피 이름이 같은 field의 offset을 런타임 시에 eBPF 로더가 계산해 주므로.)

예를 들어, task_struct를 사용한다고 가정해 볼까요? ^^ 전에도 설명했지만 task_struct는 너무 크고 빌드 시 정의한 전처리 상수에 따라 유동적이므로 그런 구조체의 정의를 BPF 내에서 직접 정의해 사용하는 것은 좀 애매합니다. 바로 그럴 때, __attribute__((preserve_access_index))를 사용해 관심 있는 필드만 정의할 수 있습니다.

struct task_struct {
    unsigned int flags;
    const struct cred *cred;
} __attribute__((preserve_access_index));

한 가지 유의할 점이라면, 저렇게 정의할 수 있는 구조체는 "vmlinux.h"에 정의되지 않은 구조체여야 한다는 점입니다. 왜냐하면 같은 이름의 구조체를 재정의할 수 없기 때문입니다.

즉, 약식 정의한 구조체의 이름을 task_struct라고 정의하면, vmlinux.h에 이미 동일한 이름으로 정의돼 있으므로 컴파일 오류가 발생하는 것입니다. (사실, 굳이 vmlinux.h에 있는 구조체를 재정의할 필요가 없긴 합니다.) 그렇다고 해서, 대신 my_task_struct 같은 이름을 사용하면 어떻게 될까요?

struct my_task_struct {
    unsigned int flags;
    const struct cred *cred;
} __attribute__((preserve_access_index));

SEC("tracepoint/syscalls/sys_enter_execve")
int sys_enter_execve(struct pt_regs *ctx) {
    struct my_task_struct *current_task = (struct my_task_struct *)bpf_get_current_task();

    unsigned int task_flags = BPF_CORE_READ(current_task, flags);

    bpf_printk("task_flags == %d\n", task_flags);

    return 0;
}

저렇게 하면 일단 (bpf2go 등의) 컴파일 단계는 통과하지만, 이후의 실행 단계에서 bpf 프로그램을 로드할 때 다음과 같은 오류가 발생합니다.

program sys_enter_execve: load program: bad CO-RE relocation: invalid func unknown#195896080 (6 line(s) omitted)

왜냐하면 CO-RE 재배치(relocation)를 위해서는 구조체의 이름 정보까지 동일해야 하기 때문입니다.




그렇다면, vmlinux.h 헤더를 포함시키지 않으면 되지 않을까요? 그러기에는 ^^; vmlinux 헤더 파일에 포함된 수많은 정의를 사용할 수 없어 더 큰 불편이 발생합니다.

그래도 아주 불가능한 것은 아닌데요, 이에 대한 좋은 예제를 cilium에서 찾아볼 수 있습니다.

examples/headers
; https://github.com/cilium/ebpf/tree/main/examples/headers

examples/tracepoint_in_c/tracepoint.c
; https://github.com/cilium/ebpf/blob/main/examples/tracepoint_in_c/tracepoint.c

tracepoint.c 예제를 보면, "examples/headers" 경로에 있는 common.h 헤더만 포함시키고 있는데요, 그 파일(및 그것이 포함하는 헤더 파일)은 vmlinux.h의 약식 버전에 해당합니다. 즉, task_struct 같은 커널 구조체 정의는 포함하고 있지 않으므로 다음과 같이 코딩할 수 있습니다.

#include "common.h"
#include <bpf/bpf_core_read.h>

#define TASK_COMM_LEN 16

struct task_struct {
    unsigned int flags;
    const struct cred *cred;
    char comm[TASK_COMM_LEN];
} __attribute__((preserve_access_index));

SEC("tracepoint/syscalls/sys_enter_execve")
int sys_enter_execve(struct pt_regs *ctx) {
    struct task_struct *current_task = (struct task_struct *)bpf_get_current_task();

    unsigned int task_flags = BPF_CORE_READ(current_task, flags);

    bpf_printk("task_flags == %d, %s\n", task_flags, current_task->comm);

    return 0;
}

당연하겠지만, 저 attribute가 적용된 구조체는 BTF가 활성화된 OS에서만 사용 가능합니다. 만약 BTF가 비활성화된 OS에서 저 BPF 프로그램을 로딩하면 다음과 같은 오류가 발생합니다.

program sys_enter_execve: apply CO-RE relocations: load kernel spec: btf: not found




그런데... 굳이 저 옵션이 (vmlinux.h 포함 여부에 상관없이) task_struct와 같은 커널 구조체에 대해 필요한가...입니다. 왜냐하면 vmlinux.h 헤더에 정의된 task_struct도 preserve_access_index 옵션 없이 정의돼 있기 때문입니다. 실제로, (vmlinux.h를 없애고 common.h로 실습한) 위의 예제에서도 task_struct를 preserve_access_index 없이 정의해도 잘 동작합니다. 그러니까, 어차피 BTF 정보가 있는 타입이라면 굳이 저 속성을 붙이지 않아도 된다는 뜻입니다.

그런 의미에서 "eBPF application development: Beyond the basics" 글에서 설명한 저 속성의 용도는 딱히 와닿지 않습니다.

그렇다고 그냥 사용자 코드에서 정의한 구조체에 저 속성을 붙이는 것도,

[BPF] Add preserve_access_index attribute for record definition
; https://reviews.llvm.org/D69759

의미 있어 보이진 않습니다. 혹시 저 옵션에 대한 이력이나 관련 설명을 해주실 분이 계실까요? ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 10/3/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 166  167  [168]  169  170  171  172  173  174  175  176  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
942정성태11/7/201048085.NET Framework: 187. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 [7]파일 다운로드3
941정성태11/6/201029551.NET Framework: 186. windbg로 확인하는 .NET CLR LCG 메서드(DynamicMethod) [1]파일 다운로드1
940정성태11/6/201030769.NET Framework: 185. windbg로 확인하는 .NET CLR 메서드파일 다운로드1
939정성태10/24/201037256Windows: 51. RDP로 접속한 Windows Server 2008 R2 환경에서 Aero Glass 활성화 [1]
938정성태10/23/201030051디버깅 기술: 29. Windbg - Hyper-V 윈도우 7 원격 디버깅 구성 [1]
937정성태10/22/201035534DDK: 5. NT Legacy 드라이버: 프로세스(EXE) 생성/제거 모니터링 [3]파일 다운로드1
936정성태10/21/201034423DDK: 4. Device Driver 응용 프로그램의 빌드 스크립트 - 두 번째 이야기 [2]파일 다운로드1
935정성태10/17/201035206디버깅 기술: 28. Windbg - 윈도우 핸들 테이블 [3]
934정성태10/11/201038483디버깅 기술: 27. Windbg - Local Kernel Debug 모드 [2]
933정성태10/10/201030335.NET Framework: 184. 닷넷에서 호출 스택의 메서드에 대한 인자 값 확인이 가능할까? [2]파일 다운로드1
932정성태10/10/201033666DDK: 3. NT Legacy 드라이버를 이용하여 C#에서 Port 입출력파일 다운로드1
931정성태9/30/201028269오류 유형: 108. Net.Tcp Listener Adapter 서비스 시작 실패
930정성태9/30/201027171웹: 16. 윈도우 미디어 플레이어 - 일시 정지/시작을 스크립트에서 감지
929정성태9/17/201028593웹: 15. IE 9 - 작업 표시줄의 웹 사이트 바로가기 사용자 정의 - JumpLists [3]파일 다운로드1
928정성태9/16/201032350VC++: 42. 쓰기 전용 파일(예: 로그 파일)의 동기화 방법파일 다운로드1
927정성태9/14/201042904VC++: 41. UTF-8 포맷의 INI 파일에 대한 GetPrivateProfile... API 사용 불가 [2]
926정성태9/9/201032217Team Foundation Server: 41. 빌드 스크립트에 Code Coverage 추가 [1]파일 다운로드1
925정성태9/8/201035916Team Foundation Server: 40. Visual Studio 2010 - Code Coverage 결과를 외부 XML 파일로 출력하는 명령행 도구 제작 [1]파일 다운로드1
924정성태9/6/201025401개발 환경 구성: 88. SCVMM이 설치된 도메인에 참여하지 않은 Hyper-V 호스트 추가
923정성태9/5/201031998오류 유형: 107. SCVMM Agent 설치 오류 - Failed to configure the WS-Management service.
922정성태9/4/201040913오류 유형: 106. Hyper-V 가상 머신의 네트워크 끊김 현상
921정성태9/2/201034861DDK: 2. Device Driver 응용 프로그램의 빌드 스크립트 [2]파일 다운로드1
920정성태9/1/201039803오류 유형: 105. WMI - The RPC server is unavailable [2]
919정성태8/30/201045828DDK: 1. Visual Studio 2010 - Device Driver 제작- Hello World 예제 [3]파일 다운로드1
918정성태8/28/201031395개발 환경 구성: 87. Hyper-V의 네트워크 유형 (3)
917정성태8/26/201026250개발 환경 구성: 86. "Routing and Remote Access"의 "Routing" 기능 활성화 방법
... 166  167  [168]  169  170  171  172  173  174  175  176  177  178  179  180  ...