[C++] debugging을 통해 메모리 stack segment 코드내용 보는 과

gyxor의 이미지

#include<iostream>
using namespace std;

void show(char i='a')
{
	char e = 'b';

}
int main()
{
	show();
	return 0;
}

위 소스를 디버깅하다가 show()가 호출이되고
char i='a' 로컬변수가 생성되고
정확하게 아래와 같은 순간에
void show(char i='a')
=>{
char e = 'b';

}
레지스터의 상태를 보면

EAX = CCCCCCCC EBX = 00560000
ECX = 00000000 EDX = 00790B70
ESI = 8178DD64 EDI = 0066FDF8
EIP = 00401030 ESP = 0066FDA4
EBP = 0066FDF8 EFL = 00000206 CS = 017F
DS = 0187 ES = 0187 SS = 0187 FS = 3D67
GS = 0000 OV=0 UP=0 EI=1 PL=0 ZR=0 AC=0
PE=1 CY=0

입니다. 어셈에서 배운대로
실제 스택의 위치는 SS + SP이므로
0066FDA4
+01870000
이렇게 하였습니다. SS의 경우 16비트이므로 0187이 나왔지만 4자리 확장이
되었으므로 뒤에 0000을 붙였습니다. 아무트 계산결과는
01ECFDA4
이렇게 나오고 이 주소의 메모리를 찾아가봤는데요..
거기에는 'a'를 나타내는 97이란 값이 없었습니다.
그 아래의 주소에도 없었구요..

제생각대로라면 찾아낸 해당위치에 97이란 값이 들어가고.. 스탭을 더밟아서
char e = 'b'; 이구문이 실행이 되면 해당위치보다 한바이트 낮은곳에 98이라고 값이 들어갈것으로 생각을 했습니다.
이런식의 접근은 잘못된것인가요??

이러한 추적을 해보는 이유는 2가지 입니다.

첫째, 함수호출시 모든 지역변수가 순차적으로 생성이 된다고 들었는데요
파라미터로 생성되는 로컬변수와 {}블럭안의 로컬변수중에 stack에 자리를
잡는 순서를 알아보기 위함입니다.

둘째, .exe파일이 처음 실행되었을때 최초 stack segment는 완전히 비어있지 않습니까?(원래는 가비지 값이 들어가죠?)
main()함수가 호출됨으로써 프로그램이 시작이 되는데요 그럼 그때 stacksegment에 들어가게 되는 return주소 다시말해 main함수에서
return 0; 했을때 돌아가야할 주소가 반드시 stack segment에 들어가야
할것 같은데요..
그 주소값은 어떤값이 되는지... null인지... 프로그램마다 다른지...
어떤의미를 가지는지 궁금했습니다.

이런방법으로는 위의 사항들을 알아낼 수 가 없나요?
틀렸다면 왜틀렸는지 궁금하구요 또한 어떤 다른 방법으로 위 사항을 직접볼수 있는지 알고싶습니다.
답변부탁드립니다.

pynoos의 이미지

#include<iostream>
using namespace std;

void show(char i='a')
{
	char e = 'b';

}
int main()
{
	show();
	return 0;
}

위의 코드에서 default 값으로 넣어주는 것은 함수가 호출될 때, 즉

show( 'a' );

로 치환되어 컴파일된다고 생각하시면 됩니다. 그렇게 되면, 'a' 는 부르는 쪽에서 stack에 넣게 되는 것입니다.

gyxor wrote:

첫째, 함수호출시 모든 지역변수가 순차적으로 생성이 된다고 들었는데요
파라미터로 생성되는 로컬변수와 {}블럭안의 로컬변수중에 stack에 자리를
잡는 순서를 알아보기 위함입니다.

둘째, .exe파일이 처음 실행되었을때 최초 stack segment는 완전히 비어있지 않습니까?(원래는 가비지 값이 들어가죠?)
main()함수가 호출됨으로써 프로그램이 시작이 되는데요 그럼 그때 stacksegment에 들어가게 되는 return주소 다시말해 main함수에서
return 0; 했을때 돌아가야할 주소가 반드시 stack segment에 들어가야
할것 같은데요..
그 주소값은 어떤값이 되는지... null인지... 프로그램마다 다른지...
어떤의미를 가지는지 궁금했습니다.

