Microsoft MVP성태의 닷넷 이야기
Linux: 122. eBPF - __attribute__((preserve_access_index)) 사용법 [링크 복사], [링크+제목 복사],
조회: 867
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 76  77  78  79  80  81  82  83  84  85  86  87  [88]  89  90  ...
NoWriterDateCnt.TitleFile(s)
11834정성태3/4/201924311개발 환경 구성: 432. Visual Studio 없이 최신 C# (8.0) 컴파일러를 사용하는 방법
11833정성태3/4/201926199개발 환경 구성: 431. Visual Studio 2019 - CMake를 이용한 공유/실행(so/out) 리눅스 프로젝트 설정파일 다운로드1
11832정성태3/4/201919821오류 유형: 524. Visual Studio CMake - rsync: connection unexpectedly closed
11831정성태3/4/201921769오류 유형: 523. Visual Studio 2019 - 새 창으로 뜬 윈도우를 닫을 때 비정상 종료
11830정성태2/26/201920143오류 유형: 522. 이벤트 로그 - Error opening event log file State. Log will not be processed. Return code from OpenEventLog is 87.
11829정성태2/26/201921134개발 환경 구성: 430. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 - 리눅스 환경 [1]
11828정성태2/26/201930311개발 환경 구성: 429. Component Services 관리자의 RuntimeBroker 설정이 2개 있는 경우 [8]
11827정성태2/26/201922516오류 유형: 521. Visual Studio - Could not start the 'rsync' command on the remote host, please install it using your system package manager.
11826정성태2/26/201922789오류 유형: 520. 우분투에 .NET Core SDK 설치 시 패키지 의존성 오류
11825정성태2/25/201929370개발 환경 구성: 428. Visual Studio 2019 - CMake를 이용한 리눅스 빌드 환경 설정 [1]
11824정성태2/25/201923243오류 유형: 519. The SNMP Service encountered an error while accessing the registry key SYSTEM\CurrentControlSet\Services\SNMP\Parameters\TrapConfiguration. [1]
11823정성태2/21/201923707오류 유형: 518. IIS 관리 콘솔이 뜨지 않는 문제
11822정성태2/20/201923803오류 유형: 517. docker에 설치한 MongoDB 서버로 연결이 안 되는 경우
11821정성태2/20/201924563오류 유형: 516. Visual Studio 2019 - This extension uses deprecated APIs and is at risk of not functioning in a future VS update. [1]
11820정성태2/20/201927794오류 유형: 515. 윈도우 10 1809 업데이트 후 "User Profiles Service" 1534 경고 발생
11819정성태2/20/201926707Windows: 158. 컴퓨터와 사용자의 SID(security identifier) 확인 방법
11818정성태2/20/201924480VS.NET IDE: 131. Visual Studio 2019 Preview의 닷넷 프로젝트 빌드가 20초 이상 걸리는 경우 [2]
11817정성태2/17/201920067오류 유형: 514. WinDbg Preview 실행 오류 - Error : DbgX.dll : WindowsDebugger.WindowsDebuggerException: Could not load dbgeng.dll
11816정성태2/17/201924821Windows: 157. 윈도우 스토어 앱(Microsoft Store App)을 명령행에서 직접 실행하는 방법
11815정성태2/14/201923038오류 유형: 513. Visual Studio 2019 - VSIX 설치 시 "The extension cannot be installed to this product due to prerequisites that cannot be resolved." 오류 발생
11814정성태2/12/201920484오류 유형: 512. VM(가상 머신)의 NT 서비스들이 자동 시작되지 않는 문제
11813정성태2/12/201921387.NET Framework: 809. C# - ("Save File Dialog" 등의) 대화 창에 확장 속성을 보이는 방법
11812정성태2/11/201919034오류 유형: 511. Windows Server 2003 VM 부팅 후 로그인 시점에 0xC0000005 BSOD 발생
11811정성태2/11/201926385오류 유형: 510. 서버 운영체제에 NVIDIA GeForce Experience 실행 시 wlanapi.dll 누락 문제
11810정성태2/11/201921592.NET Framework: 808. .NET Profiler - GAC 모듈에서 GAC 비-등록 모듈을 참조하는 경우의 문제
11809정성태2/11/201925331.NET Framework: 807. ClrMD를 이용해 메모리 덤프 파일로부터 특정 인스턴스를 참조하고 있는 소유자 확인
... 76  77  78  79  80  81  82  83  84  85  86  87  [88]  89  90  ...