Exception Table (extable)

Exception Table(extable)에는 프로세스 주소 공간(process address space)을 access하는 커널 API를 사용한 코드의 주소와 해당 API의 예외 처리 코드의 주소가 함께 담겨 있다.

  • 커널이 프로세스용으로 할당된 페이지를 읽다 에러가 발생한 경우 페이지 테이블에 아직 로드(lazy load)가 되어 있지 않아 추가적으로 로드를 해야 하는 경우도 있고 그렇지 않고 정말 access 할 수 없는 공간으로 access를 시도하다 발생한 exception인 경우도 있다. 따라서 이에 대한 처리를 하기 위한 코드들이 호출될 수 있도록 설계되었다.

프로세스가 특정 주소를 access 하다가 exception이 발생하는 여러 fault(fsr_info[])별 처리 핸들러 함수를 실행시킬 때 extable을 검사하여 에러 진원지와 동일한 엔트리를 찾은 경우 해당 fixup 코드를 실행시킨다.

  • 다음은 fsr_info[]에 연결되어 있는 처리함수들이다.
    • do_translation_fault()
      • “section translation fault”
    • do_page_fault()
      • “page translation fault”
      • “page permission fault”
    • do_sect_fault()
      • “section permission fault”

 

다음 함수들에서 .fixup 섹션과 __ex_table 섹션을 사용한다.

  • get_user(), strlen_user(), strnlen_user()
    • __get_user_asm()
  • put_user(), clear_user()
    • __put_user_asm()
  • 이 외에 영역 검사 및 futex 등 몇 개 더 있다.

 

다음 그림은 커널 코드가 process space에 있는 한 페이지에 접근하다 exception이 발생한 경우를 보여준다.

  • 페이지를 access할 때 exception이 발생하는 경우 Data Abort 또는 Prefetch Abort 등의 exception이 발생하고 해당 exception vector로 jump를 하여 관련 함수를 수행한다.

exception-table-1

 

search_exception_tables()

kernel/extable.c

/* Given an address, look for it in the exception tables. */
const struct exception_table_entry *search_exception_tables(unsigned long addr)
{
        const struct exception_table_entry *e;

        e = search_extable(__start___ex_table, __stop___ex_table-1, addr);
        if (!e)
                e = search_module_extables(addr);
        return e;
}

Main exception table과 전체 모듈에 있는 exception table의 insn 가상주소가 인수 addr와 같은 경우 해당 엔트리를 반환하고 검색되지 않는 경우 null을 리턴한다.

 

search_module_extables()

kernel/module.c

/* Given an address, look for it in the module exception tables. */
const struct exception_table_entry *search_module_extables(unsigned long addr)
{
        const struct exception_table_entry *e = NULL;
        struct module *mod;

        preempt_disable();
        list_for_each_entry_rcu(mod, &modules, list) {
                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;
                if (mod->num_exentries == 0)
                        continue;

                e = search_extable(mod->extable,
                                   mod->extable + mod->num_exentries - 1,
                                   addr);
                if (e)
                        break;
        }
        preempt_enable();

        /* Now, if we found one, we are running inside it now, hence
           we cannot unload the module, hence no refcnt needed. */
        return e;
}

전체 모듈을 루프를 돌며 각각의 모듈에 있는 exception table의 insn 가상주소가 인수 addr와 같은 경우 해당 엔트리를 반환하고 검색되지 않는 경우 null을 리턴한다.

 

search_extable()

lib/extable.c

#ifndef ARCH_HAS_SEARCH_EXTABLE
/*
 * Search one exception table for an entry corresponding to the
 * given instruction address, and return the address of the entry,
 * or NULL if none is found.
 * We use a binary search, and thus we assume that the table is
 * already sorted.
 */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
               const struct exception_table_entry *last,
               unsigned long value)
{
        while (first <= last) {
                const struct exception_table_entry *mid;

                mid = ((last - first) >> 1) + first;
                /*
                 * careful, the distance between value and insn
                 * can be larger than MAX_LONG:
                 */
                if (mid->insn < value)
                        first = mid + 1;
                else if (mid->insn > value)
                        last = mid - 1;
                else
                        return mid;
        }
        return NULL;
}
#endif

Exception Table의 insn 가상 주소가 인수 value로 binary 검색을 통해 인덱스를 찾아 반환한다. 검색이 실패한 경우 0을 반환한다.

 

구조체 및 전역변수

exception_table_entry 구조체

/*
 * The exception table consists of pairs of addresses: the first is the
 * address of an instruction that is allowed to fault, and the second is
 * the address at which the program should continue.  No registers are
 * modified, so it is entirely up to the continuation code to figure out
 * what to do.
 *
 * All the routines below use bits of fixup code that are out of line
 * with the main instruction path.  This means when everything is well,
 * we don't even have to jump over them.  Further, they do not intrude
 * on our cache or tlb entries.
 */

struct exception_table_entry
{
        unsigned long insn, fixup;
};
  • insn
    • process(user) space에 접근하는 커널 API의 가상 주소
  • fixup
    • 해당 API에 대한 예외 처리 코드를 가리키는 가상 주소

fsr_info[]

static struct fsr_info fsr_info[] = {
        /*
         * The following are the standard ARMv3 and ARMv4 aborts.  ARMv5
         * defines these to be "precise" aborts.
         */
        { do_bad,               SIGSEGV, 0,             "vector exception"                 },
        { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
        { do_bad,               SIGKILL, 0,             "terminal exception"               },
        { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
        { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
        { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
        { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
        { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
        { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
        { do_bad,               SIGBUS,  0,             "external abort on translation"    },
        { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
        { do_bad,               SIGBUS,  0,             "external abort on translation"    },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
        /*
         * The following are "imprecise" aborts, which are signalled by bit
         * 10 of the FSR, and may not be recoverable.  These are only
         * supported if the CPU abort handler supports bit 10.
         */
        { do_bad,               SIGBUS,  0,             "unknown 16"                       },
        { do_bad,               SIGBUS,  0,             "unknown 17"                       },
        { do_bad,               SIGBUS,  0,             "unknown 18"                       },
        { do_bad,               SIGBUS,  0,             "unknown 19"                       },
        { do_bad,               SIGBUS,  0,             "lock abort"                       }, /* xscale */
        { do_bad,               SIGBUS,  0,             "unknown 21"                       },
        { do_bad,               SIGBUS,  BUS_OBJERR,    "imprecise external abort"         }, /* xscale */              
        { do_bad,               SIGBUS,  0,             "unknown 23"                       },
        { do_bad,               SIGBUS,  0,             "dcache parity error"              }, /* xscale */              
        { do_bad,               SIGBUS,  0,             "unknown 25"                       },
        { do_bad,               SIGBUS,  0,             "unknown 26"                       },
        { do_bad,               SIGBUS,  0,             "unknown 27"                       },
        { do_bad,               SIGBUS,  0,             "unknown 28"                       },
        { do_bad,               SIGBUS,  0,             "unknown 29"                       },
        { do_bad,               SIGBUS,  0,             "unknown 30"                       },
        { do_bad,               SIGBUS,  0,             "unknown 31"                       },
};

exception이 발생했을 때 fault가 32개로 구분되며 해당 fault 핸들러 함수가 등록된다.

 

참고

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다