포스트

bootlean glibc 2.35 malloc.c 분석

bootlean glibc 2.35 malloc.c _int_malloc, _int_free 함수 분석하기

bootlean glibc 2.35 malloc.c 분석

_int_malloc, _int_free 함수 코드를 분석하는 것이 목표다
함수와 매크로를 따라가면서 정리한다

본인은 운영체제, 컴퓨터 구조 등 CS 지식이 빈약하기 때문에 최대한 상세하게 작성해볼 생각이다

https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c

1. _int_malloc()

1
2
// mallo/malloc.c
static void*  _int_malloc(mstate, size_t); // 1108 line

인자로 mstate, size_t를 받는다


1-1. 자료형 mstate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// include/malloc.h
#ifndef _MALLOC_H

#include <malloc/malloc.h>

# ifndef _ISOMAC
#  include <rtld-malloc.h>

struct malloc_state;
typedef struct malloc_state *mstate;

# endif /* !_ISOMAC */

#endif

include/malloc.h 헤더 파일에 mstate 구조체 자료형이 typedef로 정의되어 있다 malloc_state 구조체를 가리키는 포인터 자료형이다

1
2
3
// malloc/malloc.h
#ifndef _MALLOC_H
#define _MALLOC_H 1

_MALLOC_Hmalloc/malloc.h 헤더 파일에서 정의된다

_MALLOC_H가 정의되어 있지 않을 경우 1로 정의되기 때문에 include/malloc.h 헤더 파일에서 malloc/malloc.h 헤더 파일을 참조할 수 있게 된다

1
2
3
4
// include/libc-symbols.h
#if IS_IN (testsuite) || defined IS_IN_build || defined __cplusplus
# define _ISOMAC 1
#endif

_ISOMACinclude/libc-symbols.h 헤더 파일에서 1로 정의된다

glibc 자체 테스트 코드 컴파일 또는 빌드 도중 생성되는 보조 도구 컴파일 또는 C++ 컴파일 시 자동으로 1로 정의되는 것을 알 수 있다

1-1-1. malloc_state 구조체

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// malloc/malloc.c
struct malloc_state
{
  /* Serialize access. 접근을 직렬화한다  */
  __libc_lock_define (, mutex);

  /* Flags (formerly in max_fast). 플래그 (이전에는 max_fast)  */
  int flags;

  /* Set if the fastbin chunks contain recently inserted free blocks.  */
  /* fastbin 청크가 최근에 삽입된 free 청크들을 포함할 때 설정됨 */
  /* Note this is a bool but not all targets support atomics on booleans.  */
  /* bool 타입이지만 모든 대상이 boolean에 atomics를 지원하지는 않음 */
  int have_fastchunks;

  /* Fastbins */
  mfastbinptr fastbinsY[NFASTBINS];

  /* Base of the topmost chunk -- not otherwise kept in a bin */
  /* 가장 꼭대기 청크의 베이스 -- bin에서 유지됨 */
  mchunkptr top;

  /* The remainder from the most recent split of a small request */
  /* 가장 최근에 분할된 작은 요청의 나머지 */
  mchunkptr last_remainder;

  /* Normal bins packed as described above */
  /* 위에서 설명했듯이 패킹된 일반 bins */
  mchunkptr bins[NBINS * 2 - 2];

  /* Bitmap of bins */
  unsigned int binmap[BINMAPSIZE];

  /* Linked list 연결리스트 */
  struct malloc_state *next;

  /* Linked list for free arenas.  Access to this field is serialized
     by free_list_lock in arena.c.  */
  /* free 아레나 연결리스트.
     이 필드에 대한 접근은 arena.c의 free_list_lock에 의해 직렬화됨 */
  struct malloc_state *next_free;

  /* Number of threads attached to this arena.  0 if the arena is on
     the free list.  Access to this field is serialized by
     free_list_lock in arena.c.  */
  /* 이 아레나에 연결된 스레드의 수
     아레나가 free 리스트에 있으면 0
     이 필드에 대한 접근은 arena.c의 free_list_lock에 의해 직렬화됨 */
  INTERNAL_SIZE_T attached_threads;

  /* Memory allocated from the system in this arena.  */
  /* 이 아레나에서 시스템으로부터 할당 받은 메모리 */
  INTERNAL_SIZE_T system_mem;
  INTERNAL_SIZE_T max_system_mem;
};

위와 같은 구조체 구조를 가진다 연결리스트 형식으로 구현되는 것을 알 수 있다

주로 스레드 아레나와 관련된 정보를 저장한다 멀티스레드 환경에서 여러 스레드가 독립적으로 메모리 할당을 효율적이고 안전하게 수행할 수 있도록 한다

해당 아레나에 속한 힙의 정보(크기, 시작 주소, 이전/다음 힙 정보), 할당 및 해제 추적, 동기화 관련 정보를 포함한다

