ARM Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

203239
1625
172600


[리눅스][v3.18] 모듈 방식 드라이버를 설치할 때 커널 코드 분석(sys_init_module) 리눅스 디바이스 드라이버

Kernel Version: 3.18


터미널에서 'insmod module.ko' 명령어를 입력하면 모듈 형식 드라이버를 설치할 수 있습니다. 이번 포스팅에서는 이 과정에서 호출되는 함수를 분석합니다.

TRACE32로 콜스택 확인

먼저 콜스택을 보겠습니다.

-000|NSR:0xBF03B114(asm)  // <<--
-001|do_one_initcall_debug(inline)
-001|do_one_initcall(fn = 0xBF03B000)
-002|do_init_module(inline)
-003|load_module(info = 0xDE895F48, ?, ?)
-004|sys_init_module(umod = -1227014136, len = 284732, uargs = -1097475832)
-005|ret_fast_syscall(asm)

insmod에 대응하는 시스템 콜 핸들러인 sys_init_module() 함수가 호출된다는 사실을 알 수 있습니다.

sys_init_module() 함수 

먼저 sys_init_module() 함수를 보겠습니다.

https://elixir.bootlin.com/linux/v5.4/source/kernel/module.c
SYSCALL_DEFINE3(init_module, void __user *, umod,
        unsigned long, len, const char __user *, uargs)
{
    int err;
    struct load_info info = { };

    err = may_init_module();
    if (err)
        return err;

    pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
           umod, len, uargs);

    err = copy_module_from_user(umod, len, &info);
    if (err)
        return err;

    return load_module(&info, uargs, 0);
}

모듈 형식 드라이버의 기본 속성 정보를 저장한 후 load_module() 함수를 호출합니다.

https://elixir.bootlin.com/linux/v5.4/source/kernel/module.c
static int load_module(struct load_info *info, const char __user *uargs,
               int flags)
{
    struct module *mod;
    long err = 0;
    char *after_dashes;

    err = elf_header_check(info);

...
    /* Get rid of temporary copy. */
    free_copy(info);

    /* Done! */
    trace_module_load(mod);

    return do_init_module(mod);

load_module() 함수의 전체 코드를 보면 모듈 드라이버 파일이 오염됐다고 가정하고 다양한 방식으로 예외 처리 코드를 수행합니다.
이 함수에서 체크하는 정보는;
   * 모듈 파일의 해더 정보
   * SIGNATURE 정보
   * 섹션 정보

do_init_module() 함수 

do_init_module() 함수는 모듈 드라이버를 실행시키는 출발점입니다.
이 함수에서 모듈 드라이버의 module_init() 으로 지정된 함수를 호출합니다. do_init_module() 함수를 보겠습니다.

https://elixir.bootlin.com/linux/v5.4/source/kernel/module.c
/*
 * This is where the real work happens.
 *
 * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb
 * helper command 'lx-symbols'.
 */
static noinline int do_init_module(struct module *mod)
{
    int ret = 0;
    struct mod_initfree *freeinit;

    freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
    if (!freeinit) {
        ret = -ENOMEM;
        goto fail;
    }
    freeinit->module_init = mod->init_layout.base;

    /*
     * We want to find out whether @mod uses async during init.  Clear
     * PF_USED_ASYNC.  async_schedule*() will set it.
     */
    current->flags &= ~PF_USED_ASYNC;

    do_mod_ctors(mod);
    /* Start the module */
    if (mod->init != NULL)
        ret = do_one_initcall(mod->init);
    if (ret < 0) {
        goto fail_free_freeinit;
    }

핵심 코드는 보시다시피 do_one_initcall() 함수를 호출하는 부분입니다.

디버깅 포인트 

실전 개발에서 다음과 같은 상황을 겼을 수 있습니다. 

   * 모듈 드라이버가 특정 조건에서 설치가 안된다.
   * 모듈 드라이버를 설치할 때 예상치 못한 오류 메시지와 함께 설치가 안된다.

이때 위에서 분석한 함수에 커널 로그를 추가한 후 디버깅을 하면 됩니다.

혹시 이런 문제를 겪으면 당황하지 맙시다. 
'로그 추가' + '테스트'를 무한 반복하면 언젠가는 문제는 반드시 해결됩니다.

덧글

  • Daniel 2020/02/07 13:17 # 답글

    module이 init 되는 시점을 찾고자 구글링 많이 했는데 자주 찾는 여기서 찾다니 등잔 밑이 어두웠네요!
    module 형태로 돼있는 Cold plug device는 저 시점에 init이 된다고 생각하면 될까요?
  • AustinKim 2020/02/07 13:32 #

    네, 맞습니다.

    Thanks,
    Austin Kim
댓글 입력 영역