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

비밀번호

댓글 작성자
 




... 91  92  93  94  95  96  97  98  99  100  101  [102]  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11482정성태4/10/201824462VC++: 126. CUDA Core 수를 알아내는 방법
11481정성태4/10/201837228개발 환경 구성: 357. CUDA의 인덱싱 관련 용어 - blockIdx, threadIdx, blockDim, gridDim
11480정성태4/9/201826947.NET Framework: 736. C# - API를 사용해 Azure에 접근하는 방법 [2]파일 다운로드1
11479정성태4/9/201820840.NET Framework: 735. Azure - PowerShell로 Access control(IAM)에 새로운 계정 만드는 방법
11478정성태11/8/201924863디버깅 기술: 115. windbg - 덤프 파일로부터 PID와 환경변수 등의 정보를 구하는 방법 [1]
11477정성태4/8/201821290오류 유형: 460. windbg - sos 명령어 수행 시 c0000006 오류 발생
11476정성태4/8/201822708디버깅 기술: 114. windbg - !threads 출력 결과로부터 닷넷 관리 스레드(System.Threading.Thread) 객체를 구하는 방법
11475정성태3/28/201826590디버깅 기술: 113. windbg - Thread.Suspend 호출 시 응용 프로그램 hang 현상에 대한 덤프 분석
11474정성태3/27/201824451오류 유형: 459. xperf: error: TEST.Event: Invalid flags. (0x3ec).
11473정성태3/22/201829660.NET Framework: 734. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상파일 다운로드2
11472정성태3/22/201822163개발 환경 구성: 356. GTX 1070, GTX 960, GT 640M의 cudaGetDeviceProperties 출력 결과
11471정성태3/20/201825109VC++: 125. CUDA로 작성한 RGB2RGBA 성능 [1]파일 다운로드1
11470정성태3/20/201828463오류 유형: 458. Visual Studio - CUDA 프로젝트 빌드 시 오류 C1189, expression must have a constant value
11469정성태3/19/201821436오류 유형: 457. error MSB3103: Invalid Resx file. Could not load file or assembly 'System.Windows.Forms, ...' or one of its dependencies.
11468정성태3/19/201820004오류 유형: 456. 닷넷 응용 프로그램 실행 시 0x80131401 예외 발생
11467정성태3/19/201820769오류 유형: 455. Visual Studio Installer - 업데이트 실패
11466정성태3/18/201820295개발 환경 구성: 355. 한 대의 PC에서 2개 이상의 DirectX 게임을 실행하는 방법
11463정성태3/15/201822385.NET Framework: 733. 스레드 간의 read/write 시에도 lock이 필요 없는 경우파일 다운로드1
11462정성태3/14/201828242개발 환경 구성: 354. HTTPS 호출에 대한 TLS 설정 확인하는 방법 [1]
11461정성태3/13/201829144오류 유형: 454. 윈도우 업데이트 설치 오류 - 0x800705b4 [1]
11460정성태3/13/201820432디버깅 기술: 112. windbg - 닷넷 메모리 덤프에서 전역 객체의 내용을 조사하는 방법
11459정성태3/13/201822439오류 유형: 453. Debug Diagnostic Tool에서 mscordacwks.dll을 찾지 못하는 문제
11458정성태2/21/201823301오류 유형: 452. This share requires the obsolete SMB1 protocol, which is unsafe and could expose your system to attack. [1]
11457정성태2/17/201826527.NET Framework: 732. C# - Task.ContinueWith 설명 [1]파일 다운로드1
11456정성태2/17/201833072.NET Framework: 731. C# - await을 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법 [7]파일 다운로드1
11455정성태2/17/201822610오류 유형: 451. ASP.NET Core - An error occurred during the compilation of a resource required to process this request.
... 91  92  93  94  95  96  97  98  99  100  101  [102]  103  104  105  ...