malloc() 함수는 요청된 메모리 크기대로 메모리 블록을 할당할 때 malloc_state 구조체를 참조하여 메모리 블록을 찾고 필요하면 새로운 힙을 할당받거나 확장한다 free() 함수는 malloc_state 구조체를 업데이트하며 할당된 메모리 블록을 해제한다

  • __libc_lock_define (, mutex)

    1
    2
    
      /* Serialize access. 접근을 직렬화한다  */
      __libc_lock_define (, mutex);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      // sysdeps/mach/libc-lock.h
      typedef unsigned int __libc_lock_t;
        
      ...
        
      /* Define a lock variable NAME with storage class CLASS.  The lock must be
         initialized with __libc_lock_init before it can be used (or define it
         with __libc_lock_define_initialized, below).  Use `extern' for CLASS to
         declare a lock defined in another module.  In public structure
         definitions you must use a pointer to the lock structure (i.e., NAME
         begins with a `*'), because its storage size will not be known outside
         of libc.  */
      #define __libc_lock_define(CLASS,NAME) \
        CLASS __libc_lock_t NAME;
    

    mutex 변수를 __libc_lock_define 매크로에 전달한다

    사실상 __libc_lock_t 자료형으로 mutex 이름의 변수를 선언하는 것과 같다

    1
    
      __libc_lock_t mutex;
    

    사실상 위와 같은 의미이다 malloc_state 내부에 플랫폼 독립적 뮤텍스 변수 mutex를 선언하라는 매크로 호출이다

    아레나에 대한 접근을 동기화하는 뮤텍스이다

    sysdeps/mach/libc-lock.h, sysdeps/generic/libc-lock.h, sysdeps/nptl/libc-P.h 처럼 플랫폼별 버전이 존재하는데 __libc_lock_t로 묶어서 하나의 매크로를 사용할 수 있도록 한다 덕분에 glibc는 다양한 플랫폼에서 동일한 소스를 유지하며 빌드 설정에 맞춰 최적의 안전한 락을 자동으로 선택할 수 있다 해당 소스에서는 __libc_lock_t를 부호 없는 정수형으로 사용한다

    glibc 내부의 락(뮤텍스) 변수를 선언하기 위한 매크로이다 이 락을 통해 malloc_state 하나에 여러 스레드가 동시에 들어오지 못하도록 접근을 직렬화한다.

    매크로의미
    0x0LLL_LOCK_INITIALIZER열려 있음
    0x1LLL_LOCK_INITIALIZER_LOCKED잠금, 대기자 없음
    0x2LLL_LOCK_INITIALIZER_WAITERS잠금, 대기자 존재

    LLL_LOCK_INITIALIZER, LLL_LOCK_INITIALIZER_LOCKED는 각각 sysdeps/nptl/lowlevellock.h 또는 mach/lowlevellock.h에 0, 1 정수 상수로 정의되어 있다 LLL_LOCK_INITAILIZER_WAITERS는 어디에 정의되어 있는지 모르겠다..

  • flags

    1
    2
    
      /* Flags (formerly in max_fast). 플래그 (이전에는 max_fast)  */
      int flags;
    

    아레나의 상태를 나타내는 플래그이다.

    비트매크로용도
    0FASTCHUNKS_BIT사용하지 않음
    1NONCONTIGUOS_BIT단일 연속 영역이 아닌지
    2~31(Reserved)정의되지 않음

    32비트 크기의 int 자료형을 잘라서 비트별로 의미를 가진다

    • FASTCHUNKS_BIT

      0번째 비트의 경우 glibc 2.26 버전 이전까지는 최근에 free된 청크가 있으면 플래그가 켜졌지만 have_fastchunks 독립 필드로 분리되었다 glibc 2.26 이전까지는 flags의 하위 8비트에 max_fast 값이 들어가고 상위 비트에는 FASTCHUNKS_BIT만 보관하였다 glibc 2.26부터 max_fast가 전역 변수화가 되면서 flags에서 제거되었고 FASTCHUNKS_BIT는 정의만 남고 실제로 사용되지는 않는다

      glibc 2.34-2.35에서 flags에서 NONCONTIGUOUS_BIT만 실제로 사용된다

    새 아레나가 mmap으로 생성되거나 ASLR 등으로 sbrk-힙 단절이 감지되는 경우에 켜진다 한 번 켜지면 꺼지지 않는다

    1
    2
    3
    4
    5
    6
    
      #define NONCONTIGUOUS_BIT     (2U)
        
      #define contiguous(M)          (((M)->flags & NONCONTIGUOUS_BIT) == 0)
      #define noncontiguous(M)       (((M)->flags & NONCONTIGUOUS_BIT) != 0)
      #define set_noncontiguous(M)   ((M)->flags |= NONCONTIGUOUS_BIT)
      #define set_contiguous(M)      ((M)->flags &= ~NONCONTIGUOUS_BIT)
    

    NONCONTIGUOUS_BIT는 초기 0값에 set_noncontiguous 에 의해 NONCONTIGUOUS_BIT 와의 OR 연산을 통해 설정되는데 NONCONTIGUOUS_BIT는 2U 값을 갖는다

  • have_fastchunks

    1
    2
    3
    4
    5
    
      /* Set if the fastbin chunks contain recently inserted free blocks.  */
      /* fastbin 청크가 최근에 삽입된 free 청크들을 포함할 때 설정됨 */
      /* Note this is a bool but not all targets support atomics on booleans.  */
      /* bool 타입이지만 모든 대상이 boolean에 atomics를 지원하지는 않음 */
      int have_fastchunks;
    

    fastbin 안에 최근에 free된 블록이 있는지를 나타내는 플래그이다 0과 1로 나타낸다

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
      // malloc/malloc.c
      static void
      malloc_init_state (mstate av)
      {
        int i;
        mbinptr bin;
        
        /* Establish circular links for normal bins */
        for (i = 1; i < NBINS; ++i)
          {
            bin = bin_at (av, i);
            bin->fd = bin->bk = bin;
          }
        
      #if MORECORE_CONTIGUOUS
        if (av != &main_arena)
      #endif
        set_noncontiguous (av);
        if (av == &main_arena)
          set_max_fast (DEFAULT_MXFAST);
        atomic_store_relaxed (&av->have_fastchunks, false);
        
        av->top = initial_top (av);
      }
    

    malloc_init_state() 함수가 호출되면 인자로 받은 mstatehave_fastchunks를 0(False)로 설정한다

  • top

    힙의 마지막 할당 위치를 가리키는 포인터

  • last_remainder

    마지막으로 사용된 메모리 조각의 정보 저장

이 게시글은 저작권자의 CC BY 4.0 라이센스를 따릅니다.