[TRACE32][리눅스커널] 유저 공간 콜스택 복원하기 - ARMv8(Aarch64)

AustinKim의 이미지

< Overview >

커널 크래시가 발생해 덤프를 TRACE32로 분석할 때 유저 공간에서 실행 중인 함수들의 콜스택을 보고 싶을 때가 있습니다. 그 이유는 아래와 같은 디버깅 정보를 얻기 위해서 입니다.;

* 유저 공간에서 어떤 함수가 실행한 후 시스템 콜을 통해 커널 공간으로 실행 흐름이 바뀌었나?
* 유저 공간에서 어떤 파라미터를 전달했을까?

그런데 대부분 리눅스 시스템 개발자들은 TRACE32로 커널 공간의 콜스택만을 보면서 디버깅을 합니다.

이번 포스팅에서는 커널 패닉이 발생했을 때 유저 공간의 콜스택을 TRACE32으로 보는 방법을 소개합니다. 패치 코드를 활용하면 커널의 특정 함수에 브레이크 포인트를 걸었을 때도 유저 공간의 콜스택을 볼 수 있습니다.

< 조건 >

이번에 소개하는 패치 코드와 TRACE32 설정 방법은 Aarch64 ARMv8 아키텍처 기반 환경입니다. 즉 64비트 ARM 아키텍처입니다.

< 추가할 코드 >

TRACE32를 설정하기 앞서 먼저 간단히 코드를 수정합시다. 다음 패치 코드는 MMU co-register를 출력하는 기능입니다.

+void get_mmu_sys_ctrl_register(void)
+{
+       u64 sctlr_el1, tcr_el1, ttbr0_el1, ttbr1_el1, sp_el0, sp_el1;
+       u64 esr_el1, far_el1, contextidr_el1, tpidr_el0, tpidr_el1, tpidrro_el0;
+
+       asm volatile ("mrs %0, sctlr_el1\n\t"
+                               "mrs %1, tcr_el1\n\t"
+                               "mrs %2, ttbr0_el1\n\t"
+                               "mrs %3, ttbr1_el1\n\t"
+                               "mrs %4, sp_el0\n\t"
+                               "mov %5, sp\n\t"
+                               "mrs %6, tcr_el1\n\t"
+                               "mrs %7, esr_el1\n\t"
+                               "mrs %8, far_el1\n\t"
+                               "mrs %9, contextidr_el1\n\t"
+                               "mrs %10, tpidr_el0\n\t"
+                               "mrs %11, tpidr_el1\n\t"
+                               "mrs %12, tpidrro_el0\n\t"
+                               : "=r"(sctlr_el1), "=r"(tcr_el1),
+                               "=r"(ttbr0_el1), "=r"(ttbr1_el1),
+                               "=r"(sp_el0), "=r"(sp_el1),
+                               "=r"(tcr_el1), "=r"(esr_el1),
+                               "=r"(far_el1), "=r"(contextidr_el1),
+                               "=r"(tpidr_el0), "=r"(tpidr_el1), "=r"(tpidrro_el0)
+                               : : "memory");
+
+       printk("[+]sctlr_el1: %016llx  tcr_el1: %016llx\n", sctlr_el1, tcr_el1);
+       printk("[+]ttbr0_el1: %016llx  ttbr1_el1: %016llx\n", ttbr0_el1, ttbr1_el1);
+       printk("[+]sp_el0: %016llx  sp_el1: %016llx\n", sp_el0, sp_el1);
+       printk("[+]tcr_el1: %016llx  esr_el1: %016llx\n", tcr_el1, esr_el1);
+       printk("[+]far_el1: %016llx  contextidr_el1: %016llx\n", far_el1, contextidr_el1);
+       printk("[+]tpidr_el0: %016llx  tpidr_el1: %016llx\n", tpidr_el0, tpidr_el1);
+       printk("[+]tpidrro_el0: %016llx\n", tpidrro_el0);
+}

커널 패닉이 발생한 후 레지스터를 뿌려주는 함수에 get_mmu_sys_ctrl_register() 함수가 호출되도록 추가합니다.

diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index dd1e817..b26656f 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -59,6 +59,9 @@
 #include <asm/processor.h>
 #include <asm/stacktrace.h>
 
 
 #ifdef CONFIG_STACKPROTECTOR
 #include <linux/stackprotector.h>
 unsigned long __stack_chk_guard __read_mostly;
@@ -306,6 +309,8 @@ void __show_regs(struct pt_regs *regs)
 
        printk("sp : %016llx\n", sp);
 
+       get_mmu_sys_ctrl_register();
        i = top_reg;

get_mmu_sys_ctrl_register() 함수 호출 위치는 상황에 맞게 수정하면 됩니다.

< 테스트 방법 >

위 패치 코드를 반영 후 "echo c > /proc/sysrq-trigger" 명령어로 강제 커널 패닉을 유발합니다. 덤프를 받고 기존 방식으로 TRACE32를 로딩합니다.

< TRACE32 세팅 방법 >

1. 먼저 다음과 같이 커널 패닉이 발생한 순간의 콜스택을 복원합니다.

-000|el1_da(asm)
 -->|exception
-001|sysrq_handle_crash(?)
-002|rcu_read_unlock(inline)
-002|__handle_sysrq(?, ?)
-003|write_sysrq_trigger(?, buf = 0x00000071EEC0B288, ?, ?)
-004|use_pde(inline)
-004|proc_reg_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, ppos = 0xFFFFFF8016C6
-005|__vfs_write(file = 0xFFFFFFC5399CDBC0, p = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-006|vfs_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-007|ksys_write(?, buf = 0x00000071EEC0B288, count = 2)
-008|__se_sys_write(inline)
-008|__arm64_sys_write(regs = 0xFFFFFF8016C6BEC0)
-009|el0_svc_common(regs = 0xFFFFFF8016C6BEC0, scno = 64, sc_nr = 294, syscall_table = 0xFFFFFFAF18600790
-010|el0_svc_handler(regs = 0xFFFFFF8016C6BEC0)
-011|el0_svc(asm)
 -->|exception
-012|NUX:0x71EF25EAF8(asm)
 ---|end of frame

콜스택과 같이 유저 공간에는 주소가 1줄 'NUX:0x71EF25EAF8(asm)' 밖에 보이지 않습니다.
아마 유저 공간에서 시스템 콜을 발생하는 함수의 주소로 판단이 됩니다.

이제 커널 로그를 보겠습니다.
커널 로그를 보면 다음과 같이 MMU Co-Processor 레지스터 정보를 볼 수 있습니다.

[  235.751791] <c6>CPU: 5 PID: 11014 Comm: sh Tainted: G S      W  OE     4.19.30+ #7
[  235.751812] <c2>pstate: 60400005 (nZCv daif +PAN -UAO)
[  235.751835] <c2>pc : sysrq_handle_crash+0x70/0x80
[  235.751846] <c2>lr : sysrq_handle_crash+0x5c/0x80
[  235.751856] <c2>sp : ffffff801a853c70
[  235.751879] <c2>[+]sctlr_el1: 000000003475591d  tcr_el1: 000001b2b5593519
[  235.751889] <c2>[+]ttbr0_el1: 000000012c924000  ttbr1_el1: 17280000a3ff0000
[  235.751899] <c2>[+]sp_el0: fffffff270350040  sp_el1: ffffff801a8537f0
[  235.751908] <c2>[+]tcr_el1: 000001b2b5593519  esr_el1: 0000000056000000
[  235.751918] <c2>[+]far_el1: 0000000000000000  contextidr_el1: 0000000000002b06
[  235.751928] <c2>[+]tpidr_el0: 0000007da4881020  tpidr_el1: 000000533e00d000
[  235.751937] <c2>[+]tpidrro_el0: 0000000000000000
[  235.751946] <c2>x29: ffffff801a853c80 x28: fffffff270350040 
[  235.751962] <c2>x27: 0000000000000000 x26: 0000000000000000 
[  235.751977] <c2>x25: 0000000056000000 x24: fffffff270350040 

2. TRACE32 명령어로 MMU Co-Register를 복원하기

다음과 TRACE32 cmm script를 복사한 후 restore_user.cmm으로 저장합니다.
"do restore_user.cmm" 명령어를 실행해 MMU Co-Register를 로딩합니다.

&sctlr_el1_reg=0x0 
&tcr_el1_reg=0x0 
&ttbr0_el1_reg=0x0 
&ttbr1_el1_reg=0x0 
&esr_el1_reg=0x0 
&far_el1_reg=0x0 
&contextidr_el1_reg=0x0 
&tpidr_el0_reg=0x0 
&tpidr_el1_reg=0x0 
&tpidrro_el0_reg=0x0 
 
&sctlr_el1_reg=V.VALUE(sctlr_el1)
&tcr_el1_reg=V.VALUE(tcr_el1)
&ttbr0_el1_reg=V.VALUE(ttbr0_el1)
&ttbr1_el1_reg=V.VALUE(ttbr1_el1) 
&esr_el1_reg=V.VALUE(esr_el1) 
&far_el1_reg=V.VALUE(far_el1)
&contextidr_el1_reg=V.VALUE(contextidr_el1) 
&tpidr_el0_reg=V.VALUE(tpidr_el0) 
&tpidr_el1_reg=V.VALUE(tpidr_el1)
&tpidrro_el0_reg=V.VALUE(tpidrro_el0) 
 
&sctlr_el1_reg_t=FORMAT.DECIMAL(0., &sctlr_el1_reg)
 
PER.Set SPR:0x30100 %QUAD &sctlr_el1_reg  	// SCTLR_EL1
PER.Set SPR:0x30200 %QUAD &ttbr0_el1_reg  	// TTBR0_EL1
PER.Set SPR:0x30201 %QUAD &ttbr1_el1_reg 	// TTBR1_EL1
PER.Set SPR:0x30202 %QUAD &tcr_el1_reg 		// TCR_EL1
PER.Set SPR:0x30520 %QUAD &esr_el1_reg		// ESR_EL1
PER.Set SPR:0x30600 %QUAD &far_el1_reg		// FAR_EL1 
PER.Set SPR:0x30D01 %QUAD &contextidr_el1_reg				
// CONTEXTIDR_EL1
PER.Set SPR:0x33D02 %QUAD &tpidr_el0_reg // TPIDR_EL0
PER.Set SPR:0x33D03 %QUAD &tpidrro_el0_reg  // TPIDRRO_EL0 
PER.Set SPR:0x33D04 %QUAD &tpidr_el1_reg // TPIDR_EL1
 
mmu.delete
mmu.scan
mmu.on
 
enddo

보시다시피 위에서 보이는 TRACE32 스크립트 코드는 MMU의 메모리 페이지 테이블을 설정하는 기능입니다.
다시 콜스택을 보겠습니다. 이제 콜스택 정보가 다릅니다.

-000|el1_da(asm)
 -->|exception
-001|sysrq_handle_crash(?)
-002|rcu_read_unlock(inline)
-002|__handle_sysrq(?, ?)
-003|write_sysrq_trigger(?, buf = 0x00000071EEC0B288, ?, ?)
-004|use_pde(inline)
-004|proc_reg_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, ppos = 0xFFFFFF8016C6
-005|__vfs_write(file = 0xFFFFFFC5399CDBC0, p = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-006|vfs_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-007|ksys_write(?, buf = 0x00000071EEC0B288, count = 2)
-008|__se_sys_write(inline)
-008|__arm64_sys_write(regs = 0xFFFFFF8016C6BEC0)
-009|el0_svc_common(regs = 0xFFFFFF8016C6BEC0, scno = 64, sc_nr = 294, syscall_table = 0xFFFFFFAF18600790
-010|el0_svc_handler(regs = 0xFFFFFF8016C6BEC0)
-011|el0_svc(asm)
 -->|exception
-012|NUX:0x71EF25EAF8(asm)
-013|NUX:0x55686F8384(asm)
-014|NUX:0x55686F3764(asm)
-015|NUX:0x55686F2150(asm)
-016|NUX:0x5568708294(asm)
-017|NUX:0x5568707D58(asm)
-018|NUX:0x71EF20A89C(asm)
 ---|end of frame

다음과 같이 유저 공간에서 실행 중인 함수의 주소가 더 보입니다.

-012|NUX:0x71EF25EAF8(asm)
-013|NUX:0x55686F8384(asm)
-014|NUX:0x55686F3764(asm)
-015|NUX:0x55686F2150(asm)
-016|NUX:0x5568708294(asm)
-017|NUX:0x5568707D58(asm)
-018|NUX:0x71EF20A89C(asm)
 ---|end of frame

3. 유저 공간의 라이브러리를 로딩해 유저 공간의 함수 콜스택 복원하기

자, 이제 본격적으로 유저 공간 콜스택을 복원 합시다.
먼저 위에서 확인한 바와 같이 유저 공간에서 가장 마지막에 실행된 코드 주소는 콜스택과 같이 0x71EF25EAF8 입니다.

이 주소의 정체를 확인하기 위해 커널 패닉이 발생한 프로세스의 virtual 메모리 공간을 덤프합니다.

crash> vm 5533
PID: 5533   TASK: ffffffc4da6d4340  CPU: 5   COMMAND: "sh"
       MM               PGD          RSS    TOTAL_VM
ffffffc50cdbbc40  ffffffc4e2efc000  3036k    33588k
      VMA           START       END     FLAGS FILE
ffffffc4dde36528 55686d3000 55686de000    871 /system/bin/sh
ffffffc4dde36f78 55686de000 556871b000    874 /system/bin/sh
ffffffc4e42f6948 556871b000 556871c000 100873 /system/bin/sh
ffffffc4dde377b8 556871c000 556871e000 100871 /system/bin/sh
...
ffffffc4e409b398 71ef18c000 71ef1cd000     71 /apex/com.android.runtime/system/apex/com.android.runtime.debug/lib64/bionic/libc.so
ffffffc4e409b188 "71ef1cd000" 71ef279000     74 /apex/com.android.runtime/system/apex/com.android.runtime.debug/lib64/bionic/libc.so
ffffffc4e409bde8 71ef279000 71ef27c000 100073 /apex/com.android.runtime/system/apex/com.android.runtime.debug/lib64/bionic/libc.so
ffffffc4e409b5a8 71ef27c000 71ef283000 100071 /apex/com.android.runtime/system/apex/com.android.runtime.debug/lib64/bionic/libc.so

Flags 100073 윗 부분에 있는 0x71ef1cd000 주소가 libc.so 파일을 로딩할 주소입니다.

참고로 심볼 정보가 있는 libc.so 라이브러리 위치는 다음 경로입니다.
out/target/product/rpi/symbols/recovery/root/system/lib64/libc.so

이번에 다음 명령어로 libc.so를 로딩합니다. TRACE32로 libc.so 라이브러리를 로딩할 때 오프셋 커맨드를 주의하세요.

Data.LOAD.elf libc.so 0x71EF18C000 /pack /nocode /noclear /strippart 5

명령어 포멧
Data.LOAD.elf [라이브러리 이름] [오프셋] /pack /nocode /noclear /strippart 5

위 명령어를 입력하니 유저 공간 콜스택에서 write 심볼이 보입니다.

-000|el1_da(asm)
 -->|exception
-001|sysrq_handle_crash(?)
-002|rcu_read_unlock(inline)
-002|__handle_sysrq(?, ?)
-003|write_sysrq_trigger(?, buf = 0x00000071EEC0B288, ?, ?)
-004|use_pde(inline)
-004|proc_reg_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, ppos = 0xFFFFFF8016C6
-005|__vfs_write(file = 0xFFFFFFC5399CDBC0, p = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-006|vfs_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-007|ksys_write(?, buf = 0x00000071EEC0B288, count = 2)
-008|__se_sys_write(inline)
-008|__arm64_sys_write(regs = 0xFFFFFF8016C6BEC0)
-009|el0_svc_common(regs = 0xFFFFFF8016C6BEC0, scno = 64, sc_nr = 294, syscall_table = 0xFFFFFFAF18600790
-010|el0_svc_handler(regs = 0xFFFFFF8016C6BEC0)
-011|el0_svc(asm)
 -->|exception
-012|write(asm)
-013|NUX:0x55686F8384(asm)
-014|NUX:0x55686F3764(asm)
-015|NUX:0x55686F2150(asm)
-016|NUX:0x5568708294(asm)
-017|NUX:0x5568707D58(asm)
-018|__libc_init(?, ?, ?, ?)
 ---|end of frame

다음에 파싱할 주소는 아래와 같이 NUX:0x55686F8384(asm)이며 이에 매핑하는 라이브러리 파일을 찾아야 할 차례입니다.

-013|NUX:0x55686F8384(asm)

커널 패닉이 발생한 프로세스의 virtual 메모리 공간을 덤프합니다.

crash> vm 5533
PID: 5533   TASK: ffffffc4da6d4340  CPU: 5   COMMAND: "sh"
       MM               PGD          RSS    TOTAL_VM
ffffffc50cdbbc40  ffffffc4e2efc000  3036k    33588k
      VMA           START       END     FLAGS FILE
ffffffc4dde36528 55686d3000 55686de000    871 /system/bin/sh
ffffffc4dde36f78 55686de000 556871b000    874 /system/bin/sh
ffffffc4e42f6948 556871b000 556871c000 100873 /system/bin/sh
ffffffc4dde377b8 556871c000 556871e000 100871 /system/bin/sh

Flags 871 에 있는 0x55686d3000 주소가 sh 파일을 로딩할 주소입니다.

참고로, 심볼 정보가 포함된 sh 파일 위치는 다음과 같습니다.

out/target/product/rpi/symbols/vendor/bin/sh

이어서 sh 파일을 다음 오프셋으로 로딩합니다.

Data.LOAD.elf sh 0x55686D3000  /pack /noclear /strippart 5

< 복원된 유저 공간 콜스택의 정보 >

이제 유저 공간에 있는 콜스택을 모두 볼 수 있습니다.

-000|el1_da(asm)
 -->|exception
-001|sysrq_handle_crash(?)
-002|rcu_read_unlock(inline)
-002|__handle_sysrq(?, ?)
-003|write_sysrq_trigger(?, buf = 0x00000071EEC0B288, ?, ?)
-004|use_pde(inline)
-004|proc_reg_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, ppos = 0xFFFFFF8016C6
-005|__vfs_write(file = 0xFFFFFFC5399CDBC0, p = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-006|vfs_write(file = 0xFFFFFFC5399CDBC0, buf = 0x00000071EEC0B288, count = 2, pos = 0xFFFFFF8016C6BE00)
-007|ksys_write(?, buf = 0x00000071EEC0B288, count = 2)
-008|__se_sys_write(inline)
-008|__arm64_sys_write(regs = 0xFFFFFF8016C6BEC0)
-009|el0_svc_common(regs = 0xFFFFFF8016C6BEC0, scno = 64, sc_nr = 294, syscall_table = 0xFFFFFFAF18600790
-010|el0_svc_handler(regs = 0xFFFFFF8016C6BEC0)
-011|el0_svc(asm)
 -->|exception
-012|write(asm)
-013|c_print(?)
-014|call_builtin(inline)
-014|comexec()
-015|execute(t = 0x00000071EEC4F808, flags = 0, xerrok = 0x0000007FE1E8BEA4)
-016|shell(s = 0x00000071EEC38008, level = 0)
-017|main(?, ?)
-018|__libc_init(?, ?, ?, ?)
 ---|end of frame

< 유저 공간에서 호출된 함수의 정보 확인해보기 >

'y.l.line r(pc)' 명령어로 유저 공간에 있는 각각 함수 위치를 확인해봤습니다.

-012|write(asm)
__________________address________________|module_________|source__________________________________|line_______________|offset____|
     P:00000071EF25EAF8--00000071EF25EAFB|\\libc\write   |bionic\libc\arch-arm64\syscalls\write.S |\9--0              |   prelim.| \\libc\Global\write+0x8
     P:00000071EF25EAFC--00000071EF25EAFF|\\libc\write   |bionic\libc\arch-arm64\syscalls\write.S |\10--0             |   prelim.| \\libc\Global\write+0x0C

-013|c_print(?)

__________________address________________|module____|source______________________|line_______|offset____|
     P:00000055686F8384--00000055686F8387|\\sh\funcs|external\mksh\src\funcs.c   |\567--569  |          | \\sh\funcs\c_print+0x770
     P:00000055686F8388--00000055686F838B|\\sh\funcs|external\mksh\src\funcs.c   |\578--583  |          | \\sh\funcs\c_print+0x774

-014|call_builtin(inline)
-014|comexec()

__________________address________________|module_______|source_____________________|line_______________|offset____|
     P:00000055686F3758--00000055686F3767|\\sh\exec    |external\mksh\src\exec.c   |\1398--1398        |          | \\sh\exec\comexec+0x974
     P:00000055686F3768--00000055686F376F|\\sh\exec    |external\mksh\src\exec.c   |\1399--1399        |          | \\sh\exec\comexec+0x984

-015|execute(t = 0x00000071EEC4F808, flags = 0, xerrok = 0x0000007FE1E8BEA4)

__________________address________________|module_______|source______________________|line_______________|offset____|
     P:00000055686F2138--00000055686F2153|\\sh\exec    |external\mksh\src\exec.c    |\161--162          |          | \\sh\exec\execute+0xB44
     P:00000055686F2154--00000055686F216B|\\sh\exec    |external\mksh\src\exec.c    |\163--166          |          | \\sh\exec\execute+0xB60

-016|shell(s = 0x00000071EEC38008, level = 0)

__________________address________________|module________|source_____________________|line_______________|offset____|
     P:000000556870828C--000000556870829F|\\sh\main     |external\mksh\src\main.c   |\910--910          |          | \\sh\main\shell+0x2D8
     P:00000055687082A0--00000055687082BB|\\sh\main     |external\mksh\src\main.c   |\911--911          |          | \\sh\main\shell+0x2EC

-017|main(?, ?)

__________________address________________|module______|source______________________|line_______________|offset____|
     P:0000005568707D54--0000005568707D5B|\\sh\main   |external\mksh\src\main.c    |\699--704          |          | \\sh\main\main+0x9F8
     P:0000005568707D5C--0000005568707D67|\\sh\main   |external\mksh\src\main.c    |\705--705          |          | \\sh\main\main+0xA00

-018|__libc_init(?, ?, ?, ?)

 ---|end of frame
__________________address________________|module_____________________|source__________________________________|line_______________|offset____|
     P:00000071EF20A898--00000071EF20A89F|\\libc\libc_init_dynamic   |bionic\libc\bionic\libc_init_dynamic.cpp|\134--136          |          | \\libc\libc_init_dynamic\__libc_init+0x6C
     P:00000071EF20A8A0--00000071EF20A8A3|\\libc\libc_init_dynamic   |bionic\libc\bionic\libc_init_dynamic.cpp|\138--144          |          | \\libc\libc_init_dynamic\__libc_shared_globals

이 포스팅에 올린 내용을 참고해 일찍 퇴근합시다.

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

Forums: 
익명 사용자의 이미지

crash에서 gcore extension을 load하여 coredump file을 생성하여 (arm or aarch64) gdb로 디버깅하는 방법도 있습니다.
aarch64인 경우에 gcore librrary code를 조금 수정해야 합니다만...

https://people.redhat.com/anderson/extensions/crash-gcore-command-1.5.1.tar.gz

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.