이런방법으로는 위의 사항들을 알아낼 수 가 없나요?
틀렸다면 왜틀렸는지 궁금하구요 또한 어떤 다른 방법으로 위 사항을 직접볼수 있는지 알고싶습니다.
답변부탁드립니다.

함수의 맨처음 시작은 main이 아닙니다. main은 c runtime library의 callback 함수명일 뿐입니다. 즉 crt0.obj 과 같은 startup 코드에서 링크되기를 희망하는 함수일 뿐이지요. 따라서 main의 return 주소는 startup code로 돌아가도록 되어 있습니다.

argument와 지역변수는 생각하시는대로 위치가 다르며 return address를 사이에 두고 나뉘어 있습니다.

gyxor의 이미지

파라미터의 경우 함수호출전에 먼저 스택에 저장된다는 사실을 새롭게 알게됐습니다.
그리고 프로그램의 처음 시작이 main()함수가 아니라면 스택에 처음들어가는것도 startup code가 아닌 다른것인가요?
아무튼 맨처음의 스택에 들어가는것은 차치하고서라도..
main()함수가 호출되는 시점부터 스택의 모습은..

아래와 같이 되는것 아닌가요?

그런데 위 질문에서쓴 방법대로 스택세그먼트에 접근해봐도 스택의 내용을 볼수가 없습니다.

.최하위 주소
.
.
.
.
__________
int e='b';
__________
show()리턴Ip
__________
int i='a';
__________
startup code
__________
.
.
.최상위 주소

#include<iostream>
using namespace std;

void show(unsigned char i=255)
{
	unsigned char e = 255;

}
int main()
{
	show();
	return 0;
}

소스를 바꿔서 해봤는데요..

.최하위 주소
.
.
.
.
__________
FF
__________
show()리턴Ip
__________
FF
__________
startup code
__________
.
.
.최상위 주소
이렇게 되어야 할것같은데요..
다시말해서 위 스택구조가 맞고 틀리고를 확인조차 못해봤습니다.
어떻게 VC6.0에서 접근을 해봐야 하는지 모르겠습니다.
답변부탁드립니다.

voider의 이미지

컴파일러가 어떻게 최적화 하느냐에 따라 다르지만
일반적으로 함수의 인수목록대로 호출하는 쪽에서 스택에 함수의 인수값대로
넣습니다. 그럼 그것을 호출되는 함수에선 ebp + xx 이런식으로 참조합니다
스택은 아랫쪽으로 자라는데 +인 이유는 함수가 시작되는 시점에서
스택 포인터를 ebp로 치환하고 스택을 증가 시킵니다(선언된 지역변수를 넣을수 있을만큼 충분하게...)
VC6에선 디버깅시에 디스어셈블리된 코드를 볼수있습니다.
아주 친절하게 소스코드 밑에다가 어셈코드까지 보여줍니다.

그리고 이건 쓸데없는 참견이라고 보지만 c++에서 작업 할때 스택이 어떻게 생성되고 어떻게 처리되고 어디까지 유효한지 알필요는 없다고 봅니다
그런것을 고려하지 않아도 되게끔 만들어야 겠죠 ^^

-- 아쉬운 하루 되세요 --

pynoos의 이미지

제 답변이 늦었군요..

혹 디버깅하다 알수 있을것 같아서 잠시 미뤘는데.. 해결 하셨나요?

어려운 문제가 아니므로, 조금 들여다보면, 디버거나 memory printf 해보는 것으로 확인 가능합니다.

그리고, VC 같은 경우 디버그 버전에서는 스택을 넉넉하게 잡는 경향이 있으므로 릴리즈 모드로해보세요. 디스어셈블 코드로 확인하시는 것이 가장 좋습니다.

그리고, C++ 같은 경우도, calling convention이 this call 방식이라는 것이 도입되어, 멤버함수라도 static 멤버와 일반 멤버가 calling 방식이 약간 다릅니다.

this pointer가 내제되어있는지 여부에 따라 스택 첫번째 값이 달라지죠.

확인해보시길바랍니다.

gyxor의 이미지

일단 저는 어셈블리는 잘 모릅니다만... 우선 seok_junhee 님 지적대로, 0x0187 을 32비트로 확장하면 0x00000187 이지 0x01870000 이 아니겠지요?

