일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- BOF
- ioctl
- iptables
- LOB
- epoll
- packet flow
- Compiler
- ncurses
- C언어
- 취약점
- wrefresh
- 어셈블리어
- .net core 7
- architecture
- mvwin
- NAPT
- doupdate
- REDIS
- wnourefresh
- cbpf
- DOCKER-USER
- edge trigger
- packet filter
- 풀이
- .nret core 배포
- level trigger
- Docker
- vtable
- epoll_wait
- rfc5508
- Today
- Total
Tuuna Computer Science
container_of & offsetof 매크로 분석 본문
1-day 리눅스 커널 취약점 찾기 스터디중에 container_of 매크로랑 offsetof매크로를 분석하라는 과제를 받음.
구글링하다가 위 둘 매크로는 디바이스 드라이버 제잘할 때 자주 사용하는 매크로임을 깨달음.
분석에 앞서 ㅈㄴ 괴기하다.
[ offsetof 매크로 (부모 구조체를 기준으로 멤버의 오프셋 구함) ]
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
offsetof 매크로의 경우 구조체 타입과 오프셋을 원한 구조체 멤버를 받음
((size_t)&((TYPE *)0)->MEMBER)
코드를 보면 0이라는 값을 TYPE*형으로 치환한다음이 것이 MEMBER을 가리키고 이 주소값을 반환하는 코드인거 같음.
보니까 TYPE*은 우리가 넘긴 구조체 포인터형이다.
정리하면 아예 가상의 구조체를 주소 0으로 자리잡고 0으로 부터 떨어진 member의 주소를 반환하게 하는 것이다.
0에서 시작된 주소이니 offset으로 표현이 가능해짐.
[ containerof 매크로 (해당 member가 있는 부모 구조체의 반환 ]
#ifndef container_of
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
container_of매크로는 특정 멤버를 통해 구조체 주소를 구하는 매크로이다.
내부적으로 offsetof매크로 사용
이를 코드상으로 치환하면
container_of(&(point.y), struct Point, y));
#define container_of(&(pint.y), struct Point, y) ({ \
const typeof(((struct Point *)0)->y) * __mptr = (&(point.y)); \
(struct Point *)((char *)__mptr - offsetof(struct Point, y)); })
구조체의 주소 = (구조체 멤버의 주소값 - offsetof()를 통한 구조체 멤버의 오프셋)
3개의 인자를 받는데 먼저 주석을 참고하면
ptr : 구조체 멤버의 주소값
type : 구조체 타입
member : 구조체 멤버
typeof 연산자란 gcc의 확장기능으로서 typeof(a) b; 일시 a가 int형이면 b도 int으로 타입 캐스팅이 된다.
const typeof(((type *)0)->member) * __mptr = (ptr);
=> 처음 0은 구조체 포인터형으로 캐스팅 후에는 typeof를 통해 __mptr을 캐스팅한다.
=> point.y의 주소값을 받아야 하니 이에맞는 포인터형으로 변환할 필요가 있음!
정리하면 받는 ptr을 가리키게 하기 위해서는 ptr과 같은 포인터형이 되어야한다.
ptr은 구조체 멤버의 주소이니 받는 포인터도 구조체 멤버의 포인터형이 되어야 한다.
그래서 type*)0)->member)* __mptr이 된거고 이를 구조체 멤버 포인터형으로 타입 캐스팅을 하기위해 typeof연산자를 사용한 것.
그러면 최종적으로 __mptr은 구조체 멤버의 주소값을 나타내고 있다. 이를 char*포인터형으로 타입 캐스팅을 하여 offsetof을 통해 나온 멤버의 주소값을 뺀다. 그럼 구조체의 주소값이 나오게 된다.
Q. 의문점
char* 포인터형으로 왜 타입 캐스팅을 한것인가?
A. offsetof같은 거는 1바이트형식으로 계산해야하는데 int*같은거는 4바이트단위이니 1바이트형으로 할 수 있게 char*으로 캐스팅해야함.
'C language' 카테고리의 다른 글
[ROTATE SHIFT] C 언어 회전시프트 TIP (0) | 2019.05.18 |
---|---|
[C 언어] fgets함수 분석과, stdin 모험 (0) | 2019.03.31 |
[안드로이드 게임]텐트와 나무 게임 알고리즘 분석해보기(머리로 풀지 말고 컴퓨터로 풀자) (22) | 2018.11.25 |
시저암호 암호화 복호화 소스 (0) | 2018.11.02 |
[C Language] 배열없이 문자열의 빈도수 구하기 (0) | 2018.10.26 |