[리눅스커널] 비트 마스크를 어셈블리 코드로 빨리 읽는 방법 - HARDIRQ_MASK, SOFTIRQ_MASK, NMI_MASK

AustinKim의 이미지

이번에는 비트 마스크를 C 코드가 아닌 어셈블리 코드로 읽는 방법을 소개합니다.

<< in_interrupt() 함수 소개 >>

in_interrupt() 함수는 현재 프로세스가 인터럽트 컨택스트인지 알려주는 기능입니다.

 
https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/preempt.h
#define in_interrupt()		(irq_count())

in_interrupt() 함수 코드를 보면 irq_count() 함수로 치환됩니다.

 
https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/preempt.h
#define irq_count()	(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
				 | NMI_MASK))

irq_count() 함수 코드를 보면 preempt_count() 결과값과 다음 플래그간 AND BIT 오퍼레이션을 실행합니다.

 
HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK

(HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 의 정체는 무엇일까요?

다시 소스 코드를 따라가 보니, 눈이 어지럽습니다. "이것을 어떻게 계산할까!!!" 란 생각이 드는군요.

 
https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/preempt.h
#define PREEMPT_BITS	8
#define SOFTIRQ_BITS	8
#define HARDIRQ_BITS	4
#define NMI_BITS	1
 
#define PREEMPT_SHIFT	0
#define SOFTIRQ_SHIFT	(PREEMPT_SHIFT + PREEMPT_BITS)
#define HARDIRQ_SHIFT	(SOFTIRQ_SHIFT + SOFTIRQ_BITS)
#define NMI_SHIFT	(HARDIRQ_SHIFT + HARDIRQ_BITS)
 
#define __IRQ_MASK(x)	((1UL << (x))-1)
 
#define PREEMPT_MASK	(__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
#define SOFTIRQ_MASK	(__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
#define HARDIRQ_MASK	(__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
#define NMI_MASK	(__IRQ_MASK(NMI_BITS)     << NMI_SHIFT)

<< (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 마스크를 어셈블리 코드로 알아보기 >>

C 코드 형태로 (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 의 정체를 알기는 좀 복잡합니다.
그러면 위 마스크값들의 정체를 알기 위해 in_interrupt() 함수를 어셈블리 코드를 한 번 볼까요?

 
https://elixir.bootlin.com/linux/v4.19.30/source/mm/vmalloc.c
static struct vm_struct *__get_vm_area_node(unsigned long size,
		unsigned long align, unsigned long flags, unsigned long start,
		unsigned long end, int node, gfp_t gfp_mask, const void *caller)
{
	struct vmap_area *va;
	struct vm_struct *area;
 
	BUG_ON(in_interrupt());

__get_vm_area_node() 함수 가장 앞단에 in_interrupt() 함수를 호출합니다. 아주 좋은 예시입니다.

 
01 NSR:80265FEC|__get_vm_area_node:   cpy     r12,r13
02 NSR:80265FF0|                      push    {r4-r9,r11-r12,r14,pc}
03 NSR:80265FF4|                      sub     r11,r12,#0x4     ; r11,r12,#4
04 NSR:80265FF8|                      sub     r13,r13,#0x8     ; r13,r13,#8
05 NSR:80265FFC|                      str     r14,[r13,#-0x4]!
06 NSR:80266000|                      bl      0x8010FDBC       ; __gnu_mcount_nc
07 NSR:80266004|                      cpy     r14,r13
08 NSR:80266008|                      bic     r12,r14,#0x1FC0   ; r12,r14,#8128
09 NSR:8026600C|                      bic     r12,r12,#0x3F    ; r12,r12,#63
10 NSR:80266010|                      ldr     r14,[r12,#0x4]
11 NSR:80266014|                      ldr     r12,0x80266104
12 NSR:80266018|                      cpy     r8,r1            ; r8,align
13 NSR:8026601C|                      and     r12,r12,r14
14 NSR:80266020|                      cmp     r12,#0x0         ; r12,#0
15 NSR:80266024|                      cpy     r4,r2            ; r4,flags
16 NSR:80266028|                      cpy     r9,r3            ; r9,start

먼저 07~09번째 줄 코드를 보겠습니다.

 
07 NSR:80266004|                      cpy     r14,r13
08 NSR:80266008|                      bic     r12,r14,#0x1FC0   ; r12,r14,#8128
09 NSR:8026600C|                      bic     r12,r12,#0x3F    ; r12,r12,#63

스택 주소를 통해 프로세스 스택 최상단 주소에 접근하는 동작입니다.
위 어셈블리 코드 연산 결과 r12는 스택 최상단 주소를 저장하게 됩니다.

다음 10번째 줄 코드입니다.

 
10 NSR:80266010|                      ldr     r14,[r12,#0x4]

스택 최상단 주소에 있는 struct thread_info 구조체 preempt_count 필드를 r14에 로딩합니다.
만약 r12가 0x800c0000이면 0x800C0004주소에 있는 preempt_count 필드의 0x00010002를 r14에 저장합니다.

 
  (struct thread_info *) [-] (struct thread_info*)0x800c0000  
    (long unsigned int) [D:0x800C0000] flags = 0x0,
    (int) [D:0x800C0004] preempt_count = 0x00010002,
    (mm_segment_t) [D:0x800C0008] addr_limit = 0x0,

핵심 코드를 볼 차례입니다.

 
11 NSR:80266014|                      ldr     r12,0x80266104
12 NSR:80266018|                      cpy     r8,r1            ; r8,align
13 NSR:8026601C|                      and     r12,r12,r14
14 NSR:80266020|                      cmp     r12,#0x0         ; r12,#0


[11 행]: 0x80266104 주소에 있는 값을 r12에 저장합니다.

0x80266104 주소엔 0x001FFF00가 있습니다.

 
_____address|________0________4________8________C 
NSD:80266100| E7F001F2>001FFF00 00693EE0 809E02AC  

(HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 의 정체가 0x001FFF00입니다.

[13 행]: r12와 r14 레지스터와 AND 비트 연산을 수행합니다.
이를 쉽게 표현하면 다음과 같습니다.
(struct thread_info 구조체 preempt_count 필드) & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)

어셈블리 코드 분석으로 다음 내용을 알게 됐습니다.

 
(HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) = 0x001FFF00

<< HARDIRQ_MASK, SOFTIRQ_MASK, NMI_MASK 플래그를 어셈블리 코드로 계산해보기 >>

어셈블리 코드 분석으로 (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)의 정체가 0x001FFF00이란 사실을 알게 됐습니다.
이번엔 각각 플래그 값을 알아볼까요?

이번엔 tracing_generic_entry_update() 함수를 어셈블리 코드로 분석해 보겠습니다.

 
https://elixir.bootlin.com/linux/v4.19.30/source/kernel/trace/trace.c
1 void
2 tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags,
3 			     int pc)
4 {
5 	struct task_struct *tsk = current;
6 
7 	entry->preempt_count		= pc & 0xff;
8 	entry->pid			= (tsk) ? tsk->pid : 0;
9 	entry->flags =
10 #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
11		(irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) |
12 #else
13		TRACE_FLAG_IRQS_NOSUPPORT |
14 #endif
15		((pc & NMI_MASK    ) ? TRACE_FLAG_NMI     : 0) |
16		((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) |
17		((pc & SOFTIRQ_OFFSET) ? TRACE_FLAG_SOFTIRQ : 0) |

위 코드 15~17번째 줄을 어셈블리 코드로 분석하는 것입니다.

tracing_generic_entry_update() 함수를 어셈블리 코드로 보면 다음과 같습니다.

 
01 NSR:801E3B50|tracing_generic_entry_update:  cpy     r12,r13
02 NSR:801E3B54|                               push    {r4-r5,r11-r12,r14,pc}
03 NSR:801E3B58|                               sub     r11,r12,#0x4     ; r11,r12,#4
04 NSR:801E3B5C|                               cpy     r12,r13
05 NSR:801E3B60|                               bic     r3,r12,#0x1FC0   ; r3,r12,#8128
06 NSR:801E3B64|                               bic     r3,r3,#0x3F      ; r3,r3,#63
07 NSR:801E3B68|                               ldr     r3,[r3,#0x0C]
08 NSR:801E3B6C|                               bic     r12,r12,#0x1FC0   ; r12,r12,#8128
09 NSR:801E3B70|                               strb    r2,[r0,#0x3]     ; pc,[r0,#3]
10 NSR:801E3B74|                               cmp     r3,#0x0          ; tsk,#0
11 NSR:801E3B78|                               ldrne   r3,[r3,#0x3A8]   ; tsk,[r3,#936]
12 NSR:801E3B7C|                               bic     r12,r12,#0x3F    ; r12,r12,#63
13 NSR:801E3B80|                               tst     r2,#0x100000     ; pc,#1048576 /* <<-- NMI_MASK */
14 NSR:801E3B84|                               ldr     r12,[r12]
15 NSR:801E3B88|                               moveq   r5,#0x0          ; r5,#0
16 NSR:801E3B8C|                               movne   r5,#0x40         ; r5,#64
17 NSR:801E3B90|                               tst     r2,#0xF0000      ; pc,#983040 /* <<-- HARDIRQ_MASK */
18 NSR:801E3B94|                               moveq   r4,#0x0          ; r4,#0
19 NSR:801E3B98|                               movne   r4,#0x8          ; r4,#8
20 NSR:801E3B9C|                               tst     r2,#0x100        ; pc,#256  /* <<-- SOFTIRQ_MASK */
21 NSR:801E3BA0|                               cpy     r14,r13
22 NSR:801E3BA4|                               moveq   r14,#0x0         ; r14,#0

HARDIRQ_MASK, SOFTIRQ_MASK, NMI_MASK 플래그의 정체는 다음과 같습니다.
NMI_MASK : 0x100000
HARDIRQ_MASK : 0xF0000
SOFTIRQ_MASK : 0x100

(개인블로그)
http://rousalome.egloos.com/

Forums: 

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.