그건 그렇고, 제 손엔 VC 가 없는 고로, 웹 리소스를 좀 읽어보고 GCC 로 최적화 없이 테스트해 본 결과를 gdb 로 눈빠지게 들여다보니 (예, 이 답변은 들여다보면서 공부한 결과물입니다 ^^;) 몇 가지 눈에 뜨이는 게 있군요.

먼저, ESP 자체가 스택을 가리킵니다. SS 는 신경쓸 필요도 없습니다. 16bit 프로그래밍이 아니니까요. 정확히는 ESP 가 스택의 꼭대기, EBP 가 스택의 바닥을 가리키지요. 그러니까 인수로 넘어온 i 값은 EBP + 8 에서 찾으실 수 있을 거고요, 어쩌면 i 변수 자체는 EBP - 1 에 있을지도 모릅니다. GCC 의 경우 4 byte 내에 들어가지 않는 인수들을 이렇게 스택에 복사해서 사용하더군요.

그리고 스택 밑에는 함수 호출에 필요한 여러가지 정보들이 들어 있습니다. 순서대로 보자면 EBP + 0 에는 이전의 EBP 값이, EBP + 4 에는 return 주소가 저장되어 있습니다. EBP + 8, EBP + 12, EBP + 16, ... 에는 각각 마지막 인수값, 마지막에서 두 번째 인수값, 마지막에서 세 번째 인수값, ... 이 저장되어 있을 겁니다. (Calling convention 에 따라 순서는 바뀔 수 있습니다.)

또한, KLDP 에서도 답변을 받으셨지만, C/C++ 프로그램은 main() 에서 시작한다고 규정되어 있긴 합니다만 그렇다고 해서 정말로 실제 프로그램이 정말로 main() 에서 시작하고 종료하는 것은 아닙니다. 별도의 startup/cleanup 루틴이 그 뒤에서 보이지 않게 작용하고 있지요. 그러니 main() 함수가 리턴할 주소야 당연히 따로 있습니다. 
ESP 는 32 비트 레지스터니까 자체적으로 2기가 메모리를 다룰 수 있죠. (4기가가 아닌 이유는 전번에 말씀드렸던 것 같네요) 따라서 괜히 쓸데없이 다른 레지스터를 더 쓸 이유가 없습니다. 엇갈려 배치하는 것 같은 건 전혀 필요 없고요.

EBP 때문에 혼동을 일으키신 것 같은데요, 로컬 변수가 있는 곳이 스택이죠? 답변에도 써 뒀지만, 로컬 변수는 ESP 와 EBP 사이에 들어가며 각각 ESP 는 스택의 꼭대기, EBP 는 스택의 바닥을 가리킵니다. 바닥을 파고들어가면 답변에 썼던 것처럼 saved EBP 라든가 리턴할 주소 및 인자 리스트가 들어가 있겠죠. ESP 는 스택 크기에 따라 변하기 때문에 EBP - 4, EBP + 4, EBP + 8 등으로 표현한 거고요. (EBP 는 바닥이기 때문에 스택 크기에 따라 변하지 않으니까 기준잡기 편하죠.)


어느분이 답변을 해준것입니다. 이것을 토대로 어느정도 해결이 되었습니다.
그래도 이상한 부분이 좀 있어서 질문을 드립니다.

#include<iostream>
using namespace std;

void show(int a, int b, int c)
{

unsigned int e = 0xdddddddd;

=> 여기까지 왔을때 }
int main()
{
show(0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc);
return 0;
}

