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

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  131  132  133  [134]  135  ...
NoWriterDateCnt.TitleFile(s)
1803정성태11/4/201423278오류 유형: 261. Windows Server Backup 오류 - Error in backup of E:\$Extend\$RmMetadata\$TxfLog
1802정성태11/4/201426538오류 유형: 260. 이벤트 로그 - Windows Error Reporting / AEAPPINVW8
1801정성태11/4/201432295오류 유형: 259. 이벤트 로그 - Windows Error Reporting / IPX Assertion / KorIME.exe [1]
1800정성태11/4/201422880오류 유형: 258. 이벤트 로그 - Starting a SMART disk polling operation in Automatic mode.
1799정성태11/4/201426821오류 유형: 257. 이벤트 로그 - The WMI Performance Adapter service entered the stopped state.
1798정성태11/4/201435479오류 유형: 256. 이벤트 로그 - The WinHTTP Web Proxy Auto-Discovery Service service entered the stopped state. [1]
1797정성태11/4/201421429오류 유형: 255. 이벤트 로그 - The Adobe Flash Player Update Service service entered the stopped state.
1796정성태10/30/201428677개발 환경 구성: 249. Visual Studio 2013에서 Mono 컴파일하는 방법
1795정성태10/29/201430520개발 환경 구성: 248. Lync 2013 서버 설치 방법
1794정성태10/29/201426182개발 환경 구성: 247. "Microsoft Office 365 Enterprise E3" 서비스에 대한 간략 소개
1793정성태10/27/201427217.NET Framework: 474. C# - chromiumembedded 사용 - 두 번째 이야기 [2]파일 다운로드1
1792정성태10/27/201427344.NET Framework: 473. WebClient 객체에 쿠키(Cookie)를 사용하는 방법
1791정성태10/22/201426789VC++: 83. G++ - 템플릿 클래스의 iterator 코드 사용에서 발생하는 컴파일 오류 [5]
1790정성태10/22/201421424오류 유형: 254. NETLOGON Service is paused on [... AD Server...]
1789정성태10/22/201425310오류 유형: 253. 이벤트 로그 - The client-side extension could not remove user policy settings for '...'
1788정성태10/22/201426603VC++: 82. COM 프로그래밍에서 HRESULT 타입의 S_FALSE는 실패일까요? 성공일까요? [2]
1787정성태10/22/201435481오류 유형: 252. COM 개체 등록시 0x8002801C 오류가 발생한다면?
1786정성태10/22/201436972디버깅 기술: 65. 프로세스 비정상 종료 시 "Debug Diagnostic Tool"를 이용해 덤프를 남기는 방법 [3]파일 다운로드1
1785정성태10/22/201426297오류 유형: 251. 이벤트 로그 - Load control template file /_controltemplates/TaxonomyPicker.ascx failed [1]
1784정성태10/22/201433489.NET Framework: 472. C/C++과 C# 사이의 메모리 할당/해제 방법파일 다운로드1
1783정성태10/21/201427147VC++: 81. 프로그래밍에서 borrowing의 개념
1782정성태10/21/201424157오류 유형: 250. 이벤트 로그 - Application Server job failed for service instance Microsoft.Office.Server.Search.Administration.SearchServiceInstance
1781정성태10/21/201425289디버깅 기술: 64. new/delete의 짝이 맞는 경우에도 메모리 누수가 발생한다면?
1780정성태10/15/201428648오류 유형: 249. The application-specific permission settings do not grant Local Activation permission for the COM Server application with CLSID
1779정성태10/15/201423863오류 유형: 248. Active Directory에서 OU가 지워지지 않는 경우
1778정성태10/10/201422469오류 유형: 247. The Netlogon service could not create server share C:\Windows\SYSVOL\sysvol\[도메인명]\SCRIPTS.
... 121  122  123  124  125  126  127  128  129  130  131  132  133  [134]  135  ...