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

비밀번호

댓글 작성자
 




... [136]  137  138  139  140  141  142  143  144  145  146  147  148  149  150  ...
NoWriterDateCnt.TitleFile(s)
1753정성태9/22/201424465오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201444288Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201442510.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201428424.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201427851.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201429532개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201432966오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201428804.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201427176개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201435507.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201425009오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201431657개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201425228.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201437003.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201430834.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201426579.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201423143VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201429828VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201422555.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201424213오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201430529.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201439334Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201431063개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
1730정성태8/11/201426557개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
1729정성태8/11/201422201오류 유형: 236. SqlConnection - The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.
1728정성태8/8/201434999.NET Framework: 453. C# - 오피스 파워포인트(Powerpoint) 파일을 WinForm에서 보는 방법파일 다운로드1
... [136]  137  138  139  140  141  142  143  144  145  146  147  148  149  150  ...