위 소스로 바꿔서 컴파일을 해봤습니다.
0066FD44 08 00 00 00 F8 FD ......
0066FD4A 66 00 E4 28 7E 81 f..(~.
0066FD50 00 00 56 00 CC CC ..V...
0066FD56 CC CC CC CC CC CC ......
0066FD5C CC CC CC CC CC CC ......
0066FD62 CC CC CC CC CC CC ......
0066FD68 CC CC CC CC CC CC ......
0066FD6E CC CC CC CC CC CC ......
0066FD74 CC CC CC CC CC CC ......
0066FD7A CC CC CC CC CC CC ......
0066FD80 CC CC CC CC CC CC ......
0066FD86 CC CC CC CC CC CC ......
0066FD8C CC CC CC CC CC CC ......
0066FD92 CC CC DD DD DD DD ......
0066FD98 F8 FD 66 00 8C 10 ..f...
0066FD9E 40 00 AA AA AA AA @.....
0066FDA4 BB BB BB BB CC CC ......
0066FDAA CC CC 00 00 00 00 ...... <---여기부터

EAX = CCCCCCCC EBX = 00560000
ECX = 00000000 EDX = 00790B70
ESI = 817E28E4 EDI = 0066FD98
EIP = 0040104F ESP = 006D46F8
EBP = 0066FD98 EFL = 00000202 CS = 017F
DS = 0187 ES = 0187 SS = 0187 FS = 3A37
GS = 0000 OV=0 UP=0 EI=1 PL=0 ZR=0 AC=0
PE=0 CY=0
ST0 = +0.00000000000000000e+0000

위 내용중
[EBP + 0 에는 이전의 EBP 값이]

여기에서 궁금한것은 마지막 EBP값이.. 위 소스에선

첫째,
위 내용중에 파라미터의 값이 들어가고 IP주소가 들어가고[파랑색]
내용대로 EBP+0 의 값에는 EBP값으로 추정이되는 [빨강색]
F8 FD 66 00 이값이 들어가 있습니다. 이 값이 나오고 난
후에야.. DD DD DD DD 함수내의 로컬변수가 자리잡는것을
볼 수 있었습니다.
왜 이런 EBP값이 저장이 되어야 하는지 잘 모르겠습니다.

둘째,
지금의 구조에서 SS가 필요가 없음을 잘 알았습니다.
그렇다면
지금의 구조에서 ESP 와 EBP만으로 스택을 제어하는것인가요?
그럼 두가지 포인터가 작동하는 원리를 잘 모르겠습니다.

EIP = 0040104F ESP = 0066FD48
EBP = 0066FD98 EFL = 00000202 CS = 017F

여기에서 EBP값이 ESP값보다 낮아야 할것 같은데요..
실제로는 더 높은데요.. 왜 그런것인지 모르겠습니다.

내용중
(EBP 는 바닥이기 때문에 스택 크기에 따라 변하지 않으니까 기준잡기 편하죠.)
EBP의 값또한 고정적이지 않았구요..
스택이 채워질때 감소하는것을 볼 수 있었습니다.
그래서 좀 이해가 안됩니다.(제생각에도 어느 한가지는 고정이되어야 할것같은데요) 어떤 식으로 EBP ESP 로 스택을 제어하는것인지 모르겠습니다.

셋째, 지금은 SS레지스터와 ESP를 엇갈려 배치하는 노력이 필요없다고 하는데요 그렇다면 지금은 SS레지스터의 경우 스택을 제어하는데 안쓰인다는 말인거
같은데요.. 그럼 SS는 어디에 쓰이고 있는지 궁금합니다.

넷째, 어짜피 나중에 더 자세하게 공부를 하겠지만
이런 내용을 공부할만한 책이나 사이트를 알려주시면 감사하겠습니다.

다섯째, 리눅스 상에서는 디버깅을 어떻게 하는지 궁금합니다.
GCC에서 제공을 해주나요? (VC로만 하다보니...^^;)
질문이 좀 많아졌습니다. 죄송..
5가지 답변부탁드립니다

pynoos의 이미지

제가 시간이 없는 관계로 짧게 좀더 ESP와 EBP에 대한 얘기를 하자면, EBP란 frame pointer라는 것입니다.

그놈은 call 이 일어 날때마다 즉 함수 호출할때마다 base stack을 가리키는 형태로 바뀌게 됩니다.

stack은 함수내에서도 변할 수 있기때문에 그런 base pointer를 하나 더 가지고 다니는 형태로 사용됩니다.

gcc 를 사용하신다면, -fomit-frame-pointer 를 넣어 생성되는 assemble 코드를 비교해보세요. 그렇다면 EBP를 사용하지 않는 코드가 생성됩니다. 대신 레지스터 하나를 다른 용도로 사용할 수 있기때문에 최적화에 사용할 수 있습니다.

자세한 것은 나중에 집에서.. 답해드리죠...

그리고 release 버전으로 컴파일 해보셨나요?

gyxor의 이미지

Quote:

그리고, VC 같은 경우 디버그 버전에서는 스택을 넉넉하게 잡는 경향이 있으므로 릴리즈 모드로해보세요. 디스어셈블 코드로 확인하시는 것이 가장 좋습니다.

릴리즈 모드란 VC에서 디버깅모드로나온 exe파일을 보는것이 아니라
통상적으로 하는 정상적인 컴파일을 말씀하시는것인가요?
exe을 도스에서 debug.exe 프로그램을 이용해서 ds세그먼트 내용을 보는것밖에 몰라서요.. 죄송..
disassemble은 못해봤습니다.
암튼 EBP를 사용하지 않고도.. 스택이 제어가 되는군요.. 물론 컴파일러마다
구현이 가능하군요..
그렇다면 EBP의 값이 스택에 저장된 이유도 어느정도 짐작은 되지만..
그래도 ESP와 EBP의 주소값이 EBP가 더 높은이유 등등은..
여전히 의문으로 남습니다.

singlet의 이미지

(저 위에 gyxor 님이 인용하신 글을 적은 사람입니다)

심심하면 보이곤 하던 최적화 옵션이었는데도 뭔지 이해를 정확히 못 하고 있었는데, 그게 그거였군요. pynoos 님 덕분에 하나 배웠습니다. ^^;

그건 그렇고, 위에 인용된 글은 어셈블리나 PC 아키텍처를 잘 모르면서 혼자 삽질해가며 배운 내용들이라서 큰 실수가 있었네요. 독학이다 보니 제가 쓰고 있던 mind model (스택이 '위로' 자라는 --; ) 이 일반적인 mind model 과 반대라는 걸 지금까지 모르고 있다가, gyxor 님이 혼란스러워 하시는 걸 보고 제 실수였음을 깨달았습니다. --; gyxor 님께는 괜히 혼동 일으켜드린 점 죄송스럽습니다. 그럼 이걸로 제 실수까지 명확해졌으니 gyxor 님이 궁금해하시던 점들은 대강 해결됐을 것 같습니다.

barrios의 이미지

Quote:
릴리즈 모드란 VC에서 디버깅모드로나온 exe파일을 보는것이 아니라
통상적으로 하는 정상적인 컴파일을 말씀하시는것인가요?
exe을 도스에서 debug.exe 프로그램을 이용해서 ds세그먼트 내용을 보는것밖에 몰라서요.. 죄송..
disassemble은 못해봤습니다.
암튼 EBP를 사용하지 않고도.. 스택이 제어가 되는군요.. 물론 컴파일러마다
구현이 가능하군요..
그렇다면 EBP의 값이 스택에 저장된 이유도 어느정도 짐작은 되지만..
그래도 ESP와 EBP의 주소값이 EBP가 더 높은이유 등등은..
여전히 의문으로 남습니다.

전 리눅스 초보래서 GCC로 어떻게 해야 할지 몰라 VC로 님의 첫번째 코드를
테스트 해보았습니다. 간단한 답변 드리겠습니다.

먼저 결과론 적으로 전 디버그모드에서 테스트 해보았고 역어셈블해 어셈코드
와 같이 비교해서 보았습니다. 아무 이상 없던데요..^^ 다른 분들의 말씀처럼
디버그 모드에선 지역변수 확보를 위해 스택을 조금 넉넉히 잡습니다. 고려하시
고요. EBP만 가지고 생각하셔도 될듯.. 아 참 그리고 함수로 진입할 때 EBP를 스택에 푸쉬하는 이유는 EBP에다가 ESP를 넣으려고 하는데 먼저 가지고 있던
EBP는 메인에서 사용하던 EBP잖아요. 그 값을 따로 저장안해두고 ESP값을
EBP에 넣어버리면 나중에 함수 복귀 했을 때 먼저 있던 EBP값은 어떻게 회복
해서 올바른 수행을 합니까 ? 그래서 EBP 값을 함수 진입시 넣어두고 EBP에 ESP를 넣고 ESP를 맘대로 가지고 놀다가 나중에 EBP를 POP해주면 .. 되잖아요.. 이해가 되시려나 모르겠네요.. 말재주가 없어가지고.

1. 먼저 디버깅 모드로 들어갔을 때 툴바를 잘 보시면 Disassembly 라는 버튼
이 생깁니다. 그거 눌러보세요..역어셈블된 코드 보실수 있을 겁니다. (단,
디폴트 설정에서 이 버튼은 디버그로 실행했을 때만 나타납니다. )

2. EBP의 필요성은 앞서서 말씀 주셨던 것처럼 단순히 계산을 편하게 하고자
함입니다. 변하지 않는 스택의 바닥을 EBP에 넣어 놓고 위아래로 계산하여
취하고자 하는 변수를 취하기 위함입니다. 당연히 최적화옵션을 제공하거나
Release로 컴파일 하면 아마 빠지겠죠..^^

2. 1 그렇다면 릴리즈로 컴파일 하는 방법은 Build Menu에 Set Active Configuration에서 Release로 바꿔 놓고 build해서 exe 만들고 F5눌러서 다시 디버그로 실행하면 컴파일러가 모라고모라고 불평합니다. 릴리즈래서 디버그
정보 따 빼 버렸는데 왜 디버그로 실행하냐고..무시 하고 GO하십시요. 그럼
커널 소스 코드까지 다 보입니다. 물론 어셈이니 분석은 알아서.. 전 머리가 아
파서.. 아마 startup code까지 다 보실수 있을 겁니다. 모가 startup인지 몰라
서 그렇지..
2.2 왜 EBP가 ESP보다 더 높나 ? 이것에 대한 답변은 많은 서적들이 설명하고
있는데 스택은 위에서 아래로 자랍니다. 즉 상위주소가 바닥이 되고 하위주소가
꼭대기가 됩니다. 이렇게 해야 좋답니다. 힙은 아래에서 위로 자라고..

3. Intel 계열의 CPU를 사용하신다면 Intel Arthictecture Developer's Manual을 적극 추천드립니다. 다 나와 있습니다. 왜 스택은 위에서 아래로
자라야 하는지까지.

님 덕분에 저도 오래간만에 어셈까지 뒤적여보게 되었네요. 감사~

p.s : 아참 GCC에서 VC처럼 소스코드 한줄 나오고 그 밑에 어셈코드 나오고
이런식으로 볼수 있는 방법 없나요 .. ?? 혹시 아시면 좀 !~~ ^^;;

pynoos의 이미지

답변을 제시간에 못했는데, barrios 님이 잘 설명해주신것 같습니다.

SS 는 Protected model에서 application 프로그램을 할 때는 그다지 중요하지 않습니다. (제가 알기로는..)
과거 8086 의 real mode에서는 꽤 신경쓰이는 addressing 문제가 virtual memory 개념으로 바뀌어서 그렇습니다.
application 개발자들은 OS 에서 일어나는 일을 모른다고 가정하면, 마치 메모리를 혼자 사용하는 것처럼 ESP가 32bit 주소공간을 그대로 access 할 수 있으므로 문제 없습니다.

그리고, gdb에서 vc 처럼 볼 수 있는 방법은 없습니다. ^^

gyxor의 이미지

릴리즈 모드에 대해서 알게되었습니다. 감사합니다.
EBP 레지스터자체는 고정이라고 하면서도 값이 바뀌는 이유가..
ESP 값이 EBP로 들어가기 때문이라는것은 잘 알았습니다.
EBP가 최상위 비트여야하므로 ESP보다는 당연히 주소값이 커야한다는것도
잘 알았습니다.
감사합니다.
답변해주신대로..

릴리즈 모드로 역어셈블한 코드를 봤는데요..

좀이상한것은.. EBP를 사용하고 있는거 같아서요..

00403B4F 55                     push        ebp
00403B50 8B EC                  mov         ebp,esp
00403B52 6A FF                push        0FFh

ESP EBP 레지스터로 아무리 검색을 해봐도 스택위치가 어딘지
찾을수가 없네요..
메모리상에 어떻게 올라가는지는 확인을 못해봤습니다.

여전히..리눅스 상에서는 디버깅을 어떻게 하는지 궁금합니다.
제가 도스에서 DEBUG프로그램과 VC만 쓸줄 알아서요..
리눅스였다면.. 무엇으로 스택등의 메모리 코드를 볼 수 있는지 궁금합니다.

pynoos의 이미지

gdb 로 봅니다...

소스 컴파일 할 때는 -g 를 넣구요.

gyxor의 이미지

gdb 정말 편리하군요..
print명령어는 정말 유용하게 쓰일것 같습니다.
코드내용을 볼 수 없는것이 좀 아쉽네요..
pynoos님을 비롯해서 답변해주신 모든분들께 감사드립니다. ^^

댓글 달기

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