From 02cb21312b2d8db56e303cf334ea0e2e17224e7c Mon Sep 17 00:00:00 2001 From: Maldron Date: Wed, 14 Aug 2024 15:01:46 +0900 Subject: [PATCH 01/45] readme ko --- ko_doc/README_KO.md | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 ko_doc/README_KO.md diff --git a/ko_doc/README_KO.md b/ko_doc/README_KO.md new file mode 100644 index 0000000..e8613cb --- /dev/null +++ b/ko_doc/README_KO.md @@ -0,0 +1,90 @@ +# 어셈블리 프로그래밍 + +[![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa] [![Check Links](https://github.com/0xAX/asm/actions/workflows/link-check.yaml/badge.svg)](https://github.com/0xAX/asm/actions/workflows/link-check.yaml) [![star this repo](https://badgen.net/github/stars/0xAX/asm)](https://github.com/0xAX/asm) [![이 저장소 포크하기](https://badgen.net/github/forks/0xAX/asm)](https://github.com/0xAX/asm/fork) [![기여 환영](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/0xAX/asm/issues) [![PR 환영](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) + +이 저장소는 [assembly](https://en.wikipedia.org/wiki/Assembly_language) 프로그래밍 언어를 소개하는 블로그 포스트들을 포함하고 있습니다. 현재 모든 내용과 예제는 [x86_64](https://en.wikipedia.org/wiki/X86-64) 프로세서와 GNU Linux 운영 체제만을 다루고 있습니다. 향후에는 [ARM64](https://en.wikipedia.org/wiki/AArch64) 아키텍처에 대한 학습 자료도 게시할 계획입니다. + +경험 많은 프로그래머이든 아니든, 이 포스트들은 모든 사람이 어셈블리 프로그래밍 언어를 배울 수 있도록 의도되었습니다. 포스트들은 다음 주제들을 다룹니다: + +- x86_64 프로세서 아키텍처의 기본 설명 +- 어셈블리 프로그래밍 언어로 간단한 프로그램을 작성, 빌드 및 실행하는 방법 +- Linux용 프로그램의 주요 구성 요소 +- 메모리 할당의 기초, 스택과 힙이란 무엇인가 +- 시스템 콜이란 무엇이며 프로그램이 운영 체제와 어떻게 상호 작용하는가 +- 부동 소수점 숫자가 컴퓨터 메모리에서 어떻게 표현되는가 +- C 프로그램에서 어셈블리 코드를 호출하는 방법 +- 그 외 다양한 주제... + +즐겁게 배우세요! + +![Magic](./content/assets/asm-introduction.png) + +각 포스트에 대한 링크는 다음과 같습니다: + + * [Say hello to x86_64 Assembly part 1](https://github.com/0xAX/asm/blob/master/content/asm_1.md) + * [Say hello to x86_64 Assembly part 2](https://github.com/0xAX/asm/blob/master/content/asm_2.md) + * [Say hello to x86_64 Assembly part 3](https://github.com/0xAX/asm/blob/master/content/asm_3.md) + * [Say hello to x86_64 Assembly part 4](https://github.com/0xAX/asm/blob/master/content/asm_4.md) + * [Say hello to x86_64 Assembly part 5](https://github.com/0xAX/asm/blob/master/content/asm_5.md) + * [Say hello to x86_64 Assembly part 6](https://github.com/0xAX/asm/blob/master/content/asm_6.md) + * [Say hello to x86_64 Assembly part 7](https://github.com/0xAX/asm/blob/master/content/asm_7.md) + * [Say hello to x86_64 Assembly part 8](https://github.com/0xAX/asm/blob/master/content/asm_8.md) + +## 요구 사항 + +코드 예제를 실행하려면 다음 도구가 필요합니다: + +- [64bit Linux 배포판](https://en.wikipedia.org/wiki/Linux_distribution) +- [make](https://www.gnu.org/software/make/) +- [NASM](https://nasm.us/) +- [binutils](https://www.gnu.org/software/binutils/) + +## 번역 + +기여자분들 덕분에 어셈블리 프로그래밍에 대한 포스트들이 다양한 언어로 번역되었습니다. + +> [!Note] +> 번역은 원본 내용과 다를 수 있습니다. + +### 중국어 + + * [译文: Say hello to x64 Assembly [part 1]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-1.md) + * [译文: Say hello to x64 Assembly [part 2]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-2.md) + * [译文: Say hello to x64 Assembly [part 3]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-3.md) + * [译文: Say hello to x64 Assembly [part 4]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-4.md) + * [译文: Say hello to x64 Assembly [part 5]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-5.md) + * [译文: Say hello to x64 Assembly [part 6]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-6.md) + * [译文: Say hello to x64 Assembly [part 7]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-7.md) + * [译文: Say hello to x64 Assembly [part 8]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-8.md) + +### 튀르키예어 + + * [X86_64 Assembly'a merhaba deyin bölüm 1](https://github.com/furkanonder/asm/blob/master/bolumler/1.md) + * [X86_64 Assembly'a merhaba deyin bölüm 2](https://github.com/furkanonder/asm/blob/master/bolumler/2.md) + * [X86_64 Assembly'a merhaba deyin bölüm 3](https://github.com/furkanonder/asm/blob/master/bolumler/3.md) + * [X86_64 Assembly'a merhaba deyin bölüm 4](https://github.com/furkanonder/asm/blob/master/bolumler/4.md) + * [X86_64 Assembly'a merhaba deyin bölüm 5](https://github.com/furkanonder/asm/blob/master/bolumler/5.md) + * [X86_64 Assembly'a merhaba deyin bölüm 6](https://github.com/furkanonder/asm/blob/master/bolumler/6.md) + * [X86_64 Assembly'a merhaba deyin bölüm 7](https://github.com/furkanonder/asm/blob/master/bolumler/7.md) + * [X86_64 Assembly'a merhaba deyin bölüm 8](https://github.com/furkanonder/asm/blob/master/bolumler/8.md) + +## 기여 + +프로젝트에 기여하는 방법은 [기여 가이드](./CONTRIBUTING.md)를 읽어보세요. 기여할 때는 [행동 강령](./CODE_OF_CONDUCT.md)을 따라주세요. + +## 라이선스 + +저장소의 각 Markdown 파일은 +[크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스][cc-by-nc-sa]에 따라 라이선스가 부여됩니다. + +[![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa] + +[cc-by-nc-sa]: https://creativecommons.org/licenses/by-nc-sa/4.0/ +[cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png +[cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg + +## 저자 + +기술 내용은 [@0xAX](https://x.com/0xAX)가 작성했습니다. + +텍스트 개선에 큰 도움을 준 [@klaudiagrz](https://github.com/klaudiagrz)에게도 감사드립니다. From 5018c642b94a6c63f7ab6c584678e25869f2a726 Mon Sep 17 00:00:00 2001 From: Maldron Date: Wed, 14 Aug 2024 16:14:07 +0900 Subject: [PATCH 02/45] asm part 1 --- ko_doc/post/ko_asm_1.md | 148 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 ko_doc/post/ko_asm_1.md diff --git a/ko_doc/post/ko_asm_1.md b/ko_doc/post/ko_asm_1.md new file mode 100644 index 0000000..f1162bc --- /dev/null +++ b/ko_doc/post/ko_asm_1.md @@ -0,0 +1,148 @@ +## 소개 + +우리 사이에는 많은 개발자들이 있습니다. 우리는 매일 엄청난 양의 코드를 작성합니다. 때때로 나쁘지 않은 코드도 작성하죠 :) 우리 모두는 다음과 같은 가장 간단한 코드를 쉽게 작성할 수 있습니다: + +```c +#include + +int main() { + int x = 10; + int y = 100; + printf("x + y = %d", x + y); + return 0; +} +``` + +우리 모두 이 C 코드가 무엇을 하는지 이해할 수 있습니다. 하지만... 이 코드가 저수준에서 어떻게 작동하는지 아는 사람은 많지 않을 것입니다. +저도 그랬습니다. Haskell, Erlang, Go와 같은 고수준 프로그래밍 언어로 코드를 작성할 수는 있었지만, +이 코드가 컴파일된 후 저수준에서 어떻게 동작하는지 전혀 몰랐습니다. 그래서 어셈블리 언어로 몇 가지 깊은 단계를 밟아가면서 배운 내용을 설명하기로 했습니다. +이 과정이 저뿐만 아니라 여러분에게도 흥미로울 거라 생각합니다. 약 5~6년 전, 대학교에서 간단한 프로그램을 작성하기 위해 어셈블리를 사용한 적이 있습니다. +당시에는 Turbo Assembly와 DOS 운영 체제를 사용했습니다. 지금은 Linux-x86-64 운영 체제를 사용하고 있습니다. 그렇다면 Linux 64비트와 DOS 16비트 사이에는 큰 차이가 있을 것입니다. 시작해봅시다. + +## 준비 + +시작하기 전에 몇 가지 준비를 해야 합니다. +앞서 언급했듯이, 저는 Ubuntu (Ubuntu 14.04.1 LTS 64비트)를 사용하고 있으며, 따라서 이 글에서는 이 운영 체제와 아키텍처를 기준으로 설명할 것입니다. +각 CPU는 서로 다른 명령어 집합을 지원합니다. 저는 Intel Core i7 870 프로세서를 사용하며, 모든 코드는 이 프로세서를 기준으로 작성될 것입니다. +또한 NASM 어셈블리를 사용할 것입니다. NASM을 설치하려면 다음 명령어를 실행하면 됩니다: + +``` +$ sudo apt-get install nasm +``` + +NASM의 버전은 2.0.0 이상이어야 합니다. +저는 2013년 12월 29일에 컴파일된 NASM 2.10.09 버전을 사용하고 있습니다. +마지막으로 어셈블리 코드를 작성할 텍스트 편집기가 필요합니다. 저는 Emacs와 nasm-mode.el을 사용합니다. +물론, 다른 편집기를 사용해도 상관없습니다. +Emacs를 사용한다면, nasm-mode.el을 다운로드하고 Emacs를 다음과 같이 설정할 수 있습니다: + +```elisp +(load "~/.emacs.d/lisp/nasm.el") +(require 'nasm-mode) +(add-to-list 'auto-mode-alist '("\\.\\(asm\\|s\\)$" . nasm-mode)) +``` +현재로서는 이 정도 준비가 필요합니다. 다른 도구는 다음 게시물에서 설명하겠습니다 + +## NASM 어셈블리 문법 + +여기서는 전체 어셈블리 문법을 설명하지 않을 것이며, 이번 게시물에서 사용할 문법의 일부만을 다룰 것입니다. +일반적으로 NASM 프로그램은 여러 섹션으로 나누어집니다. 이번 게시물에서는 다음 두 섹션을 다룰 것입니다: + +* 데이터 섹션 +* 텍스트 섹션 + +데이터 섹션은 상수를 선언하는 데 사용됩니다. 이 데이터는 실행 중에 변경되지 않습니다. 수학 상수나 기타 상수 등을 선언할 수 있습니다. 데이터 섹션을 선언하는 문법은 다음과 같습니다: + +```assembly + section .data +``` + +텍스트 섹션은 코드에 사용됩니다. 이 섹션은 프로그램의 시작 지점을 정의하는 global _start로 시작해야 합니다. + +```assembly + section .text + global _start + _start: +``` + +주석은 `;` 기호로 시작합니다. 모든 NASM 소스 코드 라인은 다음 네 가지 필드의 조합을 포함합니다: + +``` +[label:] instruction [operands] [; comment] +``` + +대괄호 안의 필드는 선택적입니다. 기본 NASM 명령어는 두 부분으로 구성됩니다. 첫 번째는 실행할 명령어의 이름이고, 두 번째는 이 명령어의 피연산자입니다. 예를 들어: + +```assembly + MOV COUNT, 48 ; Put value 48 in the COUNT variable +``` + +## Hello world + +첫 번째 NASM 어셈블리 프로그램을 작성해 봅시다. 물론, 전통적인 "Hello, World!" 프로그램입니다. 코드 예제는 다음과 같습니다: + +```assembly +section .data + msg db "hello, world!" + +section .text + global _start +_start: + mov rax, 1 + mov rdi, 1 + mov rsi, msg + mov rdx, 13 + syscall + mov rax, 60 + mov rdi, 0 + syscall +``` + +네, printf("Hello world")처럼 보이지는 않습니다. +이 코드가 어떻게 작동하는지 이해해 봅시다. 1~2행을 살펴보세요. 데이터 섹션을 정의하고, 여기에서 "Hello world" 값을 가지는 msg 상수를 정의했습니다. +이제 이 상수를 코드에서 사용할 수 있습니다. 다음은 텍스트 섹션과 프로그램의 시작 지점을 선언한 것입니다. 프로그램은 7행부터 실행됩니다. 이제 가장 흥미로운 부분이 시작됩니다. +mov 명령어는 두 개의 피연산자를 받아서 두 번째 값을 첫 번째에 넣습니다. 그런데 rax, rdi 등이 무엇인지 궁금할 것입니다. 위키피디아에 따르면: + +``` +중앙 처리 장치(CPU)는 컴퓨터 프로그램의 명령어를 수행하고 시스템의 기본 산술, 논리, 입출력 작업을 수행하는 하드웨어입니다. +``` + +좋습니다, CPU는 어떤 작업을 수행하고 있습니다. +하지만 이 작업을 위한 데이터는 어디에서 가져올까요? 첫 번째 대답은 메모리입니다. +그러나 메모리에서 데이터를 읽고 저장하는 과정은 복잡하기 때문에 CPU는 레지스터라는 자체 내부 메모리 저장 위치를 가지고 있습니다: + +![registers](/content/assets/registers.png) + +따라서 mov rax, 1이라고 쓸 때는 rax 레지스터에 1을 넣는다는 의미입니다. +이제 rax, rdi, rbx 등이 무엇인지 알게 되었습니다. 그러나 언제 rax를 사용하고 언제 rsi를 사용하는지 알아야 합니다. + +* `rax` - 임시 레지스터; syscall을 호출할 때 rax에는 syscall 번호가 있어야 합니다. +* `rdx` - 함수에 3번째 인자를 전달하는 데 사용됩니다. +* `rdi` - 함수에 1번째 인자를 전달하는 데 사용됩니다. +* `rsi` - 함수에 2번째 인자를 전달하는 데 사용되는 포인터입니다. + +다시 말해, 우리는 `sys_write` 시스템 호출을 호출하고 있습니다. `sys_write`는 다음과 같습니다: + +```C +size_t sys_write(unsigned int fd, const char * buf, size_t count); +``` + +이 함수는 3개의 인자를 받습니다: + +* `fd` - 파일 디스크립터. 표준 입력, 표준 출력 및 표준 오류를 위해 각각 0, 1 및 2를 사용할 수 있습니다. +* `buf` - 파일 디스크립터가 가리키는 파일로부터 얻은 내용을 저장할 수 있는 문자 배열을 가리킵니다. +* `count` - 파일에서 문자 배열로 쓰여질 바이트 수를 지정합니다. + +`sys_write` 시스템 호출이 3개의 인자를 받으며, syscall 테이블에서 1번 번호를 가진다는 것을 알았습니다. +이제 우리의 "Hello world" 구현을 다시 살펴보겠습니다. rax 레지스터에 1을 넣으면 `sys_write` 시스템 호출을 사용할 것이라는 의미입니다. +다음 줄에서는 rdi 레지스터에 1을 넣습니다. 이는 sys_write의 첫 번째 인자인 표준 출력(1)을 의미합니다. 다음으로 msg에 대한 포인터를 rsi 레지스터에 저장합니다. +이는 sys_write의 두 번째 인자인 버퍼를 의미합니다. 그리고 문자열의 길이(13)를 rdx에 전달하여 sys_write의 세 번째 인자로 사용합니다. 이제 `sys_write`의 모든 인자를 설정했으므로, 11행에서 syscall 명령어로 호출합니다. +"Hello world" 문자열을 출력한 후, 프로그램을 올바르게 종료해야 합니다. rax 레지스터에 60을 넣습니다. 60은 종료 시스템 호출 번호입니다. 또한 rdi 레지스터에 0을 넣어 오류 코드를 설정합니다. 0은 성공적인 종료를 의미합니다. +이것으로 "Hello world" 프로그램이 완료됩니다. 꽤 간단하죠 :) 이제 프로그램을 빌드해 보겠습니다. 예를 들어, 이 코드를 hello.asm 파일에 코드를 작성했다고 가정합시다. 그러면 다음 명령어를 실행하여 프로그램을 빌드할 수 있습니다: + +``` +$ nasm -f elf64 -o hello.o hello.asm +$ ld -o hello hello.o +``` + +이후, ./hello를 실행하면 터미널에서 "Hello world" 문자열이 출력됩니다. From a4b970bc6940d894d681aa413d9cb2adf0270b34 Mon Sep 17 00:00:00 2001 From: Maldron Date: Wed, 14 Aug 2024 16:28:54 +0900 Subject: [PATCH 03/45] part 2 save --- ko_doc/post/ko_asm_2.md | 102 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 ko_doc/post/ko_asm_2.md diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md new file mode 100644 index 0000000..09f6b0f --- /dev/null +++ b/ko_doc/post/ko_asm_2.md @@ -0,0 +1,102 @@ +며칠 전에 첫 블로그 포스트인 "x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 1]"을 작성했는데, 예상외로 큰 관심을 받았습니다: + +![newscombinator](./assets/newscombinator-screenshot.png) +![reddit](./assets/reddit-screenshot.png) + +이것은 저를 더욱 학습하는 데 동기 부여를 해주었습니다. 그동안 많은 사람들로부터 피드백을 받았고, 감사의 말을 많이 들었습니다. 하지만 저에게 더 중요한 것은 많은 조언과 적절한 비판이었습니다. 특히 훌륭한 피드백을 주신 분들에게 감사의 말씀을 전하고 싶습니다: + +* [Fiennes](https://reddit.com/user/Fiennes) +* [Grienders](https://disqus.com/by/Universal178/) +* [nkurz](https://news.ycombinator.com/user?id=nkurz) + +그리고 Reddit과 Hacker News에서 토론에 참여한 모든 분들께도 감사합니다. 첫 번째 파트가 절대 초보자에게는 다소 명확하지 않았다는 의견이 많아서, 더 많은 정보를 담은 포스트를 작성하기로 결정했습니다. 자, 이제 x86_64 어셈블리에 인사하기 [파트 2]를 시작해 보겠습니다. + +## 용어 및 개념 + +위에서 언급했듯이, 첫 번째 포스트의 일부 부분이 명확하지 않다는 피드백을 많이 받았습니다. 그래서 이번 포스트부터는 우리가 이번과 다음 포스트에서 볼 수 있는 몇 가지 용어를 설명하려고 합니다. + +레지스터 - 레지스터는 프로세서 내부의 소량의 저장소입니다. 프로세서의 주요 역할은 데이터 처리입니다. 프로세서는 메모리에서 데이터를 가져올 수 있지만, 이는 느린 작업입니다. 그래서 프로세서에는 레지스터라는 내부 제한된 데이터 저장소가 있습니다. + +리틀 엔디안 - 메모리를 하나의 큰 배열로 상상할 수 있습니다. 이 배열은 바이트를 포함합니다. 각 주소는 메모리 배열의 한 요소를 저장합니다. 각 요소는 하나의 바이트입니다. 예를 들어, 4바이트가 AA 56 AB FF로 주어졌을 때, 리틀 엔디안에서는 가장 낮은 주소에 가장 낮은 유의 바이트가 저장됩니다: + +``` + 0 FF + 1 AB + 2 56 + 3 AA +``` + + +여기서 0, 1, 2, 3은 메모리 주소입니다. + +빅 엔디안 - 빅 엔디안은 리틀 엔디안과 반대 순서로 바이트를 저장합니다. 따라서 AA 56 AB FF 바이트 시퀀스는 다음과 같습니다: + +``` + 0 AA + 1 56 + 2 AB + 3 FF +``` + +시스템 호출 (Syscall) - 사용자 수준 프로그램이 운영 체제에게 작업을 요청하는 방법입니다. 시스템 호출 테이블은 [여기](https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl)에서 찾을 수 있습니다. + +스택 (Stack) - 프로세서는 매우 제한된 수의 레지스터를 가지고 있습니다. 따라서 스택은 연속적인 메모리 영역으로, `RSP`, `SS`, `RIP`와 같은 특수 레지스터로 주소 지정됩니다. 스택에 대해서는 다음 포스트에서 자세히 살펴보겠습니다. + +섹션 (Section) - 모든 어셈블리 프로그램은 섹션으로 나뉩니다. 다음과 같은 섹션이 있습니다: + +* `data` - 초기화된 데이터 또는 상수를 선언하는 데 사용됩니다. +* `bss` - 초기화되지 않은 변수를 선언하는 데 사용됩니다. +* `text` - 코드에 사용됩니다. + +범용 레지스터 (General-purpose registers) - 총 16개의 범용 레지스터가 있습니다 - rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15. 물론, 어셈블리 프로그래밍과 관련된 용어와 개념은 이 리스트에 포함되지 않은 것들이 많습니다. 다음 블로그 포스트에서 낯선 단어를 만나게 되면, 그에 대한 설명이 있을 것입니다. + +## 데이터 타입 + +기본 데이터 타입은 바이트, 워드, 더블워드, 쿼드워드, 더블 쿼드워드입니다. 바이트는 8비트, 워드는 2바이트, 더블워드는 4바이트, 쿼드워드는 8바이트, 더블 쿼드워드는 16바이트 (128비트)입니다. + +현재는 정수 숫자만 다룰 것이므로, 이에 대해 살펴보겠습니다. 정수에는 부호 없는 정수와 부호 있는 정수가 있습니다. 부호 없는 정수는 바이트, 워드, 더블워드, 쿼드워드에 저장된 부호 없는 이진 숫자입니다. 그 값의 범위는 부호 없는 바이트 정수의 경우 0부터 255까지, 부호 없는 워드 정수의 경우 0부터 65,535까지, 부호 없는 더블워드 정수의 경우 0부터 2^32 – 1까지, 부호 없는 쿼드워드 정수의 경우 0부터 2^64 – 1까지입니다. 부호 있는 정수는 부호가 있는 이진 숫자이며, 부호 없는 바이트, 워드 등에 저장됩니다. 부호 비트는 음수 정수에 대해 설정되고 양수 정수와 0에 대해 지워집니다. 정수 값의 범위는 바이트 정수의 경우 -128부터 +127까지, 워드 정수의 경우 -32,768부터 +32,767까지, 더블워드 정수의 경우 -2^31부터 +2^31 – 1까지, 쿼드워드 정수의 경우 -2^63부터 +2^63 – 1까지입니다. + +## 섹션 + +앞서 언급했듯이, 모든 어셈블리 프로그램은 섹션으로 나뉩니다. 데이터 섹션, 텍스트 섹션, bss 섹션 등이 있습니다. 데이터 섹션을 살펴보겠습니다. 이 섹션의 주요 목적은 초기화된 상수를 선언하는 것입니다. 예를 들면: + +```assembly +section .data + num1: equ 100 + num2: equ 50 + msg: db "Sum is correct", 10 +``` + +여기까지 거의 모든 것이 명확합니다. `num1`, `num2`, `msg`라는 이름의 3개의 상수와 각각의 값으로 100, 50, "Sum is correct", 10이 정의되었습니다. 그런데 `db`, `equ`는 무엇일까요? 실제 NASM은 여러 가지 의사 명령어를 지원합니다: + +* DB, DW, DD, DQ, DT, DO, DY, DZ - 초기화된 데이터를 선언하는 데 사용됩니다. 예를 들어: + +```assembly +;; 4바이트 1h, 2h, 3h, 4h 초기화 +db 0x01,0x02,0x03,0x04 + +;; 0x12 0x34로 워드 초기화 +dw 0x1234 +``` + +* RESB, RESW, RESD, RESQ, REST, RESO, RESY, RESZ - 초기화되지 않은 변수를 선언하는 데 사용됩니다. +* INCBIN - 외부 이진 파일 포함 +* EQU - 상수를 정의합니다. 예를 들면: + +```assembly +;; now one is 1 +one equ 1 +``` + +이 글에서는 그 중 일부를 실제로 살펴볼 것입니다. 다른 내용은 다음 게시물에서 다룰 예정입니다. + +## 제어 흐름 + +일반적으로 프로그래밍 언어는 평가 순서를 변경할 수 있는 기능을 가지고 있습니다 (if 문, case 문, goto 등). 어셈블리에서도 이러한 기능이 있습니다. cmp 명령어는 두 값 간의 비교를 수행합니다. 이 명령어는 조건부 점프 명령어와 함께 사용되어 결정적인 동작을 수행합니다. 예를 들어: + +```assembly +;; compare rax with 50 +cmp rax, 50 +``` + + From c7af1211e4df29a4a9ced6c3d8f47eebb03dc65e Mon Sep 17 00:00:00 2001 From: Maldron Date: Thu, 15 Aug 2024 16:34:06 +0900 Subject: [PATCH 04/45] upload asm 2 part --- ko_doc/post/ko_asm_2.md | 137 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index 09f6b0f..57cf616 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -99,4 +99,141 @@ one equ 1 cmp rax, 50 ``` +`cmp` 명령어는 단순히 두 값을 비교할 뿐, 그 값들에 영향을 주거나 비교 결과에 따라 어떤 것도 실행하지 않습니다. 비교 후 어떤 동작을 수행하려면 조건부 점프 명령어를 사용합니다. 다음과 같은 명령어들이 있습니다: + +* `JE` - 같으면 +* `JZ` - 0이면 +* `JNE` - 같지 않으면 +* `JNZ` - 0이 아니면 +* `JG` - 첫 번째 피연산자가 두 번째보다 크면 +* `JGE` - 첫 번째 피연산자가 두 번째보다 크거나 같으면 +* `JA` - JG와 동일하지만 부호 없는 비교 수행 +* `JAE` - JGE와 동일하지만 부호 없는 비교 수행 + +예를 들어, C에서의 if/else 문과 비슷한 것을 구현하고 싶다면: + +```C +if (rax != 50) { + exit(); +} else { + right(); +} +``` + +# 어셈블리 구문 비교와 점프 + +`cmp` 명령어는 단순히 두 값을 비교할 뿐, 그 값들에 영향을 주거나 비교 결과에 따라 어떤 것도 실행하지 않습니다. 비교 후 어떤 동작을 수행하려면 조건부 점프 명령어를 사용합니다. 다음과 같은 명령어들이 있습니다: + +* `JE` - 같으면 +* `JZ` - 0이면 +* `JNE` - 같지 않으면 +* `JNZ` - 0이 아니면 +* `JG` - 첫 번째 피연산자가 두 번째보다 크면 +* `JGE` - 첫 번째 피연산자가 두 번째보다 크거나 같으면 +* `JA` - JG와 동일하지만 부호 없는 비교 수행 +* `JAE` - JGE와 동일하지만 부호 없는 비교 수행 + +예를 들어, C에서의 if/else 문과 비슷한 것을 구현하고 싶다면 어셈블리에서는 다음과 같이 됩니다: + +```assembly +;; rax를 50과 비교 +cmp rax, 50 +;; rax가 50과 같지 않으면 .exit 수행 +jne .exit +jmp .right +``` + +또한 무조건 점프 구문도 있습니다: + +```assembly +JMP label +``` + +예를 들면: + +```assembly +_start: + ;; .... + ;; do something and jump to .exit label + ;; .... + jmp .exit + +.exit: + mov rax, 60 + mov rdi, 0 + syscall +``` + +여기서 우리는 _start 레이블 다음에 일부 코드를 가질 수 있고, 이 모든 코드는 실행될 것입니다. 어셈블리는 제어를 .exit 레이블로 이전하고, .exit: 다음의 코드가 실행되기 시작할 것입니다. +무조건 점프는 종종 반복문에서 사용됩니다. 예를 들어 우리는 레이블과 그 뒤에 일부 코드를 가집니다. +이 코드는 무언가를 실행하고, 그 다음 조건이 있고 조건이 성공적이지 않으면 이 코드의 시작으로 점프합니다. 반복문은 다음 파트에서 다룰 것입니다. + +## 예제 + +간단한 예제를 봅시다. 두 개의 정수를 받아, 이 숫자들의 합을 구하고 미리 정의된 숫자와 비교합니다. +미리 정의된 숫자가 합과 같으면 화면에 무언가를 출력하고, 그렇지 않으면 그냥 종료합니다. 여기 우리 예제의 소스 코드가 있습니다: + +```assembly +section .data + ; Define constants + num1: equ 100 + num2: equ 50 + ; initialize message + msg: db "Sum is correct\n" + +section .text + + global _start + +;; entry point +_start: + ; set num1's value to rax + mov rax, num1 + ; set num2's value to rbx + mov rbx, num2 + ; get sum of rax and rbx, and store it's value in rax + add rax, rbx + ; compare rax and 150 + cmp rax, 150 + ; go to .exit label if rax and 150 are not equal + jne .exit + ; go to .rightSum label if rax and 150 are equal + jmp .rightSum + +; Print message that sum is correct +.rightSum: + ;; write syscall + mov rax, 1 + ;; file descritor, standard output + mov rdi, 1 + ;; message address + mov rsi, msg + ;; length of message + mov rdx, 15 + ;; call write syscall + syscall + ; exit from program + jmp .exit + +; exit procedure +.exit: + ; exit syscall + mov rax, 60 + ; exit code + mov rdi, 0 + ; call exit syscall + syscall +``` + +소스 코드를 살펴봅시다. 우선 데이터 섹션에 두 개의 상수 num1, num2와 "Sum is correct\n" 값을 가진 변수 msg가 있습니다. 이제 14번째 줄을 보세요. +여기서 프로그램의 진입점이 시작됩니다. num1과 num2 값을 범용 레지스터 rax와 rbx로 전송합니다. +add 명령어로 이들을 더합니다. add 명령어 실행 후, rax와 rbx의 값을 더하고 그 결과를 rax에 저장합니다. +이제 rax 레지스터에 num1과 num2의 합이 있습니다. + +좋습니다. num1은 100이고 num2는 50입니다. 우리의 합은 150이 되어야 합니다. +cmp 명령어로 이를 확인해 봅시다. rax와 150을 비교한 후 비교 결과를 확인합니다. +rax와 150이 같지 않으면(jne로 확인) .exit 레이블로 갑니다. 같다면 .rightSum 레이블로 갑니다. +이제 두 개의 레이블이 있습니다: .exit와 .rightSum. 첫 번째는 단순히 rax에 60을 설정합니다. +이는 exit 시스템 콜 번호이며, rdi에는 0을 설정합니다. 이는 종료 코드입니다. +두 번째인 .rightSum은 매우 간단합니다. "Sum is correct"를 출력할 뿐입니다. From 077bb916eb5d5e11f3f6a5cd777f700ce7f590bb Mon Sep 17 00:00:00 2001 From: Maldron Date: Thu, 15 Aug 2024 16:39:25 +0900 Subject: [PATCH 05/45] pic upload --- ko_doc/post/ko_asm_2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index 57cf616..35e1387 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -1,7 +1,7 @@ 며칠 전에 첫 블로그 포스트인 "x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 1]"을 작성했는데, 예상외로 큰 관심을 받았습니다: -![newscombinator](./assets/newscombinator-screenshot.png) -![reddit](./assets/reddit-screenshot.png) +![newscombinator](/content/assets/newscombinator-screenshot.png) +![reddit](/content/assets/reddit-screenshot.png) 이것은 저를 더욱 학습하는 데 동기 부여를 해주었습니다. 그동안 많은 사람들로부터 피드백을 받았고, 감사의 말을 많이 들었습니다. 하지만 저에게 더 중요한 것은 많은 조언과 적절한 비판이었습니다. 특히 훌륭한 피드백을 주신 분들에게 감사의 말씀을 전하고 싶습니다: From c969307fc7e9632bdebce5bc962deb0fdeb3f3da Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 09:41:14 +0900 Subject: [PATCH 06/45] Update README_KO.md --- ko_doc/README_KO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ko_doc/README_KO.md b/ko_doc/README_KO.md index e8613cb..03c15c0 100644 --- a/ko_doc/README_KO.md +++ b/ko_doc/README_KO.md @@ -17,7 +17,7 @@ 즐겁게 배우세요! -![Magic](./content/assets/asm-introduction.png) +![Magic](/content/assets/asm-introduction.png) 각 포스트에 대한 링크는 다음과 같습니다: From cbae11022f740894f857ad742ba4e5a2a0a50667 Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 09:43:58 +0900 Subject: [PATCH 07/45] Update ko_asm_2.md --- ko_doc/post/ko_asm_2.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index 35e1387..03a6798 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -3,13 +3,17 @@ ![newscombinator](/content/assets/newscombinator-screenshot.png) ![reddit](/content/assets/reddit-screenshot.png) -이것은 저를 더욱 학습하는 데 동기 부여를 해주었습니다. 그동안 많은 사람들로부터 피드백을 받았고, 감사의 말을 많이 들었습니다. 하지만 저에게 더 중요한 것은 많은 조언과 적절한 비판이었습니다. 특히 훌륭한 피드백을 주신 분들에게 감사의 말씀을 전하고 싶습니다: +이것은 저를 더욱 학습하는 데 동기 부여를 해주었습니다. +그동안 많은 사람들로부터 피드백을 받았고, 감사의 말을 많이 들었습니다. +하지만 저에게 더 중요한 것은 많은 조언과 적절한 비판이었습니다. 특히 훌륭한 피드백을 주신 분들에게 감사의 말씀을 전하고 싶습니다: * [Fiennes](https://reddit.com/user/Fiennes) * [Grienders](https://disqus.com/by/Universal178/) * [nkurz](https://news.ycombinator.com/user?id=nkurz) -그리고 Reddit과 Hacker News에서 토론에 참여한 모든 분들께도 감사합니다. 첫 번째 파트가 절대 초보자에게는 다소 명확하지 않았다는 의견이 많아서, 더 많은 정보를 담은 포스트를 작성하기로 결정했습니다. 자, 이제 x86_64 어셈블리에 인사하기 [파트 2]를 시작해 보겠습니다. +그리고 Reddit과 Hacker News에서 토론에 참여한 모든 분들께도 감사합니다. +첫 번째 파트가 절대 초보자에게는 다소 명확하지 않았다는 의견이 많아서, 더 많은 정보를 담은 포스트를 작성하기로 결정했습니다. +자, 이제 x86_64 어셈블리에 인사하기 [파트 2]를 시작해 보겠습니다. ## 용어 및 개념 From 1b3fa80750a9174ba5e6cfc1eec1af84ef0e5887 Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 09:44:20 +0900 Subject: [PATCH 08/45] Update ko_asm_2.md --- ko_doc/post/ko_asm_2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index 03a6798..8bbb1c3 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -13,7 +13,7 @@ 그리고 Reddit과 Hacker News에서 토론에 참여한 모든 분들께도 감사합니다. 첫 번째 파트가 절대 초보자에게는 다소 명확하지 않았다는 의견이 많아서, 더 많은 정보를 담은 포스트를 작성하기로 결정했습니다. -자, 이제 x86_64 어셈블리에 인사하기 [파트 2]를 시작해 보겠습니다. +자, 이제 x86_64 어셈블리와 친해지기 [파트 2]를 시작해 보겠습니다. ## 용어 및 개념 From f49e7c66c69e314d0d06b1e49db75cd13494328b Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 10:11:03 +0900 Subject: [PATCH 09/45] part 3 --- ko_doc/post/ko_asm_3.md | 278 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 ko_doc/post/ko_asm_3.md diff --git a/ko_doc/post/ko_asm_3.md b/ko_doc/post/ko_asm_3.md new file mode 100644 index 0000000..024cb04 --- /dev/null +++ b/ko_doc/post/ko_asm_3.md @@ -0,0 +1,278 @@ + +스택은 LIFO(Last In, First Out) 원칙으로 작동하는 메모리의 특별한 영역입니다. + +임시 데이터 저장을 위해 16개의 범용 레지스터가 있습니다. +RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP 및 R8-R15입니다. +이는 심각한 애플리케이션에는 너무 적습니다. 따라서 스택에 데이터를 저장할 수 있습니다. +스택의 또 다른 용도는 다음과 같습니다: 함수를 호출할 때 반환 주소가 스택에 복사됩니다. +함수 실행이 끝난 후, 주소가 명령 카운터(RIP)에 복사되고 애플리케이션은 함수 다음 위치에서 계속 실행됩니다. + +예를 들어: + +```assembly +global _start + +section .text + +_start: + mov rax, 1 + call incRax + cmp rax, 2 + jne exit + ;; + ;; Do something + ;; + +incRax: + inc rax + ret +``` + +여기서 애플리케이션 실행 후 rax는 1과 같습니다. +그 다음 rax 값을 1 증가시키는 incRax 함수를 호출하므로 rax 값은 2가 되어야 합니다. +이후 실행은 8번째 줄에서 계속되며, 여기서 rax 값을 2와 비교합니다. + +또한 System V AMD64 ABI에서 읽을 수 있듯이, 처음 6개의 함수 인수는 레지스터로 전달됩니다. 이들은: + +* rdi - 첫 번째 인수 +* rsi - 두 번째 인수 +* rdx - 세 번째 인수 +* rcx - 네 번째 인수 +* r8 - 다섯 번째 인수 +* r9 - 여섯 번째 인수 + +그 다음 인수들은 스택으로 전달됩니다. 따라서 다음과 같은 함수가 있다면: + +```C +int foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7) +{ + return (a1 + a2 - a3 - a4 + a5 - a6) * a7; +} +``` + +처음 6개의 인수는 레지스터로 전달되고, 7번째 인수부터는 스택으로 전달됩니다. + +## 스택 포인터 + +앞서 말한대로 16개의 범용 레지스터가 있고, 그 중 RSP와 RBP 두 개의 흥미로운 레지스터가 있습니다. +RBP는 베이스 포인터 레지스터로, 현재 스택 프레임의 베이스를 가리킵니다. RSP는 스택 포인터로, 현재 스택 프레임의 최상단을 가리킵니다. + +## 명령어 + +스택 작업을 위한 두 가지 명령어가 있습니다: + +* `push argument` - 스택 포인터(RSP)를 증가시키고 스택 포인터가 가리키는 위치에 인수를 저장합니다. +* `pop argument` - 스택 포인터가 가리키는 위치에서 인자로 데이터를 복사합니다. + +간단한 예제를 살펴보겠습니다: + +```assembly +global _start + +section .text + +_start: + mov rax, 1 + mov rdx, 2 + push rax + push rdx + + mov rax, [rsp + 8] + + ;; + ;; Do something + ;; +``` + +여기서 우리는 rax 레지스터에 1을, rdx 레지스터에 2를 넣는 것을 볼 수 있습니다. +그 후 이 레지스터들의 값을 스택에 push합니다. 스택은 LIFO(Last In First Out) 방식으로 작동합니다. +따라서 이후 우리 애플리케이션의 스택은 다음과 같은 구조를 가지게 됩니다: + +![stack diagram](/content/assets/stack-diagram.png) + +그 다음 우리는 rsp + 8 주소를 가진 스택에서 값을 복사합니다. +이는 스택의 최상단 주소를 가져와 여기에 8을 더하고, 이 주소에 있는 데이터를 rax로 복사한다는 의미입니다. 이 후 rax 값은 1이 될 것입니다. + +## 예제 + +한 가지 예를 살펴보겠습니다. 두 개의 명령줄 인수를 받는 간단한 프로그램을 작성해 보겠습니다. 이 인수의 합을 구하고 결과를 출력합니다. + +```assembly +section .data + SYS_WRITE equ 1 + STD_IN equ 1 + SYS_EXIT equ 60 + EXIT_CODE equ 0 + + NEW_LINE db 0xa + WRONG_ARGC db "Must be two command line argument", 0xa +``` + +먼저 몇 가지 값으로 `.data` 섹션을 정의합니다. +여기에는 리눅스 시스템 호출에 대한 네 가지 상수, sys_write, sys_exit 등이 있습니다. +그리고 두 개의 문자열도 있습니다: 첫 번째는 새 줄 기호이고 두 번째는 오류 메시지입니다. + +.text 섹션을 살펴보겠습니다. 이 섹션은 프로그램의 코드로 구성되어 있습니다: + +```assembly +section .text + global _start + +_start: + pop rcx + cmp rcx, 3 + jne argcError + + add rsp, 8 + pop rsi + call str_to_int + + mov r10, rax + pop rsi + call str_to_int + mov r11, rax + + add r10, r11 +``` + +여기서 무슨 일이 일어나는지 이해해 봅시다: +_start 레이블 이후 첫 번째 명령어는 스택에서 첫 번째 값을 가져와 rcx 레지스터에 넣습니다. +명령줄 인수와 함께 애플리케이션을 실행하면 실행 후 모든 인수가 다음 순서로 스택에 있게 됩니다: + +``` + [rsp] - 스택의 맨 위에는 인수 개수가 포함됩니다. + [rsp + 8] - argv[0]이 포함됩니다. + [rsp + 16] - argv[1]이 포함됩니다. + 계속 이런 식으로... +``` + +따라서 우리는 명령줄 인수 개수를 가져와 rcx에 넣습니다. +그 후 rcx를 3과 비교합니다. 만약 같지 않다면 argcError 레이블로 점프합니다. +이 레이블은 단순히 오류 메시지를 출력합니다: + +```assembly +argcError: + ;; sys_write 시스템 콜 + mov rax, 1 + ;; 파일 디스크립터, 표준 출력 + mov rdi, 1 + ;; 메시지 주소 + mov rsi, WRONG_ARGC + ;; 메시지 길이 + mov rdx, 34 + ;; write 시스템 콜 호출 + syscall + ;; 프로그램 종료 + jmp exit +``` + +여기서 우리는 명령줄 인수의 합을 rax 레지스터에 넣고, r12를 0으로 설정한 후 int_to_str로 점프합니다. +이제 우리 프로그램의 기본 구조가 완성되었습니다. +우리는 이미 문자열을 출력하는 방법을 알고 있고 출력할 내용도 있습니다. +str_to_int와 int_to_str의 구현을 살펴보겠습니다. + +```assembly +str_to_int: + xor rax, rax + mov rcx, 10 +next: + cmp [rsi], byte 0 + je return_str + mov bl, [rsi] + sub bl, 48 + mul rcx + add rax, rbx + inc rsi + jmp next + +return_str: + ret +``` + +str_to_int의 시작에서 우리는 rax를 0으로, rcx를 10으로 설정합니다. +그 다음 next 레이블로 갑니다. 위의 예시에서 볼 수 있듯이(str_to_int의 첫 번째 호출 전 첫 줄) 우리는 스택에서 argv[1]을 rsi에 넣습니다. +이제 rsi의 첫 번째 바이트를 0과 비교합니다. 모든 문자열은 NULL 심볼로 끝나기 때문이며, 만약 0이라면 반환합니다. +0이 아니라면 그 값을 1바이트 bl 레지스터에 복사하고 48을 뺍니다. 왜 48인가요? 0부터 9까지의 모든 숫자는 ASCII 테이블에서 48부터 57까지의 코드를 가집니다. +따라서 숫자 심볼에서 48을 빼면(예를 들어 57에서) 우리는 숫자를 얻게 됩니다. 그 다음 rax를 rcx(값은 10)와 곱합니다. 이후 rsi를 증가시켜 다음 바이트를 얻고 다시 루프합니다. +알고리즘은 간단합니다. 예를 들어 rsi가 '5' '7' '6' '\000' 시퀀스를 가리킨다면 다음과 같은 단계를 거칩니다: + +``` + rax = 0 + 첫 번째 바이트 5를 가져와 rbx에 넣음 + rax * 10 --> rax = 0 * 10 + rax = rax + rbx = 0 + 5 + 두 번째 바이트 7을 가져와 rbx에 넣음 + rax * 10 --> rax = 5 * 10 = 50 + rax = rax + rbx = 50 + 7 = 57 + rsi가 \000이 될 때까지 반복 +``` + +str_to_int 후에는 rax에 숫자가 들어있게 됩니다. 이제 int_to_str을 살펴보겠습니다: + +```assembly +int_to_str: + mov rdx, 0 + mov rbx, 10 + div rbx + add rdx, 48 + add rdx, 0x0 + push rdx + inc r12 + cmp rax, 0x0 + jne int_to_str + jmp print +``` + +여기서 우리는 rdx에 0을, rbx에 10을 넣습니다. +그 다음 div rbx를 실행합니다. str_to_int 호출 전의 코드를 보면, rax에는 두 명령줄 인수의 합인 정수가 들어있습니다. +이 명령어로 rax 값을 rbx 값으로 나누고 나머지를 rdx에, 몫을 rax에 얻습니다. +다음으로 rdx에 48과 0x0을 더합니다. 48을 더하면 이 숫자의 ASCII 심볼을 얻게 되고, 모든 문자열은 0x0으로 끝나야 합니다. +이후 심볼을 스택에 저장하고, r12를 증가시킵니다(첫 반복에서는 0입니다, _start에서 0으로 설정했습니다). 그리고 rax를 0과 비교합니다. +0이면 정수를 문자열로 변환이 끝났다는 뜻입니다. 알고리즘을 단계별로 살펴보겠습니다. 예를 들어 숫자 23이 있다면: + +``` + 123 / 10. rax = 12; rdx = 3 + rdx + 48 = "3" + "3"을 스택에 push + rax를 0과 비교, 아니면 다시 반복 + 12 / 10. rax = 1; rdx = 2 + rdx + 48 = "2" + "2"를 스택에 push + rax를 0과 비교, 맞으면 함수 실행을 종료하고 스택에 "2" "3" ... 이 있게 됩니다 +``` + +우리는 정수를 문자열로, 그리고 그 반대로 변환하는 두 가지 유용한 함수 `int_to_str`과 `str_to_int`를 구현했습니다. +이제 우리는 문자열로 변환되어 스택에 저장된 두 정수의 합을 가지고 있습니다. 이제 결과를 출력할 수 있습니다. + +```assembly +print: + ;;;; 숫자 길이 계산 + mov rax, 1 + mul r12 + mov r12, 8 + mul r12 + mov rdx, rax + ;;;; 합 출력 + mov rax, SYS_WRITE + mov rdi, STD_IN + mov rsi, rsp + ;; sys_write 호출 + syscall + jmp exit +``` +우리는 이미 `sys_write` 시스템 콜로 문자열을 출력하는 방법을 알고 있지만, 여기에 한 가지 흥미로운 부분이 있습니다. +우리는 문자열의 길이를 계산해야 합니다. `int_to_str`을 보면, 매 반복마다 r12 레지스터를 증가시키는 것을 볼 수 있습니다. +따라서 r12에는 우리 숫자의 자릿수가 들어있습니다. 우리는 이를 8로 곱해야 합니다(각 심볼을 스택에 push했기 때문에), 그러면 출력해야 할 문자열의 길이가 됩니다. +이후 항상 그랬듯이 rax에 1(sys_write 번호), rdi에 1(stdin), rdx에 문자열 길이, rsi에 스택의 최상단 포인터(문자열의 시작)를 넣습니다. +그리고 프로그램을 종료합니다: + +```assembly +exit: + mov rax, SYS_EXIT + 종료 코드 + mov rdi, EXIT_CODE + syscall +``` + +이것으로 "x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 3]를 마칩니다. + From 4f19a32fa3b32b8a5fa6c9a794f45ca7dd22276d Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 15:18:23 +0900 Subject: [PATCH 10/45] code comment --- ko_doc/post/ko_asm_2.md | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index 8bbb1c3..bc3ea9f 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -158,7 +158,7 @@ JMP label ```assembly _start: ;; .... - ;; do something and jump to .exit label + ;; 작업을 수행하고 .exit 레이블로 이동합니다. ;; .... jmp .exit @@ -179,53 +179,53 @@ _start: ```assembly section .data - ; Define constants + ; 상수 정의 num1: equ 100 num2: equ 50 - ; initialize message + ; 메시지 초기화 msg: db "Sum is correct\n" section .text global _start -;; entry point +;; 진입점 _start: - ; set num1's value to rax + ; num1의 값을 rax에 설정 mov rax, num1 - ; set num2's value to rbx + ; num2의 값을 rbx에 설정 mov rbx, num2 - ; get sum of rax and rbx, and store it's value in rax + ; rax와 rbx의 합을 계산하고, 그 값을 rax에 저장 add rax, rbx - ; compare rax and 150 + ; rax와 150 비교 cmp rax, 150 - ; go to .exit label if rax and 150 are not equal + ; rax와 150이 같지 않으면 .exit 레이블로 이동 jne .exit - ; go to .rightSum label if rax and 150 are equal + ; rax와 150이 같으면 .rightSum 레이블로 이동 jmp .rightSum -; Print message that sum is correct +; 합이 맞다는 메시지 출력 .rightSum: - ;; write syscall + ;; write 시스템 호출 mov rax, 1 - ;; file descritor, standard output + ;; 파일 디스크립터, 표준 출력 mov rdi, 1 - ;; message address + ;; 메시지 주소 mov rsi, msg - ;; length of message + ;; 메시지 길이 mov rdx, 15 - ;; call write syscall + ;; write 시스템 호출 호출 syscall - ; exit from program + ; 프로그램 종료 jmp .exit -; exit procedure +; 종료 절차 .exit: - ; exit syscall + ; exit 시스템 호출 mov rax, 60 - ; exit code + ; 종료 코드 mov rdi, 0 - ; call exit syscall + ; exit 시스템 호출 호출 syscall ``` From 3330eba62bb014d5246f5686feaef57fa1758527 Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 15:36:04 +0900 Subject: [PATCH 11/45] part 4 --- ko_doc/post/ko_asm_4.md | 229 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 ko_doc/post/ko_asm_4.md diff --git a/ko_doc/post/ko_asm_4.md b/ko_doc/post/ko_asm_4.md new file mode 100644 index 0000000..83bb308 --- /dev/null +++ b/ko_doc/post/ko_asm_4.md @@ -0,0 +1,229 @@ + +얼마 전 x86_64 어셈블리 프로그래밍에 관한 블로그 포스트 시리즈를 작성하기 시작했습니다. 관련 포스트는 `asm` 태그로 찾을 수 있습니다. +최근에는 바빠서 새로운 포스트가 없었지만, 오늘부터 다시 어셈블리에 대한 포스트를 작성할 예정이며, 매주 포스트를 시도할 것입니다. + +오늘은 문자열과 문자열 작업에 대해 살펴보겠습니다. 여전히 NASM 어셈블러와 Linux x86_64를 사용할 것입니다. + +## 문자열 뒤집기 + +어셈블리 프로그래밍 언어에 대해 이야기할 때 문자열 데이터 타입에 대해 논의할 수 없습니다. +실제로 우리는 바이트 배열을 다루고 있습니다. 간단한 예제를 작성해 보겠습니다. + +문자열 데이터를 정의하고 이를 뒤집어 결과를 표준 출력에 작성하는 작업을 시도할 것입니다. +이 작업은 새로운 프로그래밍 언어를 배우기 시작할 때 일반적으로 접하게 되는 간단하고 인기 있는 과제입니다. +구현을 살펴보겠습니다. + +우선, 초기화된 데이터를 정의합니다. 이는 데이터 섹션에 배치될 것입니다 (섹션에 대한 내용은 이전 포스트에서 읽어보세요): + +```assembly +section .data + SYS_WRITE equ 1 + STD_OUT equ 1 + SYS_EXIT equ 60 + EXIT_CODE equ 0 + + NEW_LINE db 0xa + INPUT db "Hello world!" +``` + +여기서 네 가지 상수를 볼 수 있습니다: + +* `SYS_WRITE` - 'write' 시스템 호출 번호 +* `STD_OUT` - 표준 출력 파일 디스크립터 +* `SYS_EXIT` - 'exit' 시스템 호출 번호 +* `EXIT_CODE` - 종료 코드 + +시스템 호출 목록은 여기에서 확인할 수 있습니다. 또한 다음이 정의되어 있습니다: + +* `NEW_LINE` - 새 줄 (\n) 기호 +* `INPUT` - 우리가 뒤집을 입력 문자열 + +다음으로, 문자열을 뒤집어 넣을 버퍼를 위한 bss 섹션을 정의합니다: + +```assembly +section .bss + OUTPUT resb 12 +``` + +좋아요 이제 데이터와 결과를 넣을 버퍼를 준비했으므로, 코드를 위한 text 섹션을 정의할 수 있습니다. + +_start 루틴부터 시작해 보겠습니다: + +```assembly +_start: + mov rsi, INPUT + xor rcx, rcx + cld + mov rdi, $ + 15 + call calculateStrLength + xor rax, rax + xor rdi, rdi + jmp reverseStr +``` + +여기서 새로운 부분이 있습니다. 어떻게 작동하는지 살펴보겠습니다: +우선, 2행에서 INPUT 주소를 rsi 레지스터에 넣습니다. 표준 출력으로 쓰는 것과 마찬가지로, rcx 레지스터를 0으로 초기화합니다. + +rcx는 문자열의 길이를 계산하는 카운터 역할을 합니다. 4행에서 cld 명령어를 볼 수 있습니다. 이 명령어는 df 플래그를 0으로 설정합니다. +이는 문자열의 길이를 계산할 때 문자열의 기호를 왼쪽에서 오른쪽으로 처리하기 위해 필요합니다. 다음으로 calculateStrLength 함수를 호출합니다. + +5행의 mov rdi, $ + 15 명령어를 누락했는데, +이에 대해 조금 이따 설명하겠습니다. 이제 calculateStrLength 구현을 살펴보겠습니다: + +```assembly +calculateStrLength: + ;; 문자열의 끝인지 확인 + cmp byte [rsi], 0 + ;; 문자열의 끝이라면 함수 종료 + je exitFromRoutine + ;; rsi에서 바이트를 al로 로드하고 rsi를 증가 + lodsb + ;; 심볼을 스택에 푸시 + push rax + ;; 카운터 증가 + inc rcx + ;; 다시 루프 + jmp calculateStrLength +``` + +이제 calculateStrLength 함수에서 문자열을 스택에 푸시한 후, 어떻게 _start로 돌아가는지 살펴보겠습니다. +이 작업을 수행하려면 ret 명령어를 사용할 수 있습니다. +그러나 다음과 같은 코드가 있을 경우 문제가 발생할 수 있습니다: + +```assembly +exitFromRoutine: + ;; return to _start + ret +``` + +이 코드가 작동하지 않는 이유는 무엇일까요? 이는 조금 복잡합니다. +함수가 호출될 때, 함수의 매개변수는 오른쪽에서 왼쪽으로 스택에 푸시됩니다. + +그런 다음, 반환 주소가 스택에 푸시되어 함수가 끝난 후 어디로 돌아가야 하는지 알 수 있습니다. +그러나 calculateStrLength 함수에서 문자열의 각 문자를 스택에 푸시하였고, 이제 스택의 최상단에 반환 주소가 없기 때문에 함수가 어디로 돌아가야 하는지 알 수 없습니다. + +```assembly + mov rdi, $ + 15 +``` + +이 문제를 해결하기 위해, 코드에서 다음과 같은 명령어를 살펴보겠습니다: + +여기서: + +* `$` - 현재 위치를 반환합니다. +* `$$` - 현재 섹션의 시작 위치를 반환합니다. + +mov rdi, $ + 15는 현재 위치에서 15바이트를 더한 위치를 반환합니다. +이는 calculateStrLength 호출 이후에 실행될 코드의 주소를 저장하기 위함입니다. + +구체적으로, mov rdi, $ + 15 명령어는 calculateStrLength 호출 이후의 주소를 rdi 레지스터에 저장합니다. + +이제 objdump 유틸리티를 사용하여 reverse 파일을 열어보겠습니다: + +```assembly +objdump -D reverse + +reverse: file format elf64-x86-64 + +Disassembly of section .text: + +00000000004000b0 <_start>: + 4000b0: 48 be 41 01 60 00 00 movabs $0x600141,%rsi + 4000b7: 00 00 00 + 4000ba: 48 31 c9 xor %rcx,%rcx + 4000bd: fc cld + 4000be: 48 bf cd 00 40 00 00 movabs $0x4000cd,%rdi + 4000c5: 00 00 00 + 4000c8: e8 08 00 00 00 callq 4000d5 + 4000cd: 48 31 c0 xor %rax,%rax + 4000d0: 48 31 ff xor %rdi,%rdi + 4000d3: eb 0e jmp 4000e3 +``` + +우리는 이제 반환 주소가 스택에 올바르게 푸시되고 함수가 반환되는 방법을 이해했습니다. +이렇게 하면 _start로 정확하게 돌아올 수 있습니다. calculateStrLength 함수 호출 후, rax와 rdi를 0으로 초기화하고 reverseStr 레이블로 점프합니다. + +이제 reverseStr 레이블의 구현을 살펴보겠습니다: + +```assembly +exitFromRoutine: + ;; 반환 주소를 스택에 다시 푸시 + push rdi + ;; _start로 반환 + ret +``` + +이제 우리는 _start로 돌아옵니다. calculateStrLength를 호출한 후, rax와 rdi에 0을 저장하고 reverseStr 레이블로 점프합니다. + +reverseStr의 구현은 다음과 같습니다: + +```assembly +reverseStr: + cmp rcx, 0 + je printResult + pop rax + mov [OUTPUT + rdi], rax + dec rcx + inc rdi + jmp reverseStr +``` + +여기서는 문자열의 길이를 나타내는 카운터를 확인하고, 카운터가 0이 되면 모든 기호를 버퍼에 썼으므로 이를 출력할 수 있습니다. +카운터를 확인한 후, 스택에서 rax 레지스터로 첫 번째 기호를 팝하여 OUTPUT 버퍼에 씁니다. + +rdi를 추가하여 기호가 버퍼의 첫 번째 바이트에 쓰이지 않도록 합니다. 그 후 rdi를 증가시켜 OUTPUT 버퍼의 다음 위치로 이동하고, +길이 카운터를 감소시킨 후 레이블의 시작으로 점프합니다. + +reverseStr가 실행된 후에는 OUTPUT 버퍼에 문자열이 역순으로 저장되어 있으며, 새 줄을 추가하여 결과를 stdout에 쓸 수 있습니다. + +```assembly +printResult: + mov rdx, rdi + mov rax, 1 + mov rdi, 1 + mov rsi, OUTPUT + syscall + jmp printNewLine + +printNewLine: + mov rax, SYS_WRITE + mov rdi, STD_OUT + mov rsi, NEW_LINE + mov rdx, 1 + syscall + jmp exit +``` + +그리고 프로그램을 종료합니다: + +```assembly +exit: + mov rax, SYS_EXIT + mov rdi, EXIT_CODE + syscall +``` + +이제 모든 것이 완료되었습니다. 프로그램을 다음 명령어로 컴파일할 수 있습니다: + +```assembly +all: + nasm -g -f elf64 -o reverse.o reverse.asm + ld -o reverse reverse.o + +clean: + rm reverse reverse.o +``` + +실행 결과: + +![result](/content/assets/result_asm_4.png) + +## 문자열 작업 + +물론 문자열 및 바이트 조작을 위한 많은 다른 명령어가 있습니다: + +* `REP` - RCX가 0이 아닌 동안 반복합니다. +* `MOVSB` - 바이트 문자열을 복사합니다 (MOVSW, MOVSD 등도 사용 가능합니다..) +* `CMPSB` - 바이트 문자열 비교 +* `SCASB` - 바이트 문자열 스캔 +* `STOSB` - 문자열에 바이트를 기록합니다 From 81ab65b451c470a4013c9261304e44d998417542 Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 15:38:54 +0900 Subject: [PATCH 12/45] sy --- ko_doc/post/ko_asm_1.md | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/ko_doc/post/ko_asm_1.md b/ko_doc/post/ko_asm_1.md index f1162bc..35b6fbe 100644 --- a/ko_doc/post/ko_asm_1.md +++ b/ko_doc/post/ko_asm_1.md @@ -1,6 +1,8 @@ ## 소개 -우리 사이에는 많은 개발자들이 있습니다. 우리는 매일 엄청난 양의 코드를 작성합니다. 때때로 나쁘지 않은 코드도 작성하죠 :) 우리 모두는 다음과 같은 가장 간단한 코드를 쉽게 작성할 수 있습니다: +우리 사이에는 많은 개발자들이 있습니다. 우리는 매일 엄청난 양의 코드를 작성합니다. + +때때로 나쁘지 않은 코드도 작성하죠 :) 우리 모두는 다음과 같은 가장 간단한 코드를 쉽게 작성할 수 있습니다: ```c #include @@ -13,16 +15,24 @@ int main() { } ``` -우리 모두 이 C 코드가 무엇을 하는지 이해할 수 있습니다. 하지만... 이 코드가 저수준에서 어떻게 작동하는지 아는 사람은 많지 않을 것입니다. -저도 그랬습니다. Haskell, Erlang, Go와 같은 고수준 프로그래밍 언어로 코드를 작성할 수는 있었지만, -이 코드가 컴파일된 후 저수준에서 어떻게 동작하는지 전혀 몰랐습니다. 그래서 어셈블리 언어로 몇 가지 깊은 단계를 밟아가면서 배운 내용을 설명하기로 했습니다. -이 과정이 저뿐만 아니라 여러분에게도 흥미로울 거라 생각합니다. 약 5~6년 전, 대학교에서 간단한 프로그램을 작성하기 위해 어셈블리를 사용한 적이 있습니다. +우리 모두 이 C 코드가 무엇을 하는지 이해할 수 있습니다. +하지만... 이 코드가 저수준에서 어떻게 작동하는지 아는 사람은 많지 않을 것입니다. +저도 그랬습니다. + +Haskell, Erlang, Go와 같은 고수준 프로그래밍 언어로 코드를 작성할 수는 있었지만, +이 코드가 컴파일된 후 저수준에서 어떻게 동작하는지 전혀 몰랐습니다. + +그래서 어셈블리 언어로 몇 가지 깊은 단계를 밟아가면서 배운 내용을 설명하기로 했습니다. +이 과정이 저뿐만 아니라 여러분에게도 흥미로울 거라 생각합니다. + +약 5~6년 전, 대학교에서 간단한 프로그램을 작성하기 위해 어셈블리를 사용한 적이 있습니다. 당시에는 Turbo Assembly와 DOS 운영 체제를 사용했습니다. 지금은 Linux-x86-64 운영 체제를 사용하고 있습니다. 그렇다면 Linux 64비트와 DOS 16비트 사이에는 큰 차이가 있을 것입니다. 시작해봅시다. ## 준비 시작하기 전에 몇 가지 준비를 해야 합니다. 앞서 언급했듯이, 저는 Ubuntu (Ubuntu 14.04.1 LTS 64비트)를 사용하고 있으며, 따라서 이 글에서는 이 운영 체제와 아키텍처를 기준으로 설명할 것입니다. + 각 CPU는 서로 다른 명령어 집합을 지원합니다. 저는 Intel Core i7 870 프로세서를 사용하며, 모든 코드는 이 프로세서를 기준으로 작성될 것입니다. 또한 NASM 어셈블리를 사용할 것입니다. NASM을 설치하려면 다음 명령어를 실행하면 됩니다: @@ -32,6 +42,7 @@ $ sudo apt-get install nasm NASM의 버전은 2.0.0 이상이어야 합니다. 저는 2013년 12월 29일에 컴파일된 NASM 2.10.09 버전을 사용하고 있습니다. + 마지막으로 어셈블리 코드를 작성할 텍스트 편집기가 필요합니다. 저는 Emacs와 nasm-mode.el을 사용합니다. 물론, 다른 편집기를 사용해도 상관없습니다. Emacs를 사용한다면, nasm-mode.el을 다운로드하고 Emacs를 다음과 같이 설정할 수 있습니다: @@ -100,6 +111,7 @@ _start: 네, printf("Hello world")처럼 보이지는 않습니다. 이 코드가 어떻게 작동하는지 이해해 봅시다. 1~2행을 살펴보세요. 데이터 섹션을 정의하고, 여기에서 "Hello world" 값을 가지는 msg 상수를 정의했습니다. + 이제 이 상수를 코드에서 사용할 수 있습니다. 다음은 텍스트 섹션과 프로그램의 시작 지점을 선언한 것입니다. 프로그램은 7행부터 실행됩니다. 이제 가장 흥미로운 부분이 시작됩니다. mov 명령어는 두 개의 피연산자를 받아서 두 번째 값을 첫 번째에 넣습니다. 그런데 rax, rdi 등이 무엇인지 궁금할 것입니다. 위키피디아에 따르면: @@ -135,10 +147,21 @@ size_t sys_write(unsigned int fd, const char * buf, size_t count); `sys_write` 시스템 호출이 3개의 인자를 받으며, syscall 테이블에서 1번 번호를 가진다는 것을 알았습니다. 이제 우리의 "Hello world" 구현을 다시 살펴보겠습니다. rax 레지스터에 1을 넣으면 `sys_write` 시스템 호출을 사용할 것이라는 의미입니다. -다음 줄에서는 rdi 레지스터에 1을 넣습니다. 이는 sys_write의 첫 번째 인자인 표준 출력(1)을 의미합니다. 다음으로 msg에 대한 포인터를 rsi 레지스터에 저장합니다. -이는 sys_write의 두 번째 인자인 버퍼를 의미합니다. 그리고 문자열의 길이(13)를 rdx에 전달하여 sys_write의 세 번째 인자로 사용합니다. 이제 `sys_write`의 모든 인자를 설정했으므로, 11행에서 syscall 명령어로 호출합니다. -"Hello world" 문자열을 출력한 후, 프로그램을 올바르게 종료해야 합니다. rax 레지스터에 60을 넣습니다. 60은 종료 시스템 호출 번호입니다. 또한 rdi 레지스터에 0을 넣어 오류 코드를 설정합니다. 0은 성공적인 종료를 의미합니다. -이것으로 "Hello world" 프로그램이 완료됩니다. 꽤 간단하죠 :) 이제 프로그램을 빌드해 보겠습니다. 예를 들어, 이 코드를 hello.asm 파일에 코드를 작성했다고 가정합시다. 그러면 다음 명령어를 실행하여 프로그램을 빌드할 수 있습니다: + +다음 줄에서는 rdi 레지스터에 1을 넣습니다. 이는 sys_write의 첫 번째 인자인 표준 출력(1)을 의미합니다. +다음으로 msg에 대한 포인터를 rsi 레지스터에 저장합니다. + +이는 sys_write의 두 번째 인자인 버퍼를 의미합니다. +그리고 문자열의 길이(13)를 rdx에 전달하여 sys_write의 세 번째 인자로 사용합니다. 이제 `sys_write`의 모든 인자를 설정했으므로, 11행에서 syscall 명령어로 호출합니다. + +"Hello world" 문자열을 출력한 후, 프로그램을 올바르게 종료해야 합니다. +rax 레지스터에 60을 넣습니다. 60은 종료 시스템 호출 번호입니다. + +또한 rdi 레지스터에 0을 넣어 오류 코드를 설정합니다. 0은 성공적인 종료를 의미합니다. +이것으로 "Hello world" 프로그램이 완료됩니다. 꽤 간단하죠 :) 이제 프로그램을 빌드해 보겠습니다. + +예를 들어, 이 코드를 hello.asm 파일에 코드를 작성했다고 가정합시다. +그러면 다음 명령어를 실행하여 프로그램을 빌드할 수 있습니다: ``` $ nasm -f elf64 -o hello.o hello.asm From ff71b6a6e1cddc6a5db90b3e7962b9a1ec07cd3a Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 15:51:34 +0900 Subject: [PATCH 13/45] part 5 --- ko_doc/post/ko_asm_5.md | 149 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 ko_doc/post/ko_asm_5.md diff --git a/ko_doc/post/ko_asm_5.md b/ko_doc/post/ko_asm_5.md new file mode 100644 index 0000000..009dfde --- /dev/null +++ b/ko_doc/post/ko_asm_5.md @@ -0,0 +1,149 @@ + +이 글은 x86_64 어셈블리의 다섯 번째 파트로 매크로를 다룹니다. +이 포스트는 x86_64에 대한 블로그 포스트가 아니라 NASM 어셈블러와 그 전처리기에 대한 것입니다. 관심이 있다면 계속 읽어보세요. + +## 매크로 + +NASM은 두 가지 형태의 매크로를 지원합니다: + +* 단일 라인 매크로 +* 다중 라인 매크로 + +단일 라인 매크로는 %define 지시어로 시작해야 합니다. 형식은 다음과 같습니다: + +```assembly +%define macro_name(parameter) value +``` + +NASM 매크로는 C의 매크로와 비슷하게 작동합니다. 예를 들어, 다음과 같은 단일 라인 매크로를 만들 수 있습니다: + +```assembly +%define argc rsp + 8 +%define cliArg1 rsp + 24 +``` + +그리고 이를 코드에서 사용할 수 있습니다: + +```assembly +;; +;; argc는 rsp + 8로 확장됩니다 +;; +mov rax, [argc] +cmp rax, 3 +jne .mustBe3args +``` + +다중 라인 매크로는 %macro NASM 지시어로 시작하고 %endmacro로 끝납니다. 일반 형식은 다음과 같습니다: + +```assembly +%macro number_of_parameters + instruction + instruction + instruction +%endmacro +``` + +예를 들어: + +```assembly +%macro bootstrap 1 + push ebp + mov ebp,esp +%endmacro +``` + +그리고 이를 다음과 같이 사용할 수 있습니다: + +```assembly +_start: + bootstrap +``` + +다음은 PRINT 매크로의 예입니다: + +```assembly +%macro PRINT 1 + pusha + pushf + jmp %%astr +%%str db %1, 0 +%%strln equ $-%%str +%%astr: _syscall_write %%str, %%strln +popf +popa +%endmacro + +%macro _syscall_write 2 + mov rax, 1 + mov rdi, 1 + mov rsi, %%str + mov rdx, %%strln + syscall +%endmacro +``` + +매크로가 어떻게 작동하는지 살펴보겠습니다: +첫 번째 줄에서는 매개변수가 하나인 PRINT 매크로를 정의합니다. + +그 다음에는 모든 일반 레지스터(pusha 명령어를 사용하여)와 플래그 레지스터(pushf 명령어를 사용하여)를 푸시합니다. +이후 %%astr 레이블로 점프합니다. 매크로 내의 모든 레이블은 %%로 시작해야 합니다. + +이제 _syscall_write 매크로로 이동합니다. 이 매크로는 두 개의 매개변수를 받습니다. +write 시스템 호출을 사용하여 문자열을 stdout으로 출력합니다. _syscall_write 매크로의 구현을 살펴보면: + +```assembly +;; write 시스템 호출 번호 +mov rax, 1 +;; 파일 디스크립터, 표준 출력 +mov rdi, 1 +;; 메시지 주소 +mov rsi, msg +;; 메시지 길이 +mov rdx, 14 +;; 시스템 호출 호출 +syscall +``` + +매크로는 먼저 rax에 1을 설정하여 write 시스템 호출 번호를 지정하고, rdi에 1을 설정하여 표준 출력 파일 디스크립터를 지정합니다. +그런 다음 rsi에 %%str을 설정하여 문자열의 포인터를 지정하고, %%str은 PRINT 매크로의 첫 번째 매개변수로 전달된 문자열입니다 (매크로 매개변수는 $parameter_number로 접근합니다). + +문자열은 0으로 끝나야 합니다. %%strlen은 문자열 길이를 계산합니다. 이후 시스템 호출을 syscall 명령어로 호출합니다. + +이제 다음과 같이 사용할 수 있습니다: + +```assembly +label: PRINT "Hello World!" +``` + +## 유용한 표준 매크로 + +NASM은 다음과 같은 표준 매크로를 지원합니다: + +### STRUC + +`STRUC`와 `ENDSTRUC`를 사용하여 데이터 구조를 정의할 수 있습니다. 예를 들어: + +```assembly +struc person + name: resb 10 + age: resb 1 +endstruc +``` + +그리고 이제 우리의 구조체 인스턴스를 만들 수 있습니다: + +```assembly +section .data + p: istruc person + at name db "name" + at age db 25 + iend + +section .text +_start: + mov rax, [p + person.name] +``` + +### %include + +다른 어셈블리 파일을 포함하고 %include 지시어를 사용하여 레이블로 점프하거나 함수를 호출할 수 있습니다. From c8a1fd64b55012041671af679efdab1769488d50 Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 16:08:22 +0900 Subject: [PATCH 14/45] Create ko_asm_6 --- ko_doc/post/ko_asm_6 | 184 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 ko_doc/post/ko_asm_6 diff --git a/ko_doc/post/ko_asm_6 b/ko_doc/post/ko_asm_6 new file mode 100644 index 0000000..ceadfd1 --- /dev/null +++ b/ko_doc/post/ko_asm_6 @@ -0,0 +1,184 @@ + +이 글은 x86_64 어셈블리의 여섯 번째 파트로 AT&T 어셈블리 문법을 다룹니다. +이전에는 NASM 어셈블러를 사용했지만, AT&T 문법을 사용하는 GNU 어셈블러(gas)와 그 문법의 차이를 살펴보겠습니다. + +GCC는 GNU 어셈블러를 사용하므로, 간단한 "Hello World" 프로그램의 어셈블리 출력은 다음과 같습니다: + + +```C +#include + +int main(void) { + write(1, "Hello World\n", 15); + return 0; +} +``` + +다음과 같은 출력이 나옵니다: + +```assembly + .file "test.c" + .section .rodata +.LC0: + .string "Hello World\n" + .text + .globl main + .type main, @function +main: +.LFB0: + .cfi_startproc + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset 6, -16 + movq %rsp, %rbp + .cfi_def_cfa_register 6 + movl $15, %edx + movl $.LC0, %esi + movl $1, %edi + call write + movl $0, %eax + popq %rbp + .cfi_def_cfa 7, 8 + ret + .cfi_endproc +.LFE0: + .size main, .-main + .ident "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1" + .section .note.GNU-stack,"",@progbits +``` + +위의 출력은 NASM의 "Hello World"와 다르게 보입니다. 차이점을 살펴보겠습니다. + +## AT&T 문법 + +### 섹션 + +```assembly +.data + // + // 초기화된 데이터 정의 + // +.text + .global _start + +_start: + // + // 주요 루틴 + // +``` + +다음 두 가지 차이점이 있습니다: + +* 섹션 정의는 . 기호로 시작합니다. +* 메인 루틴은 global 대신 .globl로 정의합니다. + +GNU 어셈블러(gas)는 데이터 정의를 위해 다음과 같은 지시어를 사용합니다: + +```assembly +.section .data + // 1 바이트 + var1: .byte 10 + // 2 바이트 + var2: .word 10 + // 4 바이트 + var3: .int 10 + // 8 바이트 + var4: .quad 10 + // 16 바이트 + var5: .octa 10 + + // 자동 종료 바이트 없이 연속적인 주소로 문자열을 조립 + str1: .asci "Hello world" + // 자동 종료 바이트가 있는 문자열 + str2: .asciz "Hello world" + // 문자열을 오브젝트 파일에 복사 + str3: .string "Hello world" +``` + +피연산자 순서 +NASM에서 데이터 조작을 위한 일반 문법은 다음과 같습니다: + +```assembly +mov destination, source +``` + +GNU 어셈블러에서는 피연산자 순서가 반대입니다: + +```assembly +mov source, destination +``` + +예를 들어: + +```assembly +;; +;; nasm 문법 +;; +mov rax, rcx + +// +// GNU(gas) 문법 +// +mov %rcx, %rax +``` + +또한, GNU 어셈블러에서는 레지스터가 % 기호로 시작합니다. 직접 오퍼랜드를 사용할 때는 `$` 기호를 사용해야 합니다: + +```assembly +movb $10, %rax +``` + +## 피연산자 크기 및 연산 문법 + +메모리의 일부, 예를 들어 64비트 레지스터의 첫 바이트를 가져와야 할 때, NASM에서는 다음과 같은 문법을 사용합니다: + +```assembly +mov ax, word [rsi] +``` + +GNU 어셈블러에서는 피연산자의 크기를 명시하지 않고, 명령어에서 직접 정의합니다: + +```assembly +movw (%rsi), %ax +``` + +GNU 어셈블러는 연산을 위한 6가지 접미사를 지원합니다: + +* `b` - 1 바이트 피연산자 +* `w` - 2 바이트 피연산자 +* `l` - 4 바이트 피연산자 +* `q` - 8 바이트 피연산자 +* `t` - 10 바이트 피연산자 +* `o` - 16 바이트 피연산자 + +이 규칙은 mov 명령어뿐만 아니라 addl, xorb, cmpw 등 다른 명령어에도 적용됩니다. + +## 메모리 접근 + +이전 예제에서 () 괄호 대신 []를 사용했음을 주목할 수 있습니다. +괄호 안의 값을 역참조할 때는 GAS에서는 다음과 같이 사용합니다: (%rax). 예를 들어: + +```assembly +movq -8(%rbp),%rdi +movq 8(%rbp),%rdi +``` + +## 점프 + +GNU 어셈블러는 다음과 같은 연산자를 지원합니다: + +```assembly +lcall $section, $offset +``` + +Far jump는 현재 코드 세그먼트와 다른 세그먼트에 있는 명령어로 점프하는 것입니다. 때로는 인터세그먼트 점프라고도 합니다. + +## 주석 + +GNU 어셈블러는 다음과 같은 3가지 유형의 주석을 지원합니다: + +``` + # - 한 줄 주석 + // - 한 줄 주석 + /* */ - 여러 줄 주석 +``` From 887ac74de08fe6763db2506637e4462527ab7c66 Mon Sep 17 00:00:00 2001 From: Maldron Date: Fri, 16 Aug 2024 16:08:36 +0900 Subject: [PATCH 15/45] Rename ko_asm_6 to ko_asm_6.md --- ko_doc/post/{ko_asm_6 => ko_asm_6.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ko_doc/post/{ko_asm_6 => ko_asm_6.md} (100%) diff --git a/ko_doc/post/ko_asm_6 b/ko_doc/post/ko_asm_6.md similarity index 100% rename from ko_doc/post/ko_asm_6 rename to ko_doc/post/ko_asm_6.md From b17cfa6e6f6ab8a9ae66669dffc2733a6eaa31b1 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 10:05:20 +0900 Subject: [PATCH 16/45] Create ko SECURITY --- ko_doc/SECURITY.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ko_doc/SECURITY.md diff --git a/ko_doc/SECURITY.md b/ko_doc/SECURITY.md new file mode 100644 index 0000000..3661a0f --- /dev/null +++ b/ko_doc/SECURITY.md @@ -0,0 +1,6 @@ +## 보안 정보 + +이 저장소는 주로 [C](https://ko.wikipedia.org/wiki/C_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%EC%96%B8%EC%96%B4))와 [어셈블리](https://ko.wikipedia.org/wiki/%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC%EC%96%B4) 프로그래밍 언어를 사용하여 작성된 코드 예제를 포함하고 있습니다. + +이 코드 예제들은 어떠한 악성 소프트웨어도 포함하고 있지 않으며 로컬 머신에서 안전하게 실행할 수 있습니다. +만약 코드 예제들 중 어느 곳에서든 보안 문제가 있다고 생각되면, [이슈](https://github.com/0xAX/asm/issues/new)를 열어 주시기 바랍니다. From c7e426d6de18daec18b61d5f9a2ab71eda85ef03 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 10:09:46 +0900 Subject: [PATCH 17/45] Create ko CODE_OF_CONDUCT --- ko_doc/CODE_OF_CONDUCT.md | 124 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 ko_doc/CODE_OF_CONDUCT.md diff --git a/ko_doc/CODE_OF_CONDUCT.md b/ko_doc/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..e3299cd --- /dev/null +++ b/ko_doc/CODE_OF_CONDUCT.md @@ -0,0 +1,124 @@ +# 기여자 행동 강령 규약 + +## 서약 + +우리는 구성원, 기여자 및 리더로서 커뮤니티에 참여하여 +연령, 신체 크기, 눈에 보이거나 보이지 않는 장애, 민족성, 성별, 성 정체성과 표현, +경력, 학력, 사회 경제적 지위, 국적, 외모, 인종, 카스트 제도, 피부색, 종교 +또는 성적 정체성과 성적 성향에 관계없이 모든 사람을 차별하지 않을 것을 서약한다. + +우리는 개방적이고 친근하며 다양하고 포용적이며 건강한 커뮤니티에 기여하는 +방식으로 행동하고 상호작용할 것을 서약한다. + +## 표준 + +커뮤니티의 긍정적인 환경을 위해 기여자가 해야 할 행동은 다음과 같다: + +* 다른 사람들에 대한 친절과 공감 표현 +* 서로 다른 의견 및 관점, 경험에 대한 존중 +* 건설적인 피드백을 제공 및 열린 마음으로 수락 +* 책임을 받아들이고 실수로 인해 영향을 받은 사람들에게 사과하며 + 경험을 통해 배움 +* 개인뿐만 아닌 전체 커뮤니티를 위한 최선의 방법에 집중 + + +하지말아야 할 행동은 다음과 같다: + +* 성적인 언어와 이미지 사용, 성적 관심이나 어떤 종류의 접근 +* 소모적인 논쟁, 모욕적 또는 비하하는 댓글과 개인적 또는 정치적인 공격 +* 공개적이거나 개인적인 괴롭힘 +* 동의없는 집주소 또는 이메일 주소 등의 개인 정보의 공개 +* 부적절한 것으로 간주될 수 있는 다른 행위 + +## 집행 책임 + +커뮤니티 리더는 허용되는 행동의 기준을 명확히 하고 집행할 책임이 있으며 +부적절하다고 여겨지는 모든 행동, 위협, 공격 또는 피해에 대해 적절하고 +공정한 행동을 취한다. + +프로젝트 유지자는 이 행동 강령을 따르지 않은 댓글, 커밋, 코드, 위키 편집, +이슈와 그 외 다른 기여를 삭제, 수정 또는 거부할 권리와 책임이 있다. 또한, +부적당하거나 험악하거나 공격적이거나 해롭다고 생각하는 다른 행동을 한 기여자를 +일시적 또는 영구적으로 퇴장시킬 수 있다. + +커뮤니티 리더는 이 행동 강령을 따르지 않는 댓글, 커밋, 코드, 위키 편집, +이슈와 그 외 다른 기여를 삭제, 수정 또는 거부할 권리와 책임이 있으며, +적절한 경우 중재적 의사결정에 대한 이유를 전달할 것이다. + +## 범위 + +이 행동 강령은 개인이 공개 영역에서 커뮤니티를 공식적으로 대표할 때를 +포함하여 모든 커뮤니티 영역에 적용된다. +커뮤니티 대표의 예로 공식 이메일 주소 사용, 공식 소셜 미디어 계정을 통한 게시, +온/오프라인 이벤트에서 임명된 대표자의 활동이 있다. + +## 집행 + +모욕적이거나 괴롭힘 또는 그 외 하지말아야 할 행동을 발견하면 +[연락 방법 입력]을 통해 집행 책임이 있는 커뮤니티 리더에게 보고한다. +모든 불만사항은 신속하고 공정하게 검토되고 조사될 것이다. + +커뮤니티 리더는 사건의 보고자의 사생활과 안전을 존중할 의무가 있다. + +## 집행 지침 + +커뮤니티 리더는 행동 강령 위반으로 간주되는 행동에 대한 결과를 결정할 때, +다음의 커뮤니티 영향 지침을 준수한다: + +### 1. 정정 + +**커뮤니티 영향**: 커뮤니티 내 부적절한 언어 사용이나 +비전문적인 행동 또는 불쾌함을 주는 행동. + +**결과**: 커뮤니티 리더가 별도로 위반에 대한 명확성과 부적절함에 대한 +이유를 설명하고 서면 경고. +공개 사과를 요청할 수 있다. + +### 2. 경고 + +**커뮤니티 영향**: 단일 사고 또는 연속된 행동 위반. + +**결과**: 지속적인 행동에 대한 결과에 대해 경고. +특정 기간동안 행동 강령을 시행하는 사람들과의 원치 않는 상호작용을 포함한 +관련된 사람들과의 상호작용 금지. 소셜 미디어와 같은 외부 채널뿐만 아닌 +커뮤니티 공간에서의 상호작용도 금지된다. +이 조항을 위반하면 일시적 혹은 영구적으로 제재로 이어질 수 있다. + +### 3. 일시적인 제재 + +**커뮤니티 영향**: 지속적으로 부적절한 행동을 포함한 +심각한 커뮤니티 기준 위반. + +**결과**: 특정 기간동안 커뮤니티와의 어떠한 종류의 상호작용이나 +공개적 소통이 일시적 제재. +이 기간동안 행동 강령을 시행하는 사람들과의 원치 않는 상호작용을 포함한 +관련된 사람들과의 상호작용 금지. 소셜 미디어와 같은 외부 채널뿐만 아닌 +커뮤니티 공간에서의 상호작용도 금지된다. +이 조항을 위반하면 일시적 혹은 영구적으로 제재로 이어질 수 있다. + +### 4. 영구 제재 + +**커뮤니티 영향**: 지속적인 부적절한 행동, 개인적인 괴롭힘 또는 +개인의 계급에 대한 공격이나 폄하를 포함한 커뮤니티 표준 위반 패턴을 보임. + +**결과**: 커뮤니티와의 모든 종류의 공개적 교류를 영구적으로 제재. + +## 참고 + +이 행동 강령은 [기여자 규약][homepage] 의 2.1 버전을 변형하였습니다. 그 내용은 +[https://www.contributor-covenant.org/ko/version/2/1/code-of-conduct.html][v2.1] +에서 확인할 수 있습니다. + +커뮤니티 영향 지침은 [Mozilla's code of conduct enforcement ladder][Mozilla CoC] +에서 영감을 얻었습니다. + +이 행동 강령에 관련한 일반적인 질문에 대한 대답은 +[https://www.contributor-covenant.org/faq][FAQ]를 참고할 수 있습니다. +번역본은 [https://www.contributor-covenant.org/translations][translations]에서 +볼 수 있습니다. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations From 30862c2d74032b5c9726daa3bafd99877297b1bb Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 10:12:50 +0900 Subject: [PATCH 18/45] Create CONTIBUTING.md --- ko_doc/post/CONTIBUTING.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 ko_doc/post/CONTIBUTING.md diff --git a/ko_doc/post/CONTIBUTING.md b/ko_doc/post/CONTIBUTING.md new file mode 100644 index 0000000..ec3cfb0 --- /dev/null +++ b/ko_doc/post/CONTIBUTING.md @@ -0,0 +1,33 @@ +# 기여하기 +이 문서는 이슈 열기, 풀 리퀘스트(PR) 생성, 리뷰, PR 병합에 이르는 기여 워크플로우를 설명합니다. 이 프로젝트에서 작업할 때는 [행동 강령](./CODE_OF_CONDUCT.md)을 따르시기 바랍니다. +기여해 주셔서 감사합니다. + +## 기여 입문 가이드 +오픈 소스에 처음 기여하시는 분이라면, 첫 기여를 하기 전에 다음 자료들이 도움이 될 수 있습니다: +- [GitHub에서 오픈 소스에 기여할 방법 찾기](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) +- [Git 설정하기](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) +- [GitHub 흐름](https://docs.github.com/en/get-started/using-github/github-flow) +- [풀 리퀘스트로 협업하기](https://docs.github.com/en/github/collaborating-with-pull-requests) + +**첫 풀 리퀘스트를 작업하시나요?** [GitHub에서 오픈 소스 프로젝트에 기여하는 방법](https://kcd.im/pull-request) 무료 시리즈에서 배울 수 있습니다. + +## 이슈 생성하기 +개선 아이디어가 있거나, 누락된 기능 또는 버그를 발견하셨다면, GitHub에서 **Issues -> New issue**를 클릭하여 GitHub 이슈를 생성해주세요. 이슈 템플릿에 버그나 제안된 개선 사항에 대한 자세한 설명을 반드시 작성해 주세요. 필요한 경우 적절한 논거와 스크린샷을 제공해 주세요. +작업할 기존 이슈를 찾으셨다면, 수정사항을 담은 PR을 열어주시기 바랍니다. + +## 풀 리퀘스트 열기 +프로젝트에 직접 기여하고 싶으시다면, 제안된 변경 사항을 담은 풀 리퀘스트를 생성해주세요. 방법은 다음과 같습니다: +1. [저장소를 포크합니다](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#fork-an-example-repository). +2. 포크한 저장소의 로컬 복사본에서 변경 사항을 만듭니다. +3. 변경 사항을 커밋하고 GitHub에 푸시합니다. + +> [!IMPORTANT] +> 포크를 업데이트하는 것을 잊지 마세요. 많은 기여자들이 `master` 브랜치를 기반으로 동일한 내용을 작업할 수 있기 때문에 일부 병합 충돌이 발생할 수 있습니다. 변경 사항을 푸시하기 전에 항상 `master`와 리베이스하는 것을 잊지 말고, 브랜치가 `master`와 충돌이 없는지 확인하세요. 병합 충돌이 발생하면 [병합 충돌 해결하기](https://github.com/skills/resolve-merge-conflicts) 튜토리얼을 읽고 병합 충돌 및 기타 문제를 해결하는 방법을 배우세요. + +4. GitHub에서 풀 리퀘스트를 엽니다. 풀 리퀘스트 템플릿에 제공된 변경 사항에 대한 이유와 설명을 작성하세요. 해당되는 경우 풀 리퀘스트를 기존 이슈와 연결하세요. PR을 제출한 후 프로젝트 관리자의 리뷰를 기다리세요. + +## 리뷰 및 승인 과정 +PR을 제출한 후 리뷰를 기다리세요. 프로젝트 관리자들이 귀하의 변경 사항을 평가하고 [제안된 변경 사항](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request)이나 풀 리퀘스트 댓글을 통해 피드백을 제공할 것입니다. 리뷰 제안 사항과 댓글에 최대한 빨리 대응해 주세요. PR이 좋아 보이면 관리자들이 승인하고 병합합니다. + +## 기여자 +모든 기여는 [Contributors](CONTRIBUTORS.md)에서 인정받습니다. 거기에 자신을 추가하는 것을 잊지 마세요. From 7ca2405aa44ff64a4885ff36f22219d3c90b9d37 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 10:23:33 +0900 Subject: [PATCH 19/45] add ko_asm part 7 --- ko_doc/post/ko_asm_7.md | 168 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 ko_doc/post/ko_asm_7.md diff --git a/ko_doc/post/ko_asm_7.md b/ko_doc/post/ko_asm_7.md new file mode 100644 index 0000000..3af98fc --- /dev/null +++ b/ko_doc/post/ko_asm_7.md @@ -0,0 +1,168 @@ + +x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 7] 입니다. 이번에는 C와 어셈블러를 함께 사용하는 방법을 살펴보겠습니다. + +실제로 우리는 이를 함께 사용할 수 있는 3가지 방법이 있습니다: + +* C 코드에서 어셈블리 루틴 호출하기 +* 어셈블리 코드에서 C 루틴 호출하기 +* C 코드에서 인라인 어셈블리 사용하기 + +어셈블리와 C를 함께 사용하는 방법을 보여주는 3개의 간단한 Hello world 프로그램을 작성해 봅시다. + +## C에서 어셈블리 호출하기 + +먼저 다음과 같은 간단한 C 프로그램을 작성해 봅시다: + +```C +#include + +int main() { + char* str = "Hello World\n"; + int len = strlen(str); + printHelloWorld(str, len); + return 0; +} +``` + +여기서 우리는 두 개의 변수를 정의하는 C 코드를 볼 수 있습니다 +stdout에 쓸 Hello world 문자열과 이 문자열의 길이입니다. 다음으로 이 2개의 변수를 매개변수로 하여 printHelloWorld 어셈블리 함수를 호출합니다. + +우리는 x86_64 Linux를 사용하므로, x86_64 linux 호출 규약을 알아야 합니다. +그래야 printHelloWorld 함수를 어떻게 작성하고, 들어오는 매개변수를 어떻게 가져오는지 등을 알 수 있습니다... + +함수를 호출할 때 처음 여섯 개의 매개변수는 rdi, rsi, rdx, rcx, r8, r9 범용 레지스터를 통해 전달되며, 그 외의 모든 매개변수는 스택을 통해 전달됩니다. +따라서 우리는 첫 번째와 두 번째 매개변수를 rdi와 rsi 레지스터에서 가져와 write 시스템 콜을 호출한 다음 ret 명령어로 함수에서 반환할 수 있습니다: + +```assembly +global printHelloWorld + +section .text +printHelloWorld: + ;; 1 arg + mov r10, rdi + ;; 2 arg + mov r11, rsi + ;; call write syscall + mov rax, 1 + mov rdi, 1 + mov rsi, r10 + mov rdx, r11 + syscall + ret +``` + +이제 다음과 같이 빌드할 수 있습니다: + +``` +build: + nasm -f elf64 -o casm.o casm.asm + gcc casm.o casm.c -o casm +``` + +## 인라인 어셈블리 +다음 방법은 C 코드 직접 어셈블리 코드를 작성하는 것입니다. +이를 위한 특별한 구문이 있습니다. 일반적인 형태는 다음과 같습니다: + +``` +asm [volatile] ("assembly code" : output operand : input operand : clobbers); +``` + +gcc 문서에서 읽을 수 있듯이 volatile 키워드는 다음을 의미합니다: + +``` +Extended asm 문의 일반적인 사용은 입력 값을 조작하여 출력 값을 생성하는 것입니다. 그러나 asm 문이 부작용을 일으킬 수도 있습니다. 그런 경우, volatile 한정자를 사용하여 특정 최적화를 비활성화해야 할 수 있습니다. +``` + +각 피연산자는 괄호 안에 C 표현식이 뒤따르는 제약 문자열로 설명됩니다. 여러 가지 제약 조건이 있습니다: + +* `r` - 범용 레지스터에 변수 값 유지 +* `g` - 범용 레지스터가 아닌 레지스터를 제외하고, 모든 레지스터, 메모리 또는 즉시 정수 피연산자가 허용됩니다. +* `f` - 부동 소수점 레지스터 +* `m` - 메모리 피연산자가 허용되며, 기계가 일반적으로 지원하는 모든 종류의 주소를 사용할 수 있습니다. +기타 등등... + +따라서 우리의 hello world는 다음과 같을 것입니다: + +```C +#include + +int main() { + char* str = "Hello World\n"; + long len = strlen(str); + int ret = 0; + + __asm__("movq $1, %%rax \n\t" + "movq $1, %%rdi \n\t" + "movq %1, %%rsi \n\t" + "movl %2, %%edx \n\t" + "syscall" + : "=g"(ret) + : "g"(str), "g" (len)); + + return 0; +} +``` + +여기서 우리는 이전 예제와 같은 2개의 변수와 인라인 어셈블리 정의를 볼 수 있습니다. +먼저 우리는 rax와 rdi 레지스터에 1을 넣습니다(write 시스템 콜 번호와 stdout)는 것은 일반 어셈블리 hello world에서 했던 것과 같습니다. + +다음으로 rsi와 rdi 레지스터에 대해 유사한 작업을 수행하지만 첫 번째 피연산자는 $ 대신 % 기호로 시작합니다. +이는 str이 %1로 참조되는 출력 피연산자이고 len이 %2로 참조되는 두 번째 출력 피연산자임을 의미합니다. + +따라서 우리는 %n 표기법을 사용하여 str과 len의 값을 rsi와 rdi에 넣습니다. 여기서 n은 출력 피연산자의 번호입니다. +또한 레지스터 이름 앞에 %%가 붙습니다. + +``` +이는 GCC가 피연산자와 레지스터를 구분하는 데 도움이 됩니다. 피연산자는 접두사로 단일 %를 가집니다. +``` + +다음과 같이 빌드할 수 있습니다: + +``` +build: + gcc casm.c -o casm +``` + +## 어셈블리에서 C 호출하기 + +그리고 마지막 방법은 어셈블리 코드에서 C 함수를 호출하는 것입니다. +예를 들어, 단순히 Hello world를 출력하는 하나의 함수가 있는 다음과 같은 간단한 C 코드가 있습니다: + +```C +#include + +extern int print(); + +int print() { + printf("Hello World\n"); + return 0; +} +``` + +이제 우리는 이 함수를 어셈블리 코드에서 extern으로 정의하고 이전 게시물에서 여러 번 했던 것처럼 call 명령어로 호출할 수 있습니다: + +```asssembly +global _start + +extern print + +section .text + +_start: + call print + + mov rax, 60 + mov rdi, 0 + syscall +``` + +다음과 같이 빌드합니다: + +``` +build: + gcc -c casm.c -o c.o + nasm -f elf64 casm.asm -o casm.o + ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc casm.o c.o -o casm +``` + +이제 우리의 세 번째 hello world를 실행할 수 있습니다. From 6be8f78c3d90f30b127ecf56b5f0d844ac8a5d19 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 10:25:03 +0900 Subject: [PATCH 20/45] file tree --- ko_doc/{post/CONTIBUTING.md => CONTIRUBUTING.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ko_doc/{post/CONTIBUTING.md => CONTIRUBUTING.md} (100%) diff --git a/ko_doc/post/CONTIBUTING.md b/ko_doc/CONTIRUBUTING.md similarity index 100% rename from ko_doc/post/CONTIBUTING.md rename to ko_doc/CONTIRUBUTING.md From 5adb58babb1869ee09312df4d7edb3b7fe3c04ba Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:07:01 +0900 Subject: [PATCH 21/45] Update ko_asm_1.md From e2efad9ac00fb20ceca794c2c392c6a53a864fb2 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:23:58 +0900 Subject: [PATCH 22/45] final part --- ko_doc/post/ko_asm_8.md | 197 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 ko_doc/post/ko_asm_8.md diff --git a/ko_doc/post/ko_asm_8.md b/ko_doc/post/ko_asm_8.md new file mode 100644 index 0000000..a85ecda --- /dev/null +++ b/ko_doc/post/ko_asm_8.md @@ -0,0 +1,197 @@ +이번 파트 8이자 마지막 파트에서는 어셈블리에서 비정수 숫자를 다루는 방법을 살펴보겠습니다. 부동 소수점 데이터를 처리하는 방법에는 몇 가지가 있습니다: + +* FPU (부동 소수점 유닛) +* SSE (스트림 SIMD 확장) + +먼저 부동 소수점 숫자가 메모리에 어떻게 저장되는지 살펴보겠습니다. 부동 소수점 데이터 유형에는 세 가지가 있습니다: + +* 단정도 (single-precision) +* 배정도 (double-precision) +* 확장 배정도 (double-extended precision) + +인텔의 64-IA-32 아키텍처 소프트웨어 개발자 매뉴얼 1권에 따르면: + +``` +이 데이터 유형의 데이터 형식은 IEEE 표준 754에 명시된 이진 부동 소수점 산술 형식에 직접적으로 해당됩니다. +``` + +단정도 부동 소수점 숫자는 메모리에 다음과 같이 저장됩니다: + +* 부호 (sign) - 1 비트 +* 지수 (exponent) - 8 비트 +* 가수 (mantissa) - 23 비트 + +예를 들어, 다음과 같은 숫자가 있을 때: + + | sign | exponent | mantissa + |-------|----------|------------------------- + | 0 | 00001111 | 110000000000000000000000 + +지수는 8 비트 부호가 있는 정수로 -128에서 127 사이이거나, 8 비트 부호가 없는 정수로 0에서 255 사이일 수 있습니다. +부호 비트가 0이므로 양수입니다. 지수는 00001111b 또는 10진수로 15입니다. 단정도 부동 소수점에서의 편향값은 127이므로, 지수에서 127을 빼야 합니다. + +즉, 15 - 127 = -112입니다. 정규화된 이진 정수 부분은 항상 1이므로 가수에는 그 소수 부분만 기록됩니다. +즉, 가수는 1.110000000000000000000000입니다. 결과 값은 다음과 같습니다: + +``` +value = mantissa * 2^-112 +``` + +배정도 숫자는 64 비트 메모리에 다음과 같이 저장됩니다: + +* 부호 (sign) - 1 비트 +* 지수 (exponent) - 11 비트 +* 가수 (mantissa) - 52 비트 + +결과 숫자는 다음과 같이 계산할 수 있습니다: + +``` +value = (-1)^sign * (1 + mantissa / 2 ^ 52) * 2 ^ exponent - 1023) +``` + +확장 배정도는 80 비트 숫자이며: + +* 부호 (sign) - 1 비트 +* 지수 (exponent) - 15 비트 +* 가수 (mantissa) - 112 비트 + +자세한 내용은 [여기](https://en.wikipedia.org/wiki/Extended_precision)에서 확인할 수 있습니다. +이제 간단한 예제를 살펴보겠습니다. + +## x87 FPU + +x87 부동 소수점 유닛(FPU)은 고성능 부동 소수점 처리를 제공합니다. +부동 소수점, 정수 및 패킹된 BCD 정수 데이터 유형과 부동 소수점 처리 알고리즘을 지원합니다. + +x87은 다음과 같은 명령어 세트를 제공합니다: + +* 데이터 전송 명령어 +* 기본 산술 명령어 +* 비교 명령어 +* 초월적 명령어 +* 상수 로드 명령어 +* x87 FPU 제어 명령어 + +물론 여기서 x87이 제공하는 모든 명령어를 살펴보지는 않겠지만, +추가 정보는 64-IA-32 아키텍처 소프트웨어 개발자 매뉴얼 1권의 8장을 참조하세요. 몇 가지 데이터 전송 명령어는 다음과 같습니다: + +* `FDL` - 부동 소수점 로드 +* `FST` - 부동 소수점 저장 (ST(0) 레지스터에) +* `FSTP` - 부동 소수점 저장 및 팝 (ST(0) 레지스터에) + +산술 명령어: + +* `FADD` - 부동 소수점 덧셈 +* `FIADD` - 정수를 부동 소수점에 추가 +* `FSUB` - 부동 소수점 뺄셈 +* `FISUB` - 부동 소수점에서 정수 뺄셈 +* `FABS` - 절대값 가져오기 +* `FIMUL` - 정수와 부동 소수점 곱셈 +* `FIDIV` - 정수와 부동 소수점 나눗셈 + +x87에는 10바이트 레지스터 8개가 링 스택 형태로 조직되어 있습니다. 스택의 맨 위는 ST(0) 레지스터이며, 나머지 레지스터는 ST(1), ST(2), ..., ST(7)입니다. 일반적으로 부동 소수점 데이터를 다룰 때 사용됩니다. + +예를 들어: + +```assembly +section .data + x dw 1.0 + +fld dword [x] +``` + +이는 x의 값을 스택에 푸시합니다. 연산자는 32비트, 64비트 또는 80비트일 수 있습니다. +일반 스택처럼 작동하며, fld로 다른 값을 푸시하면 x 값은 ST(1)에 있고, 새로운 값이 ST(0)에 위치하게 됩니다. +FPU 명령어는 이러한 레지스터를 사용할 수 있습니다. + +예를 들어: + +```assembly +;; +;; st0 값을 st3에 더하고 결과를 st0에 저장합니다 +;; +fadd st0, st3 + +;; +;; x와 y를 더하고 결과를 st0에 저장합니다 +;; +fld dword [x] +fld dword [y] +fadd +``` + +간단한 예제를 살펴보겠습니다. +원의 반지름을 가지고 원의 면적을 계산하고 출력하는 예제입니다: + +```assembly +extern printResult + +section .data + radius dq 1.7 + result dq 0 + + SYS_EXIT equ 60 + EXIT_CODE equ 0 + +global _start +section .text + +_start: + fld qword [radius] + fld qword [radius] + fmul + + fldpi + fmul + fstp qword [result] + + mov rax, 0 + movq xmm0, [result] + call printResult + + mov rax, SYS_EXIT + mov rdi, EXIT_CODE + syscall +``` + +이 예제의 동작을 이해해 보겠습니다: +우선 데이터 섹션에는 반지름 데이터와 결과를 저장할 변수들이 정의되어 있습니다. +그 후 시스템 호출 종료를 위한 두 상수가 정의되어 있습니다. 프로그램의 진입점인 _start에서 fld 명령어로 ST(0)과 ST(1) 레지스터에 반지름 값을 저장하고, fmul 명령어로 두 값을 곱합니다. + +이 연산 후에는 ST(0) 레지스터에 반지름의 제곱이 저장됩니다. +이후 fldpi 명령어로 π 값을 ST(0) 레지스터에 로드하고, fmul로 ST(0) (π)과 ST(1) (반지름의 제곱)을 곱하여 결과를 ST(0) 레지스터에 저장합니다. +이제 ST(0) 레지스터에 원의 면적이 있으므로 fstp 명령어로 결과를 저장소에 추출합니다. 다음으로, 결과를 C 함수에 전달하고 호출합니다. + +어셈블리 코드에서 C 함수를 호출할 때 x86_64 호출 규약을 알아야 합니다. +일반적으로 함수 매개변수는 레지스터 rdi (arg1), rsi (arg2) 등을 통해 전달되지만, 부동 소수점 데이터의 경우에는 특별한 레지스터 xmm0 - xmm15가 사용됩니다. +우선 xmmN 레지스터의 번호를 rax 레지스터에 넣고 (우리의 경우 0), 결과를 xmm0 레지스터에 넣습니다. 이제 C 함수 printResult를 호출할 수 있습니다: + +```C +#include + +extern int printResult(double result); + +int printResult(double result) { + printf("Circle radius is - %f\n", result); + return 0; +} +``` + +이 코드는 다음과 같이 빌드할 수 있습니다: + +``` +build: + gcc -g -c circle_fpu_87c.c -o c.o + nasm -f elf64 circle_fpu_87.asm -o circle_fpu_87.o + ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc circle_fpu_87.o c.o -o testFloat1 + +clean: + rm -rf *.o + rm -rf testFloat1 +``` + +그리고 실행하면: + +![result](/content/assets/result_asm_8.png) + + From fd1411b40d8481b04a070f10d82ad58de9e36499 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:27:15 +0900 Subject: [PATCH 23/45] fix --- ko_doc/post/ko_asm_3.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ko_doc/post/ko_asm_3.md b/ko_doc/post/ko_asm_3.md index 024cb04..39c7af6 100644 --- a/ko_doc/post/ko_asm_3.md +++ b/ko_doc/post/ko_asm_3.md @@ -3,6 +3,7 @@ 임시 데이터 저장을 위해 16개의 범용 레지스터가 있습니다. RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP 및 R8-R15입니다. + 이는 심각한 애플리케이션에는 너무 적습니다. 따라서 스택에 데이터를 저장할 수 있습니다. 스택의 또 다른 용도는 다음과 같습니다: 함수를 호출할 때 반환 주소가 스택에 복사됩니다. 함수 실행이 끝난 후, 주소가 명령 카운터(RIP)에 복사되고 애플리케이션은 함수 다음 위치에서 계속 실행됩니다. @@ -192,7 +193,10 @@ return_str: str_to_int의 시작에서 우리는 rax를 0으로, rcx를 10으로 설정합니다. 그 다음 next 레이블로 갑니다. 위의 예시에서 볼 수 있듯이(str_to_int의 첫 번째 호출 전 첫 줄) 우리는 스택에서 argv[1]을 rsi에 넣습니다. 이제 rsi의 첫 번째 바이트를 0과 비교합니다. 모든 문자열은 NULL 심볼로 끝나기 때문이며, 만약 0이라면 반환합니다. -0이 아니라면 그 값을 1바이트 bl 레지스터에 복사하고 48을 뺍니다. 왜 48인가요? 0부터 9까지의 모든 숫자는 ASCII 테이블에서 48부터 57까지의 코드를 가집니다. + +0이 아니라면 그 값을 1바이트 bl 레지스터에 복사하고 48을 뺍니다. +왜 48인가요? 0부터 9까지의 모든 숫자는 ASCII 테이블에서 48부터 57까지의 코드를 가집니다. + 따라서 숫자 심볼에서 48을 빼면(예를 들어 57에서) 우리는 숫자를 얻게 됩니다. 그 다음 rax를 rcx(값은 10)와 곱합니다. 이후 rsi를 증가시켜 다음 바이트를 얻고 다시 루프합니다. 알고리즘은 간단합니다. 예를 들어 rsi가 '5' '7' '6' '\000' 시퀀스를 가리킨다면 다음과 같은 단계를 거칩니다: @@ -226,7 +230,9 @@ int_to_str: 여기서 우리는 rdx에 0을, rbx에 10을 넣습니다. 그 다음 div rbx를 실행합니다. str_to_int 호출 전의 코드를 보면, rax에는 두 명령줄 인수의 합인 정수가 들어있습니다. 이 명령어로 rax 값을 rbx 값으로 나누고 나머지를 rdx에, 몫을 rax에 얻습니다. + 다음으로 rdx에 48과 0x0을 더합니다. 48을 더하면 이 숫자의 ASCII 심볼을 얻게 되고, 모든 문자열은 0x0으로 끝나야 합니다. + 이후 심볼을 스택에 저장하고, r12를 증가시킵니다(첫 반복에서는 0입니다, _start에서 0으로 설정했습니다). 그리고 rax를 0과 비교합니다. 0이면 정수를 문자열로 변환이 끝났다는 뜻입니다. 알고리즘을 단계별로 살펴보겠습니다. 예를 들어 숫자 23이 있다면: @@ -262,8 +268,10 @@ print: ``` 우리는 이미 `sys_write` 시스템 콜로 문자열을 출력하는 방법을 알고 있지만, 여기에 한 가지 흥미로운 부분이 있습니다. 우리는 문자열의 길이를 계산해야 합니다. `int_to_str`을 보면, 매 반복마다 r12 레지스터를 증가시키는 것을 볼 수 있습니다. + 따라서 r12에는 우리 숫자의 자릿수가 들어있습니다. 우리는 이를 8로 곱해야 합니다(각 심볼을 스택에 push했기 때문에), 그러면 출력해야 할 문자열의 길이가 됩니다. 이후 항상 그랬듯이 rax에 1(sys_write 번호), rdi에 1(stdin), rdx에 문자열 길이, rsi에 스택의 최상단 포인터(문자열의 시작)를 넣습니다. + 그리고 프로그램을 종료합니다: ```assembly From e15808de7c1dec0ef6cf8f3e24d35555e4f6c2ed Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:28:24 +0900 Subject: [PATCH 24/45] update --- ko_doc/post/ko_asm_8.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ko_doc/post/ko_asm_8.md b/ko_doc/post/ko_asm_8.md index a85ecda..5dd0729 100644 --- a/ko_doc/post/ko_asm_8.md +++ b/ko_doc/post/ko_asm_8.md @@ -1,4 +1,4 @@ -이번 파트 8이자 마지막 파트에서는 어셈블리에서 비정수 숫자를 다루는 방법을 살펴보겠습니다. 부동 소수점 데이터를 처리하는 방법에는 몇 가지가 있습니다: +파트 8이자 마지막 파트에서는 어셈블리에서 비정수 숫자를 다루는 방법을 살펴보겠습니다. 부동 소수점 데이터를 처리하는 방법에는 몇 가지가 있습니다: * FPU (부동 소수점 유닛) * SSE (스트림 SIMD 확장) From 11015045a3bac8e394ddd3307e338d3eba39ea51 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:36:05 +0900 Subject: [PATCH 25/45] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 9dcc2cb..99c164e 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,17 @@ Thanks to the volunteers, the posts about assembly programming are translated in * [X86_64 Assembly'a merhaba deyin bölüm 7](https://github.com/furkanonder/asm/blob/master/bolumler/7.md) * [X86_64 Assembly'a merhaba deyin bölüm 8](https://github.com/furkanonder/asm/blob/master/bolumler/8.md) +### Korean translation + + * [x64 어셈블리와 친해지기 [파트 1]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_1.md) + * [x64 어셈블리와 친해지기 [파트 2]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_2.md) + * [x64 어셈블리와 친해지기 [파트 3]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_3.md) + * [x64 어셈블리와 친해지기 [파트 4]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_4.md) + * [x64 어셈블리와 친해지기 [파트 5]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_5.md) + * [x64 어셈블리와 친해지기 [파트 6]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_6.md) + * [x64 어셈블리와 친해지기 [파트 7]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_7.md) + * [x64 어셈블리와 친해지기 [파트 8]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_8.md) + ## Contribution Read the [Contribution guide](./CONTRIBUTING.md) to learn how to contribute to the project. When contributing, make sure to follow the [Code of Conduct](./CODE_OF_CONDUCT.md). From 1963233faea226aaad22652f63899f08560c0fe5 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:38:19 +0900 Subject: [PATCH 26/45] header --- ko_doc/post/ko_asm_2.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index bc3ea9f..fdf32f6 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -1,4 +1,6 @@ -며칠 전에 첫 블로그 포스트인 "x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 1]"을 작성했는데, 예상외로 큰 관심을 받았습니다: +# X86_64 어셈블리와 친해지기 [파트 2] + +며칠 전에 첫 블로그 포스트인 "x86_x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 1]"을 작성했는데, 예상외로 큰 관심을 받았습니다: ![newscombinator](/content/assets/newscombinator-screenshot.png) ![reddit](/content/assets/reddit-screenshot.png) From b5adac8acaf8003a8a40344e0d9866537061c3bd Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:39:12 +0900 Subject: [PATCH 27/45] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 99c164e..88fd433 100644 --- a/README.md +++ b/README.md @@ -70,14 +70,14 @@ Thanks to the volunteers, the posts about assembly programming are translated in ### Korean translation - * [x64 어셈블리와 친해지기 [파트 1]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_1.md) - * [x64 어셈블리와 친해지기 [파트 2]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_2.md) - * [x64 어셈블리와 친해지기 [파트 3]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_3.md) - * [x64 어셈블리와 친해지기 [파트 4]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_4.md) - * [x64 어셈블리와 친해지기 [파트 5]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_5.md) - * [x64 어셈블리와 친해지기 [파트 6]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_6.md) - * [x64 어셈블리와 친해지기 [파트 7]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_7.md) - * [x64 어셈블리와 친해지기 [파트 8]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_8.md) + * [X86_64 어셈블리와 친해지기 [파트 1]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_1.md) + * [X86_64 어셈블리와 친해지기 [파트 2]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_2.md) + * [X86_64 어셈블리와 친해지기 [파트 3]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_3.md) + * [X86_64 어셈블리와 친해지기 [파트 4]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_4.md) + * [X86_64 어셈블리와 친해지기 [파트 5]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_5.md) + * [X86_64 어셈블리와 친해지기 [파트 6]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_6.md) + * [X86_64 어셈블리와 친해지기 [파트 7]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_7.md) + * [X86_64 어셈블리와 친해지기 [파트 8]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_8.md) ## Contribution From 042bd178e6f9b3b7397974f36ee14a43d8330174 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:39:42 +0900 Subject: [PATCH 28/45] Update ko_asm_4.md --- ko_doc/post/ko_asm_4.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ko_doc/post/ko_asm_4.md b/ko_doc/post/ko_asm_4.md index 83bb308..d00c202 100644 --- a/ko_doc/post/ko_asm_4.md +++ b/ko_doc/post/ko_asm_4.md @@ -1,3 +1,4 @@ +# X86_64 어셈블리와 친해지기 [파트 4] 얼마 전 x86_64 어셈블리 프로그래밍에 관한 블로그 포스트 시리즈를 작성하기 시작했습니다. 관련 포스트는 `asm` 태그로 찾을 수 있습니다. 최근에는 바빠서 새로운 포스트가 없었지만, 오늘부터 다시 어셈블리에 대한 포스트를 작성할 예정이며, 매주 포스트를 시도할 것입니다. From d848d2d27a0a402f38c48c964a8818213d2c5042 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:39:59 +0900 Subject: [PATCH 29/45] Update ko_asm_1.md --- ko_doc/post/ko_asm_1.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ko_doc/post/ko_asm_1.md b/ko_doc/post/ko_asm_1.md index 35b6fbe..a78904d 100644 --- a/ko_doc/post/ko_asm_1.md +++ b/ko_doc/post/ko_asm_1.md @@ -1,3 +1,5 @@ +# X86_64 어셈블리와 친해지기 [파트 1] + ## 소개 우리 사이에는 많은 개발자들이 있습니다. 우리는 매일 엄청난 양의 코드를 작성합니다. From 8ce7fdd4c575e6ce41c893f7cf5301e6ebb51ffb Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:40:16 +0900 Subject: [PATCH 30/45] Update ko_asm_3.md --- ko_doc/post/ko_asm_3.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ko_doc/post/ko_asm_3.md b/ko_doc/post/ko_asm_3.md index 39c7af6..46e47fc 100644 --- a/ko_doc/post/ko_asm_3.md +++ b/ko_doc/post/ko_asm_3.md @@ -1,3 +1,4 @@ +# X86_64 어셈블리와 친해지기 [파트 3] 스택은 LIFO(Last In, First Out) 원칙으로 작동하는 메모리의 특별한 영역입니다. From fba029158a82be2eaea81dc0be533353b085dfbd Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:40:35 +0900 Subject: [PATCH 31/45] Update ko_asm_8.md --- ko_doc/post/ko_asm_8.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ko_doc/post/ko_asm_8.md b/ko_doc/post/ko_asm_8.md index 5dd0729..4695a32 100644 --- a/ko_doc/post/ko_asm_8.md +++ b/ko_doc/post/ko_asm_8.md @@ -1,3 +1,5 @@ +# X86_64 어셈블리와 친해지기 [파트 8] + 파트 8이자 마지막 파트에서는 어셈블리에서 비정수 숫자를 다루는 방법을 살펴보겠습니다. 부동 소수점 데이터를 처리하는 방법에는 몇 가지가 있습니다: * FPU (부동 소수점 유닛) From 67e5dc8da59e66c5fa6199267eaedecef30e733c Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:41:00 +0900 Subject: [PATCH 32/45] Update ko_asm_7.md --- ko_doc/post/ko_asm_7.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ko_doc/post/ko_asm_7.md b/ko_doc/post/ko_asm_7.md index 3af98fc..46464c8 100644 --- a/ko_doc/post/ko_asm_7.md +++ b/ko_doc/post/ko_asm_7.md @@ -1,3 +1,4 @@ +# X86_64 어셈블리와 친해지기 [파트 7] x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 7] 입니다. 이번에는 C와 어셈블러를 함께 사용하는 방법을 살펴보겠습니다. From ed9f3f9530c0403111c036a477d1b5478502a077 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:41:20 +0900 Subject: [PATCH 33/45] Update ko_asm_5.md --- ko_doc/post/ko_asm_5.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ko_doc/post/ko_asm_5.md b/ko_doc/post/ko_asm_5.md index 009dfde..a965863 100644 --- a/ko_doc/post/ko_asm_5.md +++ b/ko_doc/post/ko_asm_5.md @@ -1,3 +1,4 @@ +# X86_64 어셈블리와 친해지기 [파트 5] 이 글은 x86_64 어셈블리의 다섯 번째 파트로 매크로를 다룹니다. 이 포스트는 x86_64에 대한 블로그 포스트가 아니라 NASM 어셈블러와 그 전처리기에 대한 것입니다. 관심이 있다면 계속 읽어보세요. From e3eff1e4ec8e331a8a76dcd1433121adb0909855 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:41:35 +0900 Subject: [PATCH 34/45] Update ko_asm_6.md --- ko_doc/post/ko_asm_6.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ko_doc/post/ko_asm_6.md b/ko_doc/post/ko_asm_6.md index ceadfd1..a47f63d 100644 --- a/ko_doc/post/ko_asm_6.md +++ b/ko_doc/post/ko_asm_6.md @@ -1,3 +1,4 @@ +# X86_64 어셈블리와 친해지기 [파트 6] 이 글은 x86_64 어셈블리의 여섯 번째 파트로 AT&T 어셈블리 문법을 다룹니다. 이전에는 NASM 어셈블러를 사용했지만, AT&T 문법을 사용하는 GNU 어셈블러(gas)와 그 문법의 차이를 살펴보겠습니다. From 6c358e7c6cca9857cca9e3f5c49800649aa2c5d6 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:44:57 +0900 Subject: [PATCH 35/45] add Translator's Note --- ko_doc/post/ko_asm_1.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ko_doc/post/ko_asm_1.md b/ko_doc/post/ko_asm_1.md index a78904d..34f4023 100644 --- a/ko_doc/post/ko_asm_1.md +++ b/ko_doc/post/ko_asm_1.md @@ -171,3 +171,11 @@ $ ld -o hello hello.o ``` 이후, ./hello를 실행하면 터미널에서 "Hello world" 문자열이 출력됩니다. + +## 옮긴이의 말 + +이 튜토리얼을 번역하며 원문의 기술적 정확성을 유지하면서도 내용을 보다 쉽게 전달하려고 노력했습니다. +어셈블리 언어는 저수준에서 컴퓨터가 어떻게 동작하는지 이해하는 데 큰 도움이 되지만, 그만큼 도전적인 부분도 많습니다. + +이 번역이 x86_64 어셈블리 언어를 이해하고자 하는 분들에게 유용한 가이드가 되기를 바랍니다. +번역 과정에서 발생한 오류나 부정확한 부분에 대한 피드백은 언제든 환영합니다. 이 튜토리얼을 읽어주셔서 감사합니다. From 2b2896f551500fd01cc6742dd6aad68d601ec06b Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:48:54 +0900 Subject: [PATCH 36/45] note --- ko_doc/post/ko_asm_2.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index fdf32f6..e5a6edd 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -243,3 +243,12 @@ rax와 150이 같지 않으면(jne로 확인) .exit 레이블로 갑니다. 같 이제 두 개의 레이블이 있습니다: .exit와 .rightSum. 첫 번째는 단순히 rax에 60을 설정합니다. 이는 exit 시스템 콜 번호이며, rdi에는 0을 설정합니다. 이는 종료 코드입니다. 두 번째인 .rightSum은 매우 간단합니다. "Sum is correct"를 출력할 뿐입니다. + +## 옮긴이의 말 + +이번 파트에서는 x86_64 어셈블리 프로그래밍의 기초 개념들을 다뤘습니다. +레지스터, 메모리 모델, 그리고 제어 흐름과 같은 중요 개념들이 소개되었으며, 이를 통해 어셈블리 언어의 기본 구조를 이해하는 데 도움이 될 것입니다. +특히, 어셈블리 프로그래밍의 핵심인 조건부 점프와 무조건 점프 명령어를 통해 프로그램의 제어 흐름을 관리하는 방법에 대해 알아보았습니다. + +어셈블리 언어는 처음 접할 때는 다소 어려워 보일 수 있지만, 그 구조와 작동 방식을 이해하면 매우 강력한 도구가 될 수 있습니다. +이번 파트에서 다룬 개념들을 바탕으로, 어셈블리 언어와 더욱 친숙해지길 바랍니다. From a4650fb593d9c48a22c38bc1eacbce09c9a2b185 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:50:46 +0900 Subject: [PATCH 37/45] note --- ko_doc/post/ko_asm_3.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ko_doc/post/ko_asm_3.md b/ko_doc/post/ko_asm_3.md index 46e47fc..ca00f00 100644 --- a/ko_doc/post/ko_asm_3.md +++ b/ko_doc/post/ko_asm_3.md @@ -283,5 +283,13 @@ exit: syscall ``` -이것으로 "x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 3]를 마칩니다. +이것으로 "x86_64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 3]를 마칩니다. +## 옮긴이의 말 + +이 문서에서는 x86_64 어셈블리 언어의 스택과 레지스터, 그리고 기본적인 스택 작업에 대한 이해를 돕기 위해 스택의 동작 원리와 관련된 예제를 설명하였습니다. +스택은 함수 호출 시 반환 주소를 저장하거나 임시 데이터를 저장하는 데 중요한 역할을 하며, 다양한 어셈블리 명령어를 사용하여 스택을 조작할 수 있습니다. + +스택을 다루는 명령어와 그 사용법을 이해하는 것은 어셈블리 프로그래밍의 기초를 다지는 데 매우 중요합니다. +이 튜토리얼에서 다룬 예제들은 스택을 효율적으로 활용하는 데 도움을 줄 것입니다. +계속해서 어셈블리 언어에 대한 이해를 깊이 있는 것들을 공부해보세요! From a64d94a9693c5b3840ce1e1edf382eeaa5f866da Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:55:38 +0900 Subject: [PATCH 38/45] Update ko_asm_4.md --- ko_doc/post/ko_asm_4.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ko_doc/post/ko_asm_4.md b/ko_doc/post/ko_asm_4.md index d00c202..80af68d 100644 --- a/ko_doc/post/ko_asm_4.md +++ b/ko_doc/post/ko_asm_4.md @@ -228,3 +228,11 @@ clean: * `CMPSB` - 바이트 문자열 비교 * `SCASB` - 바이트 문자열 스캔 * `STOSB` - 문자열에 바이트를 기록합니다 + +## 옮긴이의 말 + +이번 튜토리얼에서는 x86_64 어셈블리에서 문자열을 뒤집는 방법을 배웠습니다. +문자열을 바이트 배열로 다루는 기본 기술을 익히며, 스택과 레지스터를 활용하는 법을 배웠습니다. 그리고 +문자열 처리 명령어와 기법들을 통해 어셈블리 언어와 한 발자국 친해졌다고 생각합니다 :) + +계속해서 포기하지 않고 실력을 쌓길 바랍니다! From d8187a30f8397e68bd56b64d87ad06796925b357 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:56:57 +0900 Subject: [PATCH 39/45] Update ko_asm_4.md --- ko_doc/post/ko_asm_4.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ko_doc/post/ko_asm_4.md b/ko_doc/post/ko_asm_4.md index 80af68d..d00c202 100644 --- a/ko_doc/post/ko_asm_4.md +++ b/ko_doc/post/ko_asm_4.md @@ -228,11 +228,3 @@ clean: * `CMPSB` - 바이트 문자열 비교 * `SCASB` - 바이트 문자열 스캔 * `STOSB` - 문자열에 바이트를 기록합니다 - -## 옮긴이의 말 - -이번 튜토리얼에서는 x86_64 어셈블리에서 문자열을 뒤집는 방법을 배웠습니다. -문자열을 바이트 배열로 다루는 기본 기술을 익히며, 스택과 레지스터를 활용하는 법을 배웠습니다. 그리고 -문자열 처리 명령어와 기법들을 통해 어셈블리 언어와 한 발자국 친해졌다고 생각합니다 :) - -계속해서 포기하지 않고 실력을 쌓길 바랍니다! From aa9785ad48ef97b8475a5cb9ecfc78e21bc3af50 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:57:15 +0900 Subject: [PATCH 40/45] Update ko_asm_2.md --- ko_doc/post/ko_asm_2.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md index e5a6edd..bad3d90 100644 --- a/ko_doc/post/ko_asm_2.md +++ b/ko_doc/post/ko_asm_2.md @@ -244,11 +244,3 @@ rax와 150이 같지 않으면(jne로 확인) .exit 레이블로 갑니다. 같 이는 exit 시스템 콜 번호이며, rdi에는 0을 설정합니다. 이는 종료 코드입니다. 두 번째인 .rightSum은 매우 간단합니다. "Sum is correct"를 출력할 뿐입니다. -## 옮긴이의 말 - -이번 파트에서는 x86_64 어셈블리 프로그래밍의 기초 개념들을 다뤘습니다. -레지스터, 메모리 모델, 그리고 제어 흐름과 같은 중요 개념들이 소개되었으며, 이를 통해 어셈블리 언어의 기본 구조를 이해하는 데 도움이 될 것입니다. -특히, 어셈블리 프로그래밍의 핵심인 조건부 점프와 무조건 점프 명령어를 통해 프로그램의 제어 흐름을 관리하는 방법에 대해 알아보았습니다. - -어셈블리 언어는 처음 접할 때는 다소 어려워 보일 수 있지만, 그 구조와 작동 방식을 이해하면 매우 강력한 도구가 될 수 있습니다. -이번 파트에서 다룬 개념들을 바탕으로, 어셈블리 언어와 더욱 친숙해지길 바랍니다. From 72b6120ab61179df6ab74548c38b84bc6cd9a12c Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 13:57:35 +0900 Subject: [PATCH 41/45] Update ko_asm_3.md --- ko_doc/post/ko_asm_3.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ko_doc/post/ko_asm_3.md b/ko_doc/post/ko_asm_3.md index ca00f00..d6a2e24 100644 --- a/ko_doc/post/ko_asm_3.md +++ b/ko_doc/post/ko_asm_3.md @@ -284,12 +284,3 @@ exit: ``` 이것으로 "x86_64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 3]를 마칩니다. - -## 옮긴이의 말 - -이 문서에서는 x86_64 어셈블리 언어의 스택과 레지스터, 그리고 기본적인 스택 작업에 대한 이해를 돕기 위해 스택의 동작 원리와 관련된 예제를 설명하였습니다. -스택은 함수 호출 시 반환 주소를 저장하거나 임시 데이터를 저장하는 데 중요한 역할을 하며, 다양한 어셈블리 명령어를 사용하여 스택을 조작할 수 있습니다. - -스택을 다루는 명령어와 그 사용법을 이해하는 것은 어셈블리 프로그래밍의 기초를 다지는 데 매우 중요합니다. -이 튜토리얼에서 다룬 예제들은 스택을 효율적으로 활용하는 데 도움을 줄 것입니다. -계속해서 어셈블리 언어에 대한 이해를 깊이 있는 것들을 공부해보세요! From 9e0968220d173a9e5a8b0372df19d3ab669c9769 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 14:09:03 +0900 Subject: [PATCH 42/45] NOTE --- ko_doc/post/ko_asm_8.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ko_doc/post/ko_asm_8.md b/ko_doc/post/ko_asm_8.md index 4695a32..3123f5f 100644 --- a/ko_doc/post/ko_asm_8.md +++ b/ko_doc/post/ko_asm_8.md @@ -196,4 +196,7 @@ clean: ![result](/content/assets/result_asm_8.png) +## 옮긴이의 말 +이제까지 x86_64 어셈블리와 NASM 어셈블러의 다양한 기능을 살펴보았습니다. 매크로를 활용해 코드를 간결하게 하고, 반복 작업을 줄이는 방법을 익혔습니다. 어셈블리 언어는 복잡할 수 있지만, +어셈블리 언어를 배워가는 여정은 계속될 것입니다. 이 시리즈가 여러분의 학습에 도움이 되었기를 바랍니다. From e01e512dae1952eac055c11f5068b4a796befd19 Mon Sep 17 00:00:00 2001 From: maldron0309 Date: Tue, 20 Aug 2024 20:21:40 +0900 Subject: [PATCH 43/45] remove ko doc --- ko_doc/CODE_OF_CONDUCT.md | 124 ----------------- ko_doc/CONTIRUBUTING.md | 33 ----- ko_doc/README_KO.md | 90 ------------ ko_doc/SECURITY.md | 6 - ko_doc/post/ko_asm_1.md | 181 ------------------------ ko_doc/post/ko_asm_2.md | 246 -------------------------------- ko_doc/post/ko_asm_3.md | 286 -------------------------------------- ko_doc/post/ko_asm_4.md | 230 ------------------------------ ko_doc/post/ko_asm_5.md | 150 -------------------- ko_doc/post/ko_asm_6.md | 185 ------------------------ ko_doc/post/ko_asm_7.md | 169 ---------------------- ko_doc/post/ko_asm_8.md | 202 --------------------------- 12 files changed, 1902 deletions(-) delete mode 100644 ko_doc/CODE_OF_CONDUCT.md delete mode 100644 ko_doc/CONTIRUBUTING.md delete mode 100644 ko_doc/README_KO.md delete mode 100644 ko_doc/SECURITY.md delete mode 100644 ko_doc/post/ko_asm_1.md delete mode 100644 ko_doc/post/ko_asm_2.md delete mode 100644 ko_doc/post/ko_asm_3.md delete mode 100644 ko_doc/post/ko_asm_4.md delete mode 100644 ko_doc/post/ko_asm_5.md delete mode 100644 ko_doc/post/ko_asm_6.md delete mode 100644 ko_doc/post/ko_asm_7.md delete mode 100644 ko_doc/post/ko_asm_8.md diff --git a/ko_doc/CODE_OF_CONDUCT.md b/ko_doc/CODE_OF_CONDUCT.md deleted file mode 100644 index e3299cd..0000000 --- a/ko_doc/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,124 +0,0 @@ -# 기여자 행동 강령 규약 - -## 서약 - -우리는 구성원, 기여자 및 리더로서 커뮤니티에 참여하여 -연령, 신체 크기, 눈에 보이거나 보이지 않는 장애, 민족성, 성별, 성 정체성과 표현, -경력, 학력, 사회 경제적 지위, 국적, 외모, 인종, 카스트 제도, 피부색, 종교 -또는 성적 정체성과 성적 성향에 관계없이 모든 사람을 차별하지 않을 것을 서약한다. - -우리는 개방적이고 친근하며 다양하고 포용적이며 건강한 커뮤니티에 기여하는 -방식으로 행동하고 상호작용할 것을 서약한다. - -## 표준 - -커뮤니티의 긍정적인 환경을 위해 기여자가 해야 할 행동은 다음과 같다: - -* 다른 사람들에 대한 친절과 공감 표현 -* 서로 다른 의견 및 관점, 경험에 대한 존중 -* 건설적인 피드백을 제공 및 열린 마음으로 수락 -* 책임을 받아들이고 실수로 인해 영향을 받은 사람들에게 사과하며 - 경험을 통해 배움 -* 개인뿐만 아닌 전체 커뮤니티를 위한 최선의 방법에 집중 - - -하지말아야 할 행동은 다음과 같다: - -* 성적인 언어와 이미지 사용, 성적 관심이나 어떤 종류의 접근 -* 소모적인 논쟁, 모욕적 또는 비하하는 댓글과 개인적 또는 정치적인 공격 -* 공개적이거나 개인적인 괴롭힘 -* 동의없는 집주소 또는 이메일 주소 등의 개인 정보의 공개 -* 부적절한 것으로 간주될 수 있는 다른 행위 - -## 집행 책임 - -커뮤니티 리더는 허용되는 행동의 기준을 명확히 하고 집행할 책임이 있으며 -부적절하다고 여겨지는 모든 행동, 위협, 공격 또는 피해에 대해 적절하고 -공정한 행동을 취한다. - -프로젝트 유지자는 이 행동 강령을 따르지 않은 댓글, 커밋, 코드, 위키 편집, -이슈와 그 외 다른 기여를 삭제, 수정 또는 거부할 권리와 책임이 있다. 또한, -부적당하거나 험악하거나 공격적이거나 해롭다고 생각하는 다른 행동을 한 기여자를 -일시적 또는 영구적으로 퇴장시킬 수 있다. - -커뮤니티 리더는 이 행동 강령을 따르지 않는 댓글, 커밋, 코드, 위키 편집, -이슈와 그 외 다른 기여를 삭제, 수정 또는 거부할 권리와 책임이 있으며, -적절한 경우 중재적 의사결정에 대한 이유를 전달할 것이다. - -## 범위 - -이 행동 강령은 개인이 공개 영역에서 커뮤니티를 공식적으로 대표할 때를 -포함하여 모든 커뮤니티 영역에 적용된다. -커뮤니티 대표의 예로 공식 이메일 주소 사용, 공식 소셜 미디어 계정을 통한 게시, -온/오프라인 이벤트에서 임명된 대표자의 활동이 있다. - -## 집행 - -모욕적이거나 괴롭힘 또는 그 외 하지말아야 할 행동을 발견하면 -[연락 방법 입력]을 통해 집행 책임이 있는 커뮤니티 리더에게 보고한다. -모든 불만사항은 신속하고 공정하게 검토되고 조사될 것이다. - -커뮤니티 리더는 사건의 보고자의 사생활과 안전을 존중할 의무가 있다. - -## 집행 지침 - -커뮤니티 리더는 행동 강령 위반으로 간주되는 행동에 대한 결과를 결정할 때, -다음의 커뮤니티 영향 지침을 준수한다: - -### 1. 정정 - -**커뮤니티 영향**: 커뮤니티 내 부적절한 언어 사용이나 -비전문적인 행동 또는 불쾌함을 주는 행동. - -**결과**: 커뮤니티 리더가 별도로 위반에 대한 명확성과 부적절함에 대한 -이유를 설명하고 서면 경고. -공개 사과를 요청할 수 있다. - -### 2. 경고 - -**커뮤니티 영향**: 단일 사고 또는 연속된 행동 위반. - -**결과**: 지속적인 행동에 대한 결과에 대해 경고. -특정 기간동안 행동 강령을 시행하는 사람들과의 원치 않는 상호작용을 포함한 -관련된 사람들과의 상호작용 금지. 소셜 미디어와 같은 외부 채널뿐만 아닌 -커뮤니티 공간에서의 상호작용도 금지된다. -이 조항을 위반하면 일시적 혹은 영구적으로 제재로 이어질 수 있다. - -### 3. 일시적인 제재 - -**커뮤니티 영향**: 지속적으로 부적절한 행동을 포함한 -심각한 커뮤니티 기준 위반. - -**결과**: 특정 기간동안 커뮤니티와의 어떠한 종류의 상호작용이나 -공개적 소통이 일시적 제재. -이 기간동안 행동 강령을 시행하는 사람들과의 원치 않는 상호작용을 포함한 -관련된 사람들과의 상호작용 금지. 소셜 미디어와 같은 외부 채널뿐만 아닌 -커뮤니티 공간에서의 상호작용도 금지된다. -이 조항을 위반하면 일시적 혹은 영구적으로 제재로 이어질 수 있다. - -### 4. 영구 제재 - -**커뮤니티 영향**: 지속적인 부적절한 행동, 개인적인 괴롭힘 또는 -개인의 계급에 대한 공격이나 폄하를 포함한 커뮤니티 표준 위반 패턴을 보임. - -**결과**: 커뮤니티와의 모든 종류의 공개적 교류를 영구적으로 제재. - -## 참고 - -이 행동 강령은 [기여자 규약][homepage] 의 2.1 버전을 변형하였습니다. 그 내용은 -[https://www.contributor-covenant.org/ko/version/2/1/code-of-conduct.html][v2.1] -에서 확인할 수 있습니다. - -커뮤니티 영향 지침은 [Mozilla's code of conduct enforcement ladder][Mozilla CoC] -에서 영감을 얻었습니다. - -이 행동 강령에 관련한 일반적인 질문에 대한 대답은 -[https://www.contributor-covenant.org/faq][FAQ]를 참고할 수 있습니다. -번역본은 [https://www.contributor-covenant.org/translations][translations]에서 -볼 수 있습니다. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations diff --git a/ko_doc/CONTIRUBUTING.md b/ko_doc/CONTIRUBUTING.md deleted file mode 100644 index ec3cfb0..0000000 --- a/ko_doc/CONTIRUBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -# 기여하기 -이 문서는 이슈 열기, 풀 리퀘스트(PR) 생성, 리뷰, PR 병합에 이르는 기여 워크플로우를 설명합니다. 이 프로젝트에서 작업할 때는 [행동 강령](./CODE_OF_CONDUCT.md)을 따르시기 바랍니다. -기여해 주셔서 감사합니다. - -## 기여 입문 가이드 -오픈 소스에 처음 기여하시는 분이라면, 첫 기여를 하기 전에 다음 자료들이 도움이 될 수 있습니다: -- [GitHub에서 오픈 소스에 기여할 방법 찾기](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) -- [Git 설정하기](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) -- [GitHub 흐름](https://docs.github.com/en/get-started/using-github/github-flow) -- [풀 리퀘스트로 협업하기](https://docs.github.com/en/github/collaborating-with-pull-requests) - -**첫 풀 리퀘스트를 작업하시나요?** [GitHub에서 오픈 소스 프로젝트에 기여하는 방법](https://kcd.im/pull-request) 무료 시리즈에서 배울 수 있습니다. - -## 이슈 생성하기 -개선 아이디어가 있거나, 누락된 기능 또는 버그를 발견하셨다면, GitHub에서 **Issues -> New issue**를 클릭하여 GitHub 이슈를 생성해주세요. 이슈 템플릿에 버그나 제안된 개선 사항에 대한 자세한 설명을 반드시 작성해 주세요. 필요한 경우 적절한 논거와 스크린샷을 제공해 주세요. -작업할 기존 이슈를 찾으셨다면, 수정사항을 담은 PR을 열어주시기 바랍니다. - -## 풀 리퀘스트 열기 -프로젝트에 직접 기여하고 싶으시다면, 제안된 변경 사항을 담은 풀 리퀘스트를 생성해주세요. 방법은 다음과 같습니다: -1. [저장소를 포크합니다](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#fork-an-example-repository). -2. 포크한 저장소의 로컬 복사본에서 변경 사항을 만듭니다. -3. 변경 사항을 커밋하고 GitHub에 푸시합니다. - -> [!IMPORTANT] -> 포크를 업데이트하는 것을 잊지 마세요. 많은 기여자들이 `master` 브랜치를 기반으로 동일한 내용을 작업할 수 있기 때문에 일부 병합 충돌이 발생할 수 있습니다. 변경 사항을 푸시하기 전에 항상 `master`와 리베이스하는 것을 잊지 말고, 브랜치가 `master`와 충돌이 없는지 확인하세요. 병합 충돌이 발생하면 [병합 충돌 해결하기](https://github.com/skills/resolve-merge-conflicts) 튜토리얼을 읽고 병합 충돌 및 기타 문제를 해결하는 방법을 배우세요. - -4. GitHub에서 풀 리퀘스트를 엽니다. 풀 리퀘스트 템플릿에 제공된 변경 사항에 대한 이유와 설명을 작성하세요. 해당되는 경우 풀 리퀘스트를 기존 이슈와 연결하세요. PR을 제출한 후 프로젝트 관리자의 리뷰를 기다리세요. - -## 리뷰 및 승인 과정 -PR을 제출한 후 리뷰를 기다리세요. 프로젝트 관리자들이 귀하의 변경 사항을 평가하고 [제안된 변경 사항](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request)이나 풀 리퀘스트 댓글을 통해 피드백을 제공할 것입니다. 리뷰 제안 사항과 댓글에 최대한 빨리 대응해 주세요. PR이 좋아 보이면 관리자들이 승인하고 병합합니다. - -## 기여자 -모든 기여는 [Contributors](CONTRIBUTORS.md)에서 인정받습니다. 거기에 자신을 추가하는 것을 잊지 마세요. diff --git a/ko_doc/README_KO.md b/ko_doc/README_KO.md deleted file mode 100644 index 03c15c0..0000000 --- a/ko_doc/README_KO.md +++ /dev/null @@ -1,90 +0,0 @@ -# 어셈블리 프로그래밍 - -[![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa] [![Check Links](https://github.com/0xAX/asm/actions/workflows/link-check.yaml/badge.svg)](https://github.com/0xAX/asm/actions/workflows/link-check.yaml) [![star this repo](https://badgen.net/github/stars/0xAX/asm)](https://github.com/0xAX/asm) [![이 저장소 포크하기](https://badgen.net/github/forks/0xAX/asm)](https://github.com/0xAX/asm/fork) [![기여 환영](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/0xAX/asm/issues) [![PR 환영](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) - -이 저장소는 [assembly](https://en.wikipedia.org/wiki/Assembly_language) 프로그래밍 언어를 소개하는 블로그 포스트들을 포함하고 있습니다. 현재 모든 내용과 예제는 [x86_64](https://en.wikipedia.org/wiki/X86-64) 프로세서와 GNU Linux 운영 체제만을 다루고 있습니다. 향후에는 [ARM64](https://en.wikipedia.org/wiki/AArch64) 아키텍처에 대한 학습 자료도 게시할 계획입니다. - -경험 많은 프로그래머이든 아니든, 이 포스트들은 모든 사람이 어셈블리 프로그래밍 언어를 배울 수 있도록 의도되었습니다. 포스트들은 다음 주제들을 다룹니다: - -- x86_64 프로세서 아키텍처의 기본 설명 -- 어셈블리 프로그래밍 언어로 간단한 프로그램을 작성, 빌드 및 실행하는 방법 -- Linux용 프로그램의 주요 구성 요소 -- 메모리 할당의 기초, 스택과 힙이란 무엇인가 -- 시스템 콜이란 무엇이며 프로그램이 운영 체제와 어떻게 상호 작용하는가 -- 부동 소수점 숫자가 컴퓨터 메모리에서 어떻게 표현되는가 -- C 프로그램에서 어셈블리 코드를 호출하는 방법 -- 그 외 다양한 주제... - -즐겁게 배우세요! - -![Magic](/content/assets/asm-introduction.png) - -각 포스트에 대한 링크는 다음과 같습니다: - - * [Say hello to x86_64 Assembly part 1](https://github.com/0xAX/asm/blob/master/content/asm_1.md) - * [Say hello to x86_64 Assembly part 2](https://github.com/0xAX/asm/blob/master/content/asm_2.md) - * [Say hello to x86_64 Assembly part 3](https://github.com/0xAX/asm/blob/master/content/asm_3.md) - * [Say hello to x86_64 Assembly part 4](https://github.com/0xAX/asm/blob/master/content/asm_4.md) - * [Say hello to x86_64 Assembly part 5](https://github.com/0xAX/asm/blob/master/content/asm_5.md) - * [Say hello to x86_64 Assembly part 6](https://github.com/0xAX/asm/blob/master/content/asm_6.md) - * [Say hello to x86_64 Assembly part 7](https://github.com/0xAX/asm/blob/master/content/asm_7.md) - * [Say hello to x86_64 Assembly part 8](https://github.com/0xAX/asm/blob/master/content/asm_8.md) - -## 요구 사항 - -코드 예제를 실행하려면 다음 도구가 필요합니다: - -- [64bit Linux 배포판](https://en.wikipedia.org/wiki/Linux_distribution) -- [make](https://www.gnu.org/software/make/) -- [NASM](https://nasm.us/) -- [binutils](https://www.gnu.org/software/binutils/) - -## 번역 - -기여자분들 덕분에 어셈블리 프로그래밍에 대한 포스트들이 다양한 언어로 번역되었습니다. - -> [!Note] -> 번역은 원본 내용과 다를 수 있습니다. - -### 중국어 - - * [译文: Say hello to x64 Assembly [part 1]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-1.md) - * [译文: Say hello to x64 Assembly [part 2]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-2.md) - * [译文: Say hello to x64 Assembly [part 3]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-3.md) - * [译文: Say hello to x64 Assembly [part 4]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-4.md) - * [译文: Say hello to x64 Assembly [part 5]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-5.md) - * [译文: Say hello to x64 Assembly [part 6]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-6.md) - * [译文: Say hello to x64 Assembly [part 7]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-7.md) - * [译文: Say hello to x64 Assembly [part 8]](https://github.com/time-river/vvl.me/blob/master/source/_posts/translation-Say-hello-to-x64-Assembly-part-8.md) - -### 튀르키예어 - - * [X86_64 Assembly'a merhaba deyin bölüm 1](https://github.com/furkanonder/asm/blob/master/bolumler/1.md) - * [X86_64 Assembly'a merhaba deyin bölüm 2](https://github.com/furkanonder/asm/blob/master/bolumler/2.md) - * [X86_64 Assembly'a merhaba deyin bölüm 3](https://github.com/furkanonder/asm/blob/master/bolumler/3.md) - * [X86_64 Assembly'a merhaba deyin bölüm 4](https://github.com/furkanonder/asm/blob/master/bolumler/4.md) - * [X86_64 Assembly'a merhaba deyin bölüm 5](https://github.com/furkanonder/asm/blob/master/bolumler/5.md) - * [X86_64 Assembly'a merhaba deyin bölüm 6](https://github.com/furkanonder/asm/blob/master/bolumler/6.md) - * [X86_64 Assembly'a merhaba deyin bölüm 7](https://github.com/furkanonder/asm/blob/master/bolumler/7.md) - * [X86_64 Assembly'a merhaba deyin bölüm 8](https://github.com/furkanonder/asm/blob/master/bolumler/8.md) - -## 기여 - -프로젝트에 기여하는 방법은 [기여 가이드](./CONTRIBUTING.md)를 읽어보세요. 기여할 때는 [행동 강령](./CODE_OF_CONDUCT.md)을 따라주세요. - -## 라이선스 - -저장소의 각 Markdown 파일은 -[크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스][cc-by-nc-sa]에 따라 라이선스가 부여됩니다. - -[![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa] - -[cc-by-nc-sa]: https://creativecommons.org/licenses/by-nc-sa/4.0/ -[cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png -[cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg - -## 저자 - -기술 내용은 [@0xAX](https://x.com/0xAX)가 작성했습니다. - -텍스트 개선에 큰 도움을 준 [@klaudiagrz](https://github.com/klaudiagrz)에게도 감사드립니다. diff --git a/ko_doc/SECURITY.md b/ko_doc/SECURITY.md deleted file mode 100644 index 3661a0f..0000000 --- a/ko_doc/SECURITY.md +++ /dev/null @@ -1,6 +0,0 @@ -## 보안 정보 - -이 저장소는 주로 [C](https://ko.wikipedia.org/wiki/C_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%EC%96%B8%EC%96%B4))와 [어셈블리](https://ko.wikipedia.org/wiki/%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC%EC%96%B4) 프로그래밍 언어를 사용하여 작성된 코드 예제를 포함하고 있습니다. - -이 코드 예제들은 어떠한 악성 소프트웨어도 포함하고 있지 않으며 로컬 머신에서 안전하게 실행할 수 있습니다. -만약 코드 예제들 중 어느 곳에서든 보안 문제가 있다고 생각되면, [이슈](https://github.com/0xAX/asm/issues/new)를 열어 주시기 바랍니다. diff --git a/ko_doc/post/ko_asm_1.md b/ko_doc/post/ko_asm_1.md deleted file mode 100644 index 34f4023..0000000 --- a/ko_doc/post/ko_asm_1.md +++ /dev/null @@ -1,181 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 1] - -## 소개 - -우리 사이에는 많은 개발자들이 있습니다. 우리는 매일 엄청난 양의 코드를 작성합니다. - -때때로 나쁘지 않은 코드도 작성하죠 :) 우리 모두는 다음과 같은 가장 간단한 코드를 쉽게 작성할 수 있습니다: - -```c -#include - -int main() { - int x = 10; - int y = 100; - printf("x + y = %d", x + y); - return 0; -} -``` - -우리 모두 이 C 코드가 무엇을 하는지 이해할 수 있습니다. -하지만... 이 코드가 저수준에서 어떻게 작동하는지 아는 사람은 많지 않을 것입니다. -저도 그랬습니다. - -Haskell, Erlang, Go와 같은 고수준 프로그래밍 언어로 코드를 작성할 수는 있었지만, -이 코드가 컴파일된 후 저수준에서 어떻게 동작하는지 전혀 몰랐습니다. - -그래서 어셈블리 언어로 몇 가지 깊은 단계를 밟아가면서 배운 내용을 설명하기로 했습니다. -이 과정이 저뿐만 아니라 여러분에게도 흥미로울 거라 생각합니다. - -약 5~6년 전, 대학교에서 간단한 프로그램을 작성하기 위해 어셈블리를 사용한 적이 있습니다. -당시에는 Turbo Assembly와 DOS 운영 체제를 사용했습니다. 지금은 Linux-x86-64 운영 체제를 사용하고 있습니다. 그렇다면 Linux 64비트와 DOS 16비트 사이에는 큰 차이가 있을 것입니다. 시작해봅시다. - -## 준비 - -시작하기 전에 몇 가지 준비를 해야 합니다. -앞서 언급했듯이, 저는 Ubuntu (Ubuntu 14.04.1 LTS 64비트)를 사용하고 있으며, 따라서 이 글에서는 이 운영 체제와 아키텍처를 기준으로 설명할 것입니다. - -각 CPU는 서로 다른 명령어 집합을 지원합니다. 저는 Intel Core i7 870 프로세서를 사용하며, 모든 코드는 이 프로세서를 기준으로 작성될 것입니다. -또한 NASM 어셈블리를 사용할 것입니다. NASM을 설치하려면 다음 명령어를 실행하면 됩니다: - -``` -$ sudo apt-get install nasm -``` - -NASM의 버전은 2.0.0 이상이어야 합니다. -저는 2013년 12월 29일에 컴파일된 NASM 2.10.09 버전을 사용하고 있습니다. - -마지막으로 어셈블리 코드를 작성할 텍스트 편집기가 필요합니다. 저는 Emacs와 nasm-mode.el을 사용합니다. -물론, 다른 편집기를 사용해도 상관없습니다. -Emacs를 사용한다면, nasm-mode.el을 다운로드하고 Emacs를 다음과 같이 설정할 수 있습니다: - -```elisp -(load "~/.emacs.d/lisp/nasm.el") -(require 'nasm-mode) -(add-to-list 'auto-mode-alist '("\\.\\(asm\\|s\\)$" . nasm-mode)) -``` -현재로서는 이 정도 준비가 필요합니다. 다른 도구는 다음 게시물에서 설명하겠습니다 - -## NASM 어셈블리 문법 - -여기서는 전체 어셈블리 문법을 설명하지 않을 것이며, 이번 게시물에서 사용할 문법의 일부만을 다룰 것입니다. -일반적으로 NASM 프로그램은 여러 섹션으로 나누어집니다. 이번 게시물에서는 다음 두 섹션을 다룰 것입니다: - -* 데이터 섹션 -* 텍스트 섹션 - -데이터 섹션은 상수를 선언하는 데 사용됩니다. 이 데이터는 실행 중에 변경되지 않습니다. 수학 상수나 기타 상수 등을 선언할 수 있습니다. 데이터 섹션을 선언하는 문법은 다음과 같습니다: - -```assembly - section .data -``` - -텍스트 섹션은 코드에 사용됩니다. 이 섹션은 프로그램의 시작 지점을 정의하는 global _start로 시작해야 합니다. - -```assembly - section .text - global _start - _start: -``` - -주석은 `;` 기호로 시작합니다. 모든 NASM 소스 코드 라인은 다음 네 가지 필드의 조합을 포함합니다: - -``` -[label:] instruction [operands] [; comment] -``` - -대괄호 안의 필드는 선택적입니다. 기본 NASM 명령어는 두 부분으로 구성됩니다. 첫 번째는 실행할 명령어의 이름이고, 두 번째는 이 명령어의 피연산자입니다. 예를 들어: - -```assembly - MOV COUNT, 48 ; Put value 48 in the COUNT variable -``` - -## Hello world - -첫 번째 NASM 어셈블리 프로그램을 작성해 봅시다. 물론, 전통적인 "Hello, World!" 프로그램입니다. 코드 예제는 다음과 같습니다: - -```assembly -section .data - msg db "hello, world!" - -section .text - global _start -_start: - mov rax, 1 - mov rdi, 1 - mov rsi, msg - mov rdx, 13 - syscall - mov rax, 60 - mov rdi, 0 - syscall -``` - -네, printf("Hello world")처럼 보이지는 않습니다. -이 코드가 어떻게 작동하는지 이해해 봅시다. 1~2행을 살펴보세요. 데이터 섹션을 정의하고, 여기에서 "Hello world" 값을 가지는 msg 상수를 정의했습니다. - -이제 이 상수를 코드에서 사용할 수 있습니다. 다음은 텍스트 섹션과 프로그램의 시작 지점을 선언한 것입니다. 프로그램은 7행부터 실행됩니다. 이제 가장 흥미로운 부분이 시작됩니다. -mov 명령어는 두 개의 피연산자를 받아서 두 번째 값을 첫 번째에 넣습니다. 그런데 rax, rdi 등이 무엇인지 궁금할 것입니다. 위키피디아에 따르면: - -``` -중앙 처리 장치(CPU)는 컴퓨터 프로그램의 명령어를 수행하고 시스템의 기본 산술, 논리, 입출력 작업을 수행하는 하드웨어입니다. -``` - -좋습니다, CPU는 어떤 작업을 수행하고 있습니다. -하지만 이 작업을 위한 데이터는 어디에서 가져올까요? 첫 번째 대답은 메모리입니다. -그러나 메모리에서 데이터를 읽고 저장하는 과정은 복잡하기 때문에 CPU는 레지스터라는 자체 내부 메모리 저장 위치를 가지고 있습니다: - -![registers](/content/assets/registers.png) - -따라서 mov rax, 1이라고 쓸 때는 rax 레지스터에 1을 넣는다는 의미입니다. -이제 rax, rdi, rbx 등이 무엇인지 알게 되었습니다. 그러나 언제 rax를 사용하고 언제 rsi를 사용하는지 알아야 합니다. - -* `rax` - 임시 레지스터; syscall을 호출할 때 rax에는 syscall 번호가 있어야 합니다. -* `rdx` - 함수에 3번째 인자를 전달하는 데 사용됩니다. -* `rdi` - 함수에 1번째 인자를 전달하는 데 사용됩니다. -* `rsi` - 함수에 2번째 인자를 전달하는 데 사용되는 포인터입니다. - -다시 말해, 우리는 `sys_write` 시스템 호출을 호출하고 있습니다. `sys_write`는 다음과 같습니다: - -```C -size_t sys_write(unsigned int fd, const char * buf, size_t count); -``` - -이 함수는 3개의 인자를 받습니다: - -* `fd` - 파일 디스크립터. 표준 입력, 표준 출력 및 표준 오류를 위해 각각 0, 1 및 2를 사용할 수 있습니다. -* `buf` - 파일 디스크립터가 가리키는 파일로부터 얻은 내용을 저장할 수 있는 문자 배열을 가리킵니다. -* `count` - 파일에서 문자 배열로 쓰여질 바이트 수를 지정합니다. - -`sys_write` 시스템 호출이 3개의 인자를 받으며, syscall 테이블에서 1번 번호를 가진다는 것을 알았습니다. -이제 우리의 "Hello world" 구현을 다시 살펴보겠습니다. rax 레지스터에 1을 넣으면 `sys_write` 시스템 호출을 사용할 것이라는 의미입니다. - -다음 줄에서는 rdi 레지스터에 1을 넣습니다. 이는 sys_write의 첫 번째 인자인 표준 출력(1)을 의미합니다. -다음으로 msg에 대한 포인터를 rsi 레지스터에 저장합니다. - -이는 sys_write의 두 번째 인자인 버퍼를 의미합니다. -그리고 문자열의 길이(13)를 rdx에 전달하여 sys_write의 세 번째 인자로 사용합니다. 이제 `sys_write`의 모든 인자를 설정했으므로, 11행에서 syscall 명령어로 호출합니다. - -"Hello world" 문자열을 출력한 후, 프로그램을 올바르게 종료해야 합니다. -rax 레지스터에 60을 넣습니다. 60은 종료 시스템 호출 번호입니다. - -또한 rdi 레지스터에 0을 넣어 오류 코드를 설정합니다. 0은 성공적인 종료를 의미합니다. -이것으로 "Hello world" 프로그램이 완료됩니다. 꽤 간단하죠 :) 이제 프로그램을 빌드해 보겠습니다. - -예를 들어, 이 코드를 hello.asm 파일에 코드를 작성했다고 가정합시다. -그러면 다음 명령어를 실행하여 프로그램을 빌드할 수 있습니다: - -``` -$ nasm -f elf64 -o hello.o hello.asm -$ ld -o hello hello.o -``` - -이후, ./hello를 실행하면 터미널에서 "Hello world" 문자열이 출력됩니다. - -## 옮긴이의 말 - -이 튜토리얼을 번역하며 원문의 기술적 정확성을 유지하면서도 내용을 보다 쉽게 전달하려고 노력했습니다. -어셈블리 언어는 저수준에서 컴퓨터가 어떻게 동작하는지 이해하는 데 큰 도움이 되지만, 그만큼 도전적인 부분도 많습니다. - -이 번역이 x86_64 어셈블리 언어를 이해하고자 하는 분들에게 유용한 가이드가 되기를 바랍니다. -번역 과정에서 발생한 오류나 부정확한 부분에 대한 피드백은 언제든 환영합니다. 이 튜토리얼을 읽어주셔서 감사합니다. diff --git a/ko_doc/post/ko_asm_2.md b/ko_doc/post/ko_asm_2.md deleted file mode 100644 index bad3d90..0000000 --- a/ko_doc/post/ko_asm_2.md +++ /dev/null @@ -1,246 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 2] - -며칠 전에 첫 블로그 포스트인 "x86_x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 1]"을 작성했는데, 예상외로 큰 관심을 받았습니다: - -![newscombinator](/content/assets/newscombinator-screenshot.png) -![reddit](/content/assets/reddit-screenshot.png) - -이것은 저를 더욱 학습하는 데 동기 부여를 해주었습니다. -그동안 많은 사람들로부터 피드백을 받았고, 감사의 말을 많이 들었습니다. -하지만 저에게 더 중요한 것은 많은 조언과 적절한 비판이었습니다. 특히 훌륭한 피드백을 주신 분들에게 감사의 말씀을 전하고 싶습니다: - -* [Fiennes](https://reddit.com/user/Fiennes) -* [Grienders](https://disqus.com/by/Universal178/) -* [nkurz](https://news.ycombinator.com/user?id=nkurz) - -그리고 Reddit과 Hacker News에서 토론에 참여한 모든 분들께도 감사합니다. -첫 번째 파트가 절대 초보자에게는 다소 명확하지 않았다는 의견이 많아서, 더 많은 정보를 담은 포스트를 작성하기로 결정했습니다. -자, 이제 x86_64 어셈블리와 친해지기 [파트 2]를 시작해 보겠습니다. - -## 용어 및 개념 - -위에서 언급했듯이, 첫 번째 포스트의 일부 부분이 명확하지 않다는 피드백을 많이 받았습니다. 그래서 이번 포스트부터는 우리가 이번과 다음 포스트에서 볼 수 있는 몇 가지 용어를 설명하려고 합니다. - -레지스터 - 레지스터는 프로세서 내부의 소량의 저장소입니다. 프로세서의 주요 역할은 데이터 처리입니다. 프로세서는 메모리에서 데이터를 가져올 수 있지만, 이는 느린 작업입니다. 그래서 프로세서에는 레지스터라는 내부 제한된 데이터 저장소가 있습니다. - -리틀 엔디안 - 메모리를 하나의 큰 배열로 상상할 수 있습니다. 이 배열은 바이트를 포함합니다. 각 주소는 메모리 배열의 한 요소를 저장합니다. 각 요소는 하나의 바이트입니다. 예를 들어, 4바이트가 AA 56 AB FF로 주어졌을 때, 리틀 엔디안에서는 가장 낮은 주소에 가장 낮은 유의 바이트가 저장됩니다: - -``` - 0 FF - 1 AB - 2 56 - 3 AA -``` - - -여기서 0, 1, 2, 3은 메모리 주소입니다. - -빅 엔디안 - 빅 엔디안은 리틀 엔디안과 반대 순서로 바이트를 저장합니다. 따라서 AA 56 AB FF 바이트 시퀀스는 다음과 같습니다: - -``` - 0 AA - 1 56 - 2 AB - 3 FF -``` - -시스템 호출 (Syscall) - 사용자 수준 프로그램이 운영 체제에게 작업을 요청하는 방법입니다. 시스템 호출 테이블은 [여기](https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl)에서 찾을 수 있습니다. - -스택 (Stack) - 프로세서는 매우 제한된 수의 레지스터를 가지고 있습니다. 따라서 스택은 연속적인 메모리 영역으로, `RSP`, `SS`, `RIP`와 같은 특수 레지스터로 주소 지정됩니다. 스택에 대해서는 다음 포스트에서 자세히 살펴보겠습니다. - -섹션 (Section) - 모든 어셈블리 프로그램은 섹션으로 나뉩니다. 다음과 같은 섹션이 있습니다: - -* `data` - 초기화된 데이터 또는 상수를 선언하는 데 사용됩니다. -* `bss` - 초기화되지 않은 변수를 선언하는 데 사용됩니다. -* `text` - 코드에 사용됩니다. - -범용 레지스터 (General-purpose registers) - 총 16개의 범용 레지스터가 있습니다 - rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15. 물론, 어셈블리 프로그래밍과 관련된 용어와 개념은 이 리스트에 포함되지 않은 것들이 많습니다. 다음 블로그 포스트에서 낯선 단어를 만나게 되면, 그에 대한 설명이 있을 것입니다. - -## 데이터 타입 - -기본 데이터 타입은 바이트, 워드, 더블워드, 쿼드워드, 더블 쿼드워드입니다. 바이트는 8비트, 워드는 2바이트, 더블워드는 4바이트, 쿼드워드는 8바이트, 더블 쿼드워드는 16바이트 (128비트)입니다. - -현재는 정수 숫자만 다룰 것이므로, 이에 대해 살펴보겠습니다. 정수에는 부호 없는 정수와 부호 있는 정수가 있습니다. 부호 없는 정수는 바이트, 워드, 더블워드, 쿼드워드에 저장된 부호 없는 이진 숫자입니다. 그 값의 범위는 부호 없는 바이트 정수의 경우 0부터 255까지, 부호 없는 워드 정수의 경우 0부터 65,535까지, 부호 없는 더블워드 정수의 경우 0부터 2^32 – 1까지, 부호 없는 쿼드워드 정수의 경우 0부터 2^64 – 1까지입니다. 부호 있는 정수는 부호가 있는 이진 숫자이며, 부호 없는 바이트, 워드 등에 저장됩니다. 부호 비트는 음수 정수에 대해 설정되고 양수 정수와 0에 대해 지워집니다. 정수 값의 범위는 바이트 정수의 경우 -128부터 +127까지, 워드 정수의 경우 -32,768부터 +32,767까지, 더블워드 정수의 경우 -2^31부터 +2^31 – 1까지, 쿼드워드 정수의 경우 -2^63부터 +2^63 – 1까지입니다. - -## 섹션 - -앞서 언급했듯이, 모든 어셈블리 프로그램은 섹션으로 나뉩니다. 데이터 섹션, 텍스트 섹션, bss 섹션 등이 있습니다. 데이터 섹션을 살펴보겠습니다. 이 섹션의 주요 목적은 초기화된 상수를 선언하는 것입니다. 예를 들면: - -```assembly -section .data - num1: equ 100 - num2: equ 50 - msg: db "Sum is correct", 10 -``` - -여기까지 거의 모든 것이 명확합니다. `num1`, `num2`, `msg`라는 이름의 3개의 상수와 각각의 값으로 100, 50, "Sum is correct", 10이 정의되었습니다. 그런데 `db`, `equ`는 무엇일까요? 실제 NASM은 여러 가지 의사 명령어를 지원합니다: - -* DB, DW, DD, DQ, DT, DO, DY, DZ - 초기화된 데이터를 선언하는 데 사용됩니다. 예를 들어: - -```assembly -;; 4바이트 1h, 2h, 3h, 4h 초기화 -db 0x01,0x02,0x03,0x04 - -;; 0x12 0x34로 워드 초기화 -dw 0x1234 -``` - -* RESB, RESW, RESD, RESQ, REST, RESO, RESY, RESZ - 초기화되지 않은 변수를 선언하는 데 사용됩니다. -* INCBIN - 외부 이진 파일 포함 -* EQU - 상수를 정의합니다. 예를 들면: - -```assembly -;; now one is 1 -one equ 1 -``` - -이 글에서는 그 중 일부를 실제로 살펴볼 것입니다. 다른 내용은 다음 게시물에서 다룰 예정입니다. - -## 제어 흐름 - -일반적으로 프로그래밍 언어는 평가 순서를 변경할 수 있는 기능을 가지고 있습니다 (if 문, case 문, goto 등). 어셈블리에서도 이러한 기능이 있습니다. cmp 명령어는 두 값 간의 비교를 수행합니다. 이 명령어는 조건부 점프 명령어와 함께 사용되어 결정적인 동작을 수행합니다. 예를 들어: - -```assembly -;; compare rax with 50 -cmp rax, 50 -``` - -`cmp` 명령어는 단순히 두 값을 비교할 뿐, 그 값들에 영향을 주거나 비교 결과에 따라 어떤 것도 실행하지 않습니다. 비교 후 어떤 동작을 수행하려면 조건부 점프 명령어를 사용합니다. 다음과 같은 명령어들이 있습니다: - -* `JE` - 같으면 -* `JZ` - 0이면 -* `JNE` - 같지 않으면 -* `JNZ` - 0이 아니면 -* `JG` - 첫 번째 피연산자가 두 번째보다 크면 -* `JGE` - 첫 번째 피연산자가 두 번째보다 크거나 같으면 -* `JA` - JG와 동일하지만 부호 없는 비교 수행 -* `JAE` - JGE와 동일하지만 부호 없는 비교 수행 - -예를 들어, C에서의 if/else 문과 비슷한 것을 구현하고 싶다면: - -```C -if (rax != 50) { - exit(); -} else { - right(); -} -``` - -# 어셈블리 구문 비교와 점프 - -`cmp` 명령어는 단순히 두 값을 비교할 뿐, 그 값들에 영향을 주거나 비교 결과에 따라 어떤 것도 실행하지 않습니다. 비교 후 어떤 동작을 수행하려면 조건부 점프 명령어를 사용합니다. 다음과 같은 명령어들이 있습니다: - -* `JE` - 같으면 -* `JZ` - 0이면 -* `JNE` - 같지 않으면 -* `JNZ` - 0이 아니면 -* `JG` - 첫 번째 피연산자가 두 번째보다 크면 -* `JGE` - 첫 번째 피연산자가 두 번째보다 크거나 같으면 -* `JA` - JG와 동일하지만 부호 없는 비교 수행 -* `JAE` - JGE와 동일하지만 부호 없는 비교 수행 - -예를 들어, C에서의 if/else 문과 비슷한 것을 구현하고 싶다면 어셈블리에서는 다음과 같이 됩니다: - -```assembly -;; rax를 50과 비교 -cmp rax, 50 -;; rax가 50과 같지 않으면 .exit 수행 -jne .exit -jmp .right -``` - -또한 무조건 점프 구문도 있습니다: - -```assembly -JMP label -``` - -예를 들면: - -```assembly -_start: - ;; .... - ;; 작업을 수행하고 .exit 레이블로 이동합니다. - ;; .... - jmp .exit - -.exit: - mov rax, 60 - mov rdi, 0 - syscall -``` - -여기서 우리는 _start 레이블 다음에 일부 코드를 가질 수 있고, 이 모든 코드는 실행될 것입니다. 어셈블리는 제어를 .exit 레이블로 이전하고, .exit: 다음의 코드가 실행되기 시작할 것입니다. -무조건 점프는 종종 반복문에서 사용됩니다. 예를 들어 우리는 레이블과 그 뒤에 일부 코드를 가집니다. -이 코드는 무언가를 실행하고, 그 다음 조건이 있고 조건이 성공적이지 않으면 이 코드의 시작으로 점프합니다. 반복문은 다음 파트에서 다룰 것입니다. - -## 예제 - -간단한 예제를 봅시다. 두 개의 정수를 받아, 이 숫자들의 합을 구하고 미리 정의된 숫자와 비교합니다. -미리 정의된 숫자가 합과 같으면 화면에 무언가를 출력하고, 그렇지 않으면 그냥 종료합니다. 여기 우리 예제의 소스 코드가 있습니다: - -```assembly -section .data - ; 상수 정의 - num1: equ 100 - num2: equ 50 - ; 메시지 초기화 - msg: db "Sum is correct\n" - -section .text - - global _start - -;; 진입점 -_start: - ; num1의 값을 rax에 설정 - mov rax, num1 - ; num2의 값을 rbx에 설정 - mov rbx, num2 - ; rax와 rbx의 합을 계산하고, 그 값을 rax에 저장 - add rax, rbx - ; rax와 150 비교 - cmp rax, 150 - ; rax와 150이 같지 않으면 .exit 레이블로 이동 - jne .exit - ; rax와 150이 같으면 .rightSum 레이블로 이동 - jmp .rightSum - -; 합이 맞다는 메시지 출력 -.rightSum: - ;; write 시스템 호출 - mov rax, 1 - ;; 파일 디스크립터, 표준 출력 - mov rdi, 1 - ;; 메시지 주소 - mov rsi, msg - ;; 메시지 길이 - mov rdx, 15 - ;; write 시스템 호출 호출 - syscall - ; 프로그램 종료 - jmp .exit - -; 종료 절차 -.exit: - ; exit 시스템 호출 - mov rax, 60 - ; 종료 코드 - mov rdi, 0 - ; exit 시스템 호출 호출 - syscall -``` - -소스 코드를 살펴봅시다. 우선 데이터 섹션에 두 개의 상수 num1, num2와 "Sum is correct\n" 값을 가진 변수 msg가 있습니다. 이제 14번째 줄을 보세요. -여기서 프로그램의 진입점이 시작됩니다. num1과 num2 값을 범용 레지스터 rax와 rbx로 전송합니다. -add 명령어로 이들을 더합니다. add 명령어 실행 후, rax와 rbx의 값을 더하고 그 결과를 rax에 저장합니다. -이제 rax 레지스터에 num1과 num2의 합이 있습니다. - -좋습니다. num1은 100이고 num2는 50입니다. 우리의 합은 150이 되어야 합니다. -cmp 명령어로 이를 확인해 봅시다. rax와 150을 비교한 후 비교 결과를 확인합니다. -rax와 150이 같지 않으면(jne로 확인) .exit 레이블로 갑니다. 같다면 .rightSum 레이블로 갑니다. - -이제 두 개의 레이블이 있습니다: .exit와 .rightSum. 첫 번째는 단순히 rax에 60을 설정합니다. -이는 exit 시스템 콜 번호이며, rdi에는 0을 설정합니다. 이는 종료 코드입니다. -두 번째인 .rightSum은 매우 간단합니다. "Sum is correct"를 출력할 뿐입니다. - diff --git a/ko_doc/post/ko_asm_3.md b/ko_doc/post/ko_asm_3.md deleted file mode 100644 index d6a2e24..0000000 --- a/ko_doc/post/ko_asm_3.md +++ /dev/null @@ -1,286 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 3] - -스택은 LIFO(Last In, First Out) 원칙으로 작동하는 메모리의 특별한 영역입니다. - -임시 데이터 저장을 위해 16개의 범용 레지스터가 있습니다. -RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP 및 R8-R15입니다. - -이는 심각한 애플리케이션에는 너무 적습니다. 따라서 스택에 데이터를 저장할 수 있습니다. -스택의 또 다른 용도는 다음과 같습니다: 함수를 호출할 때 반환 주소가 스택에 복사됩니다. -함수 실행이 끝난 후, 주소가 명령 카운터(RIP)에 복사되고 애플리케이션은 함수 다음 위치에서 계속 실행됩니다. - -예를 들어: - -```assembly -global _start - -section .text - -_start: - mov rax, 1 - call incRax - cmp rax, 2 - jne exit - ;; - ;; Do something - ;; - -incRax: - inc rax - ret -``` - -여기서 애플리케이션 실행 후 rax는 1과 같습니다. -그 다음 rax 값을 1 증가시키는 incRax 함수를 호출하므로 rax 값은 2가 되어야 합니다. -이후 실행은 8번째 줄에서 계속되며, 여기서 rax 값을 2와 비교합니다. - -또한 System V AMD64 ABI에서 읽을 수 있듯이, 처음 6개의 함수 인수는 레지스터로 전달됩니다. 이들은: - -* rdi - 첫 번째 인수 -* rsi - 두 번째 인수 -* rdx - 세 번째 인수 -* rcx - 네 번째 인수 -* r8 - 다섯 번째 인수 -* r9 - 여섯 번째 인수 - -그 다음 인수들은 스택으로 전달됩니다. 따라서 다음과 같은 함수가 있다면: - -```C -int foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7) -{ - return (a1 + a2 - a3 - a4 + a5 - a6) * a7; -} -``` - -처음 6개의 인수는 레지스터로 전달되고, 7번째 인수부터는 스택으로 전달됩니다. - -## 스택 포인터 - -앞서 말한대로 16개의 범용 레지스터가 있고, 그 중 RSP와 RBP 두 개의 흥미로운 레지스터가 있습니다. -RBP는 베이스 포인터 레지스터로, 현재 스택 프레임의 베이스를 가리킵니다. RSP는 스택 포인터로, 현재 스택 프레임의 최상단을 가리킵니다. - -## 명령어 - -스택 작업을 위한 두 가지 명령어가 있습니다: - -* `push argument` - 스택 포인터(RSP)를 증가시키고 스택 포인터가 가리키는 위치에 인수를 저장합니다. -* `pop argument` - 스택 포인터가 가리키는 위치에서 인자로 데이터를 복사합니다. - -간단한 예제를 살펴보겠습니다: - -```assembly -global _start - -section .text - -_start: - mov rax, 1 - mov rdx, 2 - push rax - push rdx - - mov rax, [rsp + 8] - - ;; - ;; Do something - ;; -``` - -여기서 우리는 rax 레지스터에 1을, rdx 레지스터에 2를 넣는 것을 볼 수 있습니다. -그 후 이 레지스터들의 값을 스택에 push합니다. 스택은 LIFO(Last In First Out) 방식으로 작동합니다. -따라서 이후 우리 애플리케이션의 스택은 다음과 같은 구조를 가지게 됩니다: - -![stack diagram](/content/assets/stack-diagram.png) - -그 다음 우리는 rsp + 8 주소를 가진 스택에서 값을 복사합니다. -이는 스택의 최상단 주소를 가져와 여기에 8을 더하고, 이 주소에 있는 데이터를 rax로 복사한다는 의미입니다. 이 후 rax 값은 1이 될 것입니다. - -## 예제 - -한 가지 예를 살펴보겠습니다. 두 개의 명령줄 인수를 받는 간단한 프로그램을 작성해 보겠습니다. 이 인수의 합을 구하고 결과를 출력합니다. - -```assembly -section .data - SYS_WRITE equ 1 - STD_IN equ 1 - SYS_EXIT equ 60 - EXIT_CODE equ 0 - - NEW_LINE db 0xa - WRONG_ARGC db "Must be two command line argument", 0xa -``` - -먼저 몇 가지 값으로 `.data` 섹션을 정의합니다. -여기에는 리눅스 시스템 호출에 대한 네 가지 상수, sys_write, sys_exit 등이 있습니다. -그리고 두 개의 문자열도 있습니다: 첫 번째는 새 줄 기호이고 두 번째는 오류 메시지입니다. - -.text 섹션을 살펴보겠습니다. 이 섹션은 프로그램의 코드로 구성되어 있습니다: - -```assembly -section .text - global _start - -_start: - pop rcx - cmp rcx, 3 - jne argcError - - add rsp, 8 - pop rsi - call str_to_int - - mov r10, rax - pop rsi - call str_to_int - mov r11, rax - - add r10, r11 -``` - -여기서 무슨 일이 일어나는지 이해해 봅시다: -_start 레이블 이후 첫 번째 명령어는 스택에서 첫 번째 값을 가져와 rcx 레지스터에 넣습니다. -명령줄 인수와 함께 애플리케이션을 실행하면 실행 후 모든 인수가 다음 순서로 스택에 있게 됩니다: - -``` - [rsp] - 스택의 맨 위에는 인수 개수가 포함됩니다. - [rsp + 8] - argv[0]이 포함됩니다. - [rsp + 16] - argv[1]이 포함됩니다. - 계속 이런 식으로... -``` - -따라서 우리는 명령줄 인수 개수를 가져와 rcx에 넣습니다. -그 후 rcx를 3과 비교합니다. 만약 같지 않다면 argcError 레이블로 점프합니다. -이 레이블은 단순히 오류 메시지를 출력합니다: - -```assembly -argcError: - ;; sys_write 시스템 콜 - mov rax, 1 - ;; 파일 디스크립터, 표준 출력 - mov rdi, 1 - ;; 메시지 주소 - mov rsi, WRONG_ARGC - ;; 메시지 길이 - mov rdx, 34 - ;; write 시스템 콜 호출 - syscall - ;; 프로그램 종료 - jmp exit -``` - -여기서 우리는 명령줄 인수의 합을 rax 레지스터에 넣고, r12를 0으로 설정한 후 int_to_str로 점프합니다. -이제 우리 프로그램의 기본 구조가 완성되었습니다. -우리는 이미 문자열을 출력하는 방법을 알고 있고 출력할 내용도 있습니다. -str_to_int와 int_to_str의 구현을 살펴보겠습니다. - -```assembly -str_to_int: - xor rax, rax - mov rcx, 10 -next: - cmp [rsi], byte 0 - je return_str - mov bl, [rsi] - sub bl, 48 - mul rcx - add rax, rbx - inc rsi - jmp next - -return_str: - ret -``` - -str_to_int의 시작에서 우리는 rax를 0으로, rcx를 10으로 설정합니다. -그 다음 next 레이블로 갑니다. 위의 예시에서 볼 수 있듯이(str_to_int의 첫 번째 호출 전 첫 줄) 우리는 스택에서 argv[1]을 rsi에 넣습니다. -이제 rsi의 첫 번째 바이트를 0과 비교합니다. 모든 문자열은 NULL 심볼로 끝나기 때문이며, 만약 0이라면 반환합니다. - -0이 아니라면 그 값을 1바이트 bl 레지스터에 복사하고 48을 뺍니다. -왜 48인가요? 0부터 9까지의 모든 숫자는 ASCII 테이블에서 48부터 57까지의 코드를 가집니다. - -따라서 숫자 심볼에서 48을 빼면(예를 들어 57에서) 우리는 숫자를 얻게 됩니다. 그 다음 rax를 rcx(값은 10)와 곱합니다. 이후 rsi를 증가시켜 다음 바이트를 얻고 다시 루프합니다. -알고리즘은 간단합니다. 예를 들어 rsi가 '5' '7' '6' '\000' 시퀀스를 가리킨다면 다음과 같은 단계를 거칩니다: - -``` - rax = 0 - 첫 번째 바이트 5를 가져와 rbx에 넣음 - rax * 10 --> rax = 0 * 10 - rax = rax + rbx = 0 + 5 - 두 번째 바이트 7을 가져와 rbx에 넣음 - rax * 10 --> rax = 5 * 10 = 50 - rax = rax + rbx = 50 + 7 = 57 - rsi가 \000이 될 때까지 반복 -``` - -str_to_int 후에는 rax에 숫자가 들어있게 됩니다. 이제 int_to_str을 살펴보겠습니다: - -```assembly -int_to_str: - mov rdx, 0 - mov rbx, 10 - div rbx - add rdx, 48 - add rdx, 0x0 - push rdx - inc r12 - cmp rax, 0x0 - jne int_to_str - jmp print -``` - -여기서 우리는 rdx에 0을, rbx에 10을 넣습니다. -그 다음 div rbx를 실행합니다. str_to_int 호출 전의 코드를 보면, rax에는 두 명령줄 인수의 합인 정수가 들어있습니다. -이 명령어로 rax 값을 rbx 값으로 나누고 나머지를 rdx에, 몫을 rax에 얻습니다. - -다음으로 rdx에 48과 0x0을 더합니다. 48을 더하면 이 숫자의 ASCII 심볼을 얻게 되고, 모든 문자열은 0x0으로 끝나야 합니다. - -이후 심볼을 스택에 저장하고, r12를 증가시킵니다(첫 반복에서는 0입니다, _start에서 0으로 설정했습니다). 그리고 rax를 0과 비교합니다. -0이면 정수를 문자열로 변환이 끝났다는 뜻입니다. 알고리즘을 단계별로 살펴보겠습니다. 예를 들어 숫자 23이 있다면: - -``` - 123 / 10. rax = 12; rdx = 3 - rdx + 48 = "3" - "3"을 스택에 push - rax를 0과 비교, 아니면 다시 반복 - 12 / 10. rax = 1; rdx = 2 - rdx + 48 = "2" - "2"를 스택에 push - rax를 0과 비교, 맞으면 함수 실행을 종료하고 스택에 "2" "3" ... 이 있게 됩니다 -``` - -우리는 정수를 문자열로, 그리고 그 반대로 변환하는 두 가지 유용한 함수 `int_to_str`과 `str_to_int`를 구현했습니다. -이제 우리는 문자열로 변환되어 스택에 저장된 두 정수의 합을 가지고 있습니다. 이제 결과를 출력할 수 있습니다. - -```assembly -print: - ;;;; 숫자 길이 계산 - mov rax, 1 - mul r12 - mov r12, 8 - mul r12 - mov rdx, rax - ;;;; 합 출력 - mov rax, SYS_WRITE - mov rdi, STD_IN - mov rsi, rsp - ;; sys_write 호출 - syscall - jmp exit -``` -우리는 이미 `sys_write` 시스템 콜로 문자열을 출력하는 방법을 알고 있지만, 여기에 한 가지 흥미로운 부분이 있습니다. -우리는 문자열의 길이를 계산해야 합니다. `int_to_str`을 보면, 매 반복마다 r12 레지스터를 증가시키는 것을 볼 수 있습니다. - -따라서 r12에는 우리 숫자의 자릿수가 들어있습니다. 우리는 이를 8로 곱해야 합니다(각 심볼을 스택에 push했기 때문에), 그러면 출력해야 할 문자열의 길이가 됩니다. -이후 항상 그랬듯이 rax에 1(sys_write 번호), rdi에 1(stdin), rdx에 문자열 길이, rsi에 스택의 최상단 포인터(문자열의 시작)를 넣습니다. - -그리고 프로그램을 종료합니다: - -```assembly -exit: - mov rax, SYS_EXIT - 종료 코드 - mov rdi, EXIT_CODE - syscall -``` - -이것으로 "x86_64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 3]를 마칩니다. diff --git a/ko_doc/post/ko_asm_4.md b/ko_doc/post/ko_asm_4.md deleted file mode 100644 index d00c202..0000000 --- a/ko_doc/post/ko_asm_4.md +++ /dev/null @@ -1,230 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 4] - -얼마 전 x86_64 어셈블리 프로그래밍에 관한 블로그 포스트 시리즈를 작성하기 시작했습니다. 관련 포스트는 `asm` 태그로 찾을 수 있습니다. -최근에는 바빠서 새로운 포스트가 없었지만, 오늘부터 다시 어셈블리에 대한 포스트를 작성할 예정이며, 매주 포스트를 시도할 것입니다. - -오늘은 문자열과 문자열 작업에 대해 살펴보겠습니다. 여전히 NASM 어셈블러와 Linux x86_64를 사용할 것입니다. - -## 문자열 뒤집기 - -어셈블리 프로그래밍 언어에 대해 이야기할 때 문자열 데이터 타입에 대해 논의할 수 없습니다. -실제로 우리는 바이트 배열을 다루고 있습니다. 간단한 예제를 작성해 보겠습니다. - -문자열 데이터를 정의하고 이를 뒤집어 결과를 표준 출력에 작성하는 작업을 시도할 것입니다. -이 작업은 새로운 프로그래밍 언어를 배우기 시작할 때 일반적으로 접하게 되는 간단하고 인기 있는 과제입니다. -구현을 살펴보겠습니다. - -우선, 초기화된 데이터를 정의합니다. 이는 데이터 섹션에 배치될 것입니다 (섹션에 대한 내용은 이전 포스트에서 읽어보세요): - -```assembly -section .data - SYS_WRITE equ 1 - STD_OUT equ 1 - SYS_EXIT equ 60 - EXIT_CODE equ 0 - - NEW_LINE db 0xa - INPUT db "Hello world!" -``` - -여기서 네 가지 상수를 볼 수 있습니다: - -* `SYS_WRITE` - 'write' 시스템 호출 번호 -* `STD_OUT` - 표준 출력 파일 디스크립터 -* `SYS_EXIT` - 'exit' 시스템 호출 번호 -* `EXIT_CODE` - 종료 코드 - -시스템 호출 목록은 여기에서 확인할 수 있습니다. 또한 다음이 정의되어 있습니다: - -* `NEW_LINE` - 새 줄 (\n) 기호 -* `INPUT` - 우리가 뒤집을 입력 문자열 - -다음으로, 문자열을 뒤집어 넣을 버퍼를 위한 bss 섹션을 정의합니다: - -```assembly -section .bss - OUTPUT resb 12 -``` - -좋아요 이제 데이터와 결과를 넣을 버퍼를 준비했으므로, 코드를 위한 text 섹션을 정의할 수 있습니다. - -_start 루틴부터 시작해 보겠습니다: - -```assembly -_start: - mov rsi, INPUT - xor rcx, rcx - cld - mov rdi, $ + 15 - call calculateStrLength - xor rax, rax - xor rdi, rdi - jmp reverseStr -``` - -여기서 새로운 부분이 있습니다. 어떻게 작동하는지 살펴보겠습니다: -우선, 2행에서 INPUT 주소를 rsi 레지스터에 넣습니다. 표준 출력으로 쓰는 것과 마찬가지로, rcx 레지스터를 0으로 초기화합니다. - -rcx는 문자열의 길이를 계산하는 카운터 역할을 합니다. 4행에서 cld 명령어를 볼 수 있습니다. 이 명령어는 df 플래그를 0으로 설정합니다. -이는 문자열의 길이를 계산할 때 문자열의 기호를 왼쪽에서 오른쪽으로 처리하기 위해 필요합니다. 다음으로 calculateStrLength 함수를 호출합니다. - -5행의 mov rdi, $ + 15 명령어를 누락했는데, -이에 대해 조금 이따 설명하겠습니다. 이제 calculateStrLength 구현을 살펴보겠습니다: - -```assembly -calculateStrLength: - ;; 문자열의 끝인지 확인 - cmp byte [rsi], 0 - ;; 문자열의 끝이라면 함수 종료 - je exitFromRoutine - ;; rsi에서 바이트를 al로 로드하고 rsi를 증가 - lodsb - ;; 심볼을 스택에 푸시 - push rax - ;; 카운터 증가 - inc rcx - ;; 다시 루프 - jmp calculateStrLength -``` - -이제 calculateStrLength 함수에서 문자열을 스택에 푸시한 후, 어떻게 _start로 돌아가는지 살펴보겠습니다. -이 작업을 수행하려면 ret 명령어를 사용할 수 있습니다. -그러나 다음과 같은 코드가 있을 경우 문제가 발생할 수 있습니다: - -```assembly -exitFromRoutine: - ;; return to _start - ret -``` - -이 코드가 작동하지 않는 이유는 무엇일까요? 이는 조금 복잡합니다. -함수가 호출될 때, 함수의 매개변수는 오른쪽에서 왼쪽으로 스택에 푸시됩니다. - -그런 다음, 반환 주소가 스택에 푸시되어 함수가 끝난 후 어디로 돌아가야 하는지 알 수 있습니다. -그러나 calculateStrLength 함수에서 문자열의 각 문자를 스택에 푸시하였고, 이제 스택의 최상단에 반환 주소가 없기 때문에 함수가 어디로 돌아가야 하는지 알 수 없습니다. - -```assembly - mov rdi, $ + 15 -``` - -이 문제를 해결하기 위해, 코드에서 다음과 같은 명령어를 살펴보겠습니다: - -여기서: - -* `$` - 현재 위치를 반환합니다. -* `$$` - 현재 섹션의 시작 위치를 반환합니다. - -mov rdi, $ + 15는 현재 위치에서 15바이트를 더한 위치를 반환합니다. -이는 calculateStrLength 호출 이후에 실행될 코드의 주소를 저장하기 위함입니다. - -구체적으로, mov rdi, $ + 15 명령어는 calculateStrLength 호출 이후의 주소를 rdi 레지스터에 저장합니다. - -이제 objdump 유틸리티를 사용하여 reverse 파일을 열어보겠습니다: - -```assembly -objdump -D reverse - -reverse: file format elf64-x86-64 - -Disassembly of section .text: - -00000000004000b0 <_start>: - 4000b0: 48 be 41 01 60 00 00 movabs $0x600141,%rsi - 4000b7: 00 00 00 - 4000ba: 48 31 c9 xor %rcx,%rcx - 4000bd: fc cld - 4000be: 48 bf cd 00 40 00 00 movabs $0x4000cd,%rdi - 4000c5: 00 00 00 - 4000c8: e8 08 00 00 00 callq 4000d5 - 4000cd: 48 31 c0 xor %rax,%rax - 4000d0: 48 31 ff xor %rdi,%rdi - 4000d3: eb 0e jmp 4000e3 -``` - -우리는 이제 반환 주소가 스택에 올바르게 푸시되고 함수가 반환되는 방법을 이해했습니다. -이렇게 하면 _start로 정확하게 돌아올 수 있습니다. calculateStrLength 함수 호출 후, rax와 rdi를 0으로 초기화하고 reverseStr 레이블로 점프합니다. - -이제 reverseStr 레이블의 구현을 살펴보겠습니다: - -```assembly -exitFromRoutine: - ;; 반환 주소를 스택에 다시 푸시 - push rdi - ;; _start로 반환 - ret -``` - -이제 우리는 _start로 돌아옵니다. calculateStrLength를 호출한 후, rax와 rdi에 0을 저장하고 reverseStr 레이블로 점프합니다. - -reverseStr의 구현은 다음과 같습니다: - -```assembly -reverseStr: - cmp rcx, 0 - je printResult - pop rax - mov [OUTPUT + rdi], rax - dec rcx - inc rdi - jmp reverseStr -``` - -여기서는 문자열의 길이를 나타내는 카운터를 확인하고, 카운터가 0이 되면 모든 기호를 버퍼에 썼으므로 이를 출력할 수 있습니다. -카운터를 확인한 후, 스택에서 rax 레지스터로 첫 번째 기호를 팝하여 OUTPUT 버퍼에 씁니다. - -rdi를 추가하여 기호가 버퍼의 첫 번째 바이트에 쓰이지 않도록 합니다. 그 후 rdi를 증가시켜 OUTPUT 버퍼의 다음 위치로 이동하고, -길이 카운터를 감소시킨 후 레이블의 시작으로 점프합니다. - -reverseStr가 실행된 후에는 OUTPUT 버퍼에 문자열이 역순으로 저장되어 있으며, 새 줄을 추가하여 결과를 stdout에 쓸 수 있습니다. - -```assembly -printResult: - mov rdx, rdi - mov rax, 1 - mov rdi, 1 - mov rsi, OUTPUT - syscall - jmp printNewLine - -printNewLine: - mov rax, SYS_WRITE - mov rdi, STD_OUT - mov rsi, NEW_LINE - mov rdx, 1 - syscall - jmp exit -``` - -그리고 프로그램을 종료합니다: - -```assembly -exit: - mov rax, SYS_EXIT - mov rdi, EXIT_CODE - syscall -``` - -이제 모든 것이 완료되었습니다. 프로그램을 다음 명령어로 컴파일할 수 있습니다: - -```assembly -all: - nasm -g -f elf64 -o reverse.o reverse.asm - ld -o reverse reverse.o - -clean: - rm reverse reverse.o -``` - -실행 결과: - -![result](/content/assets/result_asm_4.png) - -## 문자열 작업 - -물론 문자열 및 바이트 조작을 위한 많은 다른 명령어가 있습니다: - -* `REP` - RCX가 0이 아닌 동안 반복합니다. -* `MOVSB` - 바이트 문자열을 복사합니다 (MOVSW, MOVSD 등도 사용 가능합니다..) -* `CMPSB` - 바이트 문자열 비교 -* `SCASB` - 바이트 문자열 스캔 -* `STOSB` - 문자열에 바이트를 기록합니다 diff --git a/ko_doc/post/ko_asm_5.md b/ko_doc/post/ko_asm_5.md deleted file mode 100644 index a965863..0000000 --- a/ko_doc/post/ko_asm_5.md +++ /dev/null @@ -1,150 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 5] - -이 글은 x86_64 어셈블리의 다섯 번째 파트로 매크로를 다룹니다. -이 포스트는 x86_64에 대한 블로그 포스트가 아니라 NASM 어셈블러와 그 전처리기에 대한 것입니다. 관심이 있다면 계속 읽어보세요. - -## 매크로 - -NASM은 두 가지 형태의 매크로를 지원합니다: - -* 단일 라인 매크로 -* 다중 라인 매크로 - -단일 라인 매크로는 %define 지시어로 시작해야 합니다. 형식은 다음과 같습니다: - -```assembly -%define macro_name(parameter) value -``` - -NASM 매크로는 C의 매크로와 비슷하게 작동합니다. 예를 들어, 다음과 같은 단일 라인 매크로를 만들 수 있습니다: - -```assembly -%define argc rsp + 8 -%define cliArg1 rsp + 24 -``` - -그리고 이를 코드에서 사용할 수 있습니다: - -```assembly -;; -;; argc는 rsp + 8로 확장됩니다 -;; -mov rax, [argc] -cmp rax, 3 -jne .mustBe3args -``` - -다중 라인 매크로는 %macro NASM 지시어로 시작하고 %endmacro로 끝납니다. 일반 형식은 다음과 같습니다: - -```assembly -%macro number_of_parameters - instruction - instruction - instruction -%endmacro -``` - -예를 들어: - -```assembly -%macro bootstrap 1 - push ebp - mov ebp,esp -%endmacro -``` - -그리고 이를 다음과 같이 사용할 수 있습니다: - -```assembly -_start: - bootstrap -``` - -다음은 PRINT 매크로의 예입니다: - -```assembly -%macro PRINT 1 - pusha - pushf - jmp %%astr -%%str db %1, 0 -%%strln equ $-%%str -%%astr: _syscall_write %%str, %%strln -popf -popa -%endmacro - -%macro _syscall_write 2 - mov rax, 1 - mov rdi, 1 - mov rsi, %%str - mov rdx, %%strln - syscall -%endmacro -``` - -매크로가 어떻게 작동하는지 살펴보겠습니다: -첫 번째 줄에서는 매개변수가 하나인 PRINT 매크로를 정의합니다. - -그 다음에는 모든 일반 레지스터(pusha 명령어를 사용하여)와 플래그 레지스터(pushf 명령어를 사용하여)를 푸시합니다. -이후 %%astr 레이블로 점프합니다. 매크로 내의 모든 레이블은 %%로 시작해야 합니다. - -이제 _syscall_write 매크로로 이동합니다. 이 매크로는 두 개의 매개변수를 받습니다. -write 시스템 호출을 사용하여 문자열을 stdout으로 출력합니다. _syscall_write 매크로의 구현을 살펴보면: - -```assembly -;; write 시스템 호출 번호 -mov rax, 1 -;; 파일 디스크립터, 표준 출력 -mov rdi, 1 -;; 메시지 주소 -mov rsi, msg -;; 메시지 길이 -mov rdx, 14 -;; 시스템 호출 호출 -syscall -``` - -매크로는 먼저 rax에 1을 설정하여 write 시스템 호출 번호를 지정하고, rdi에 1을 설정하여 표준 출력 파일 디스크립터를 지정합니다. -그런 다음 rsi에 %%str을 설정하여 문자열의 포인터를 지정하고, %%str은 PRINT 매크로의 첫 번째 매개변수로 전달된 문자열입니다 (매크로 매개변수는 $parameter_number로 접근합니다). - -문자열은 0으로 끝나야 합니다. %%strlen은 문자열 길이를 계산합니다. 이후 시스템 호출을 syscall 명령어로 호출합니다. - -이제 다음과 같이 사용할 수 있습니다: - -```assembly -label: PRINT "Hello World!" -``` - -## 유용한 표준 매크로 - -NASM은 다음과 같은 표준 매크로를 지원합니다: - -### STRUC - -`STRUC`와 `ENDSTRUC`를 사용하여 데이터 구조를 정의할 수 있습니다. 예를 들어: - -```assembly -struc person - name: resb 10 - age: resb 1 -endstruc -``` - -그리고 이제 우리의 구조체 인스턴스를 만들 수 있습니다: - -```assembly -section .data - p: istruc person - at name db "name" - at age db 25 - iend - -section .text -_start: - mov rax, [p + person.name] -``` - -### %include - -다른 어셈블리 파일을 포함하고 %include 지시어를 사용하여 레이블로 점프하거나 함수를 호출할 수 있습니다. diff --git a/ko_doc/post/ko_asm_6.md b/ko_doc/post/ko_asm_6.md deleted file mode 100644 index a47f63d..0000000 --- a/ko_doc/post/ko_asm_6.md +++ /dev/null @@ -1,185 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 6] - -이 글은 x86_64 어셈블리의 여섯 번째 파트로 AT&T 어셈블리 문법을 다룹니다. -이전에는 NASM 어셈블러를 사용했지만, AT&T 문법을 사용하는 GNU 어셈블러(gas)와 그 문법의 차이를 살펴보겠습니다. - -GCC는 GNU 어셈블러를 사용하므로, 간단한 "Hello World" 프로그램의 어셈블리 출력은 다음과 같습니다: - - -```C -#include - -int main(void) { - write(1, "Hello World\n", 15); - return 0; -} -``` - -다음과 같은 출력이 나옵니다: - -```assembly - .file "test.c" - .section .rodata -.LC0: - .string "Hello World\n" - .text - .globl main - .type main, @function -main: -.LFB0: - .cfi_startproc - pushq %rbp - .cfi_def_cfa_offset 16 - .cfi_offset 6, -16 - movq %rsp, %rbp - .cfi_def_cfa_register 6 - movl $15, %edx - movl $.LC0, %esi - movl $1, %edi - call write - movl $0, %eax - popq %rbp - .cfi_def_cfa 7, 8 - ret - .cfi_endproc -.LFE0: - .size main, .-main - .ident "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1" - .section .note.GNU-stack,"",@progbits -``` - -위의 출력은 NASM의 "Hello World"와 다르게 보입니다. 차이점을 살펴보겠습니다. - -## AT&T 문법 - -### 섹션 - -```assembly -.data - // - // 초기화된 데이터 정의 - // -.text - .global _start - -_start: - // - // 주요 루틴 - // -``` - -다음 두 가지 차이점이 있습니다: - -* 섹션 정의는 . 기호로 시작합니다. -* 메인 루틴은 global 대신 .globl로 정의합니다. - -GNU 어셈블러(gas)는 데이터 정의를 위해 다음과 같은 지시어를 사용합니다: - -```assembly -.section .data - // 1 바이트 - var1: .byte 10 - // 2 바이트 - var2: .word 10 - // 4 바이트 - var3: .int 10 - // 8 바이트 - var4: .quad 10 - // 16 바이트 - var5: .octa 10 - - // 자동 종료 바이트 없이 연속적인 주소로 문자열을 조립 - str1: .asci "Hello world" - // 자동 종료 바이트가 있는 문자열 - str2: .asciz "Hello world" - // 문자열을 오브젝트 파일에 복사 - str3: .string "Hello world" -``` - -피연산자 순서 -NASM에서 데이터 조작을 위한 일반 문법은 다음과 같습니다: - -```assembly -mov destination, source -``` - -GNU 어셈블러에서는 피연산자 순서가 반대입니다: - -```assembly -mov source, destination -``` - -예를 들어: - -```assembly -;; -;; nasm 문법 -;; -mov rax, rcx - -// -// GNU(gas) 문법 -// -mov %rcx, %rax -``` - -또한, GNU 어셈블러에서는 레지스터가 % 기호로 시작합니다. 직접 오퍼랜드를 사용할 때는 `$` 기호를 사용해야 합니다: - -```assembly -movb $10, %rax -``` - -## 피연산자 크기 및 연산 문법 - -메모리의 일부, 예를 들어 64비트 레지스터의 첫 바이트를 가져와야 할 때, NASM에서는 다음과 같은 문법을 사용합니다: - -```assembly -mov ax, word [rsi] -``` - -GNU 어셈블러에서는 피연산자의 크기를 명시하지 않고, 명령어에서 직접 정의합니다: - -```assembly -movw (%rsi), %ax -``` - -GNU 어셈블러는 연산을 위한 6가지 접미사를 지원합니다: - -* `b` - 1 바이트 피연산자 -* `w` - 2 바이트 피연산자 -* `l` - 4 바이트 피연산자 -* `q` - 8 바이트 피연산자 -* `t` - 10 바이트 피연산자 -* `o` - 16 바이트 피연산자 - -이 규칙은 mov 명령어뿐만 아니라 addl, xorb, cmpw 등 다른 명령어에도 적용됩니다. - -## 메모리 접근 - -이전 예제에서 () 괄호 대신 []를 사용했음을 주목할 수 있습니다. -괄호 안의 값을 역참조할 때는 GAS에서는 다음과 같이 사용합니다: (%rax). 예를 들어: - -```assembly -movq -8(%rbp),%rdi -movq 8(%rbp),%rdi -``` - -## 점프 - -GNU 어셈블러는 다음과 같은 연산자를 지원합니다: - -```assembly -lcall $section, $offset -``` - -Far jump는 현재 코드 세그먼트와 다른 세그먼트에 있는 명령어로 점프하는 것입니다. 때로는 인터세그먼트 점프라고도 합니다. - -## 주석 - -GNU 어셈블러는 다음과 같은 3가지 유형의 주석을 지원합니다: - -``` - # - 한 줄 주석 - // - 한 줄 주석 - /* */ - 여러 줄 주석 -``` diff --git a/ko_doc/post/ko_asm_7.md b/ko_doc/post/ko_asm_7.md deleted file mode 100644 index 46464c8..0000000 --- a/ko_doc/post/ko_asm_7.md +++ /dev/null @@ -1,169 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 7] - -x64 어셈블리 소개 - x64 어셈블리와 친해지기 [파트 7] 입니다. 이번에는 C와 어셈블러를 함께 사용하는 방법을 살펴보겠습니다. - -실제로 우리는 이를 함께 사용할 수 있는 3가지 방법이 있습니다: - -* C 코드에서 어셈블리 루틴 호출하기 -* 어셈블리 코드에서 C 루틴 호출하기 -* C 코드에서 인라인 어셈블리 사용하기 - -어셈블리와 C를 함께 사용하는 방법을 보여주는 3개의 간단한 Hello world 프로그램을 작성해 봅시다. - -## C에서 어셈블리 호출하기 - -먼저 다음과 같은 간단한 C 프로그램을 작성해 봅시다: - -```C -#include - -int main() { - char* str = "Hello World\n"; - int len = strlen(str); - printHelloWorld(str, len); - return 0; -} -``` - -여기서 우리는 두 개의 변수를 정의하는 C 코드를 볼 수 있습니다 -stdout에 쓸 Hello world 문자열과 이 문자열의 길이입니다. 다음으로 이 2개의 변수를 매개변수로 하여 printHelloWorld 어셈블리 함수를 호출합니다. - -우리는 x86_64 Linux를 사용하므로, x86_64 linux 호출 규약을 알아야 합니다. -그래야 printHelloWorld 함수를 어떻게 작성하고, 들어오는 매개변수를 어떻게 가져오는지 등을 알 수 있습니다... - -함수를 호출할 때 처음 여섯 개의 매개변수는 rdi, rsi, rdx, rcx, r8, r9 범용 레지스터를 통해 전달되며, 그 외의 모든 매개변수는 스택을 통해 전달됩니다. -따라서 우리는 첫 번째와 두 번째 매개변수를 rdi와 rsi 레지스터에서 가져와 write 시스템 콜을 호출한 다음 ret 명령어로 함수에서 반환할 수 있습니다: - -```assembly -global printHelloWorld - -section .text -printHelloWorld: - ;; 1 arg - mov r10, rdi - ;; 2 arg - mov r11, rsi - ;; call write syscall - mov rax, 1 - mov rdi, 1 - mov rsi, r10 - mov rdx, r11 - syscall - ret -``` - -이제 다음과 같이 빌드할 수 있습니다: - -``` -build: - nasm -f elf64 -o casm.o casm.asm - gcc casm.o casm.c -o casm -``` - -## 인라인 어셈블리 -다음 방법은 C 코드 직접 어셈블리 코드를 작성하는 것입니다. -이를 위한 특별한 구문이 있습니다. 일반적인 형태는 다음과 같습니다: - -``` -asm [volatile] ("assembly code" : output operand : input operand : clobbers); -``` - -gcc 문서에서 읽을 수 있듯이 volatile 키워드는 다음을 의미합니다: - -``` -Extended asm 문의 일반적인 사용은 입력 값을 조작하여 출력 값을 생성하는 것입니다. 그러나 asm 문이 부작용을 일으킬 수도 있습니다. 그런 경우, volatile 한정자를 사용하여 특정 최적화를 비활성화해야 할 수 있습니다. -``` - -각 피연산자는 괄호 안에 C 표현식이 뒤따르는 제약 문자열로 설명됩니다. 여러 가지 제약 조건이 있습니다: - -* `r` - 범용 레지스터에 변수 값 유지 -* `g` - 범용 레지스터가 아닌 레지스터를 제외하고, 모든 레지스터, 메모리 또는 즉시 정수 피연산자가 허용됩니다. -* `f` - 부동 소수점 레지스터 -* `m` - 메모리 피연산자가 허용되며, 기계가 일반적으로 지원하는 모든 종류의 주소를 사용할 수 있습니다. -기타 등등... - -따라서 우리의 hello world는 다음과 같을 것입니다: - -```C -#include - -int main() { - char* str = "Hello World\n"; - long len = strlen(str); - int ret = 0; - - __asm__("movq $1, %%rax \n\t" - "movq $1, %%rdi \n\t" - "movq %1, %%rsi \n\t" - "movl %2, %%edx \n\t" - "syscall" - : "=g"(ret) - : "g"(str), "g" (len)); - - return 0; -} -``` - -여기서 우리는 이전 예제와 같은 2개의 변수와 인라인 어셈블리 정의를 볼 수 있습니다. -먼저 우리는 rax와 rdi 레지스터에 1을 넣습니다(write 시스템 콜 번호와 stdout)는 것은 일반 어셈블리 hello world에서 했던 것과 같습니다. - -다음으로 rsi와 rdi 레지스터에 대해 유사한 작업을 수행하지만 첫 번째 피연산자는 $ 대신 % 기호로 시작합니다. -이는 str이 %1로 참조되는 출력 피연산자이고 len이 %2로 참조되는 두 번째 출력 피연산자임을 의미합니다. - -따라서 우리는 %n 표기법을 사용하여 str과 len의 값을 rsi와 rdi에 넣습니다. 여기서 n은 출력 피연산자의 번호입니다. -또한 레지스터 이름 앞에 %%가 붙습니다. - -``` -이는 GCC가 피연산자와 레지스터를 구분하는 데 도움이 됩니다. 피연산자는 접두사로 단일 %를 가집니다. -``` - -다음과 같이 빌드할 수 있습니다: - -``` -build: - gcc casm.c -o casm -``` - -## 어셈블리에서 C 호출하기 - -그리고 마지막 방법은 어셈블리 코드에서 C 함수를 호출하는 것입니다. -예를 들어, 단순히 Hello world를 출력하는 하나의 함수가 있는 다음과 같은 간단한 C 코드가 있습니다: - -```C -#include - -extern int print(); - -int print() { - printf("Hello World\n"); - return 0; -} -``` - -이제 우리는 이 함수를 어셈블리 코드에서 extern으로 정의하고 이전 게시물에서 여러 번 했던 것처럼 call 명령어로 호출할 수 있습니다: - -```asssembly -global _start - -extern print - -section .text - -_start: - call print - - mov rax, 60 - mov rdi, 0 - syscall -``` - -다음과 같이 빌드합니다: - -``` -build: - gcc -c casm.c -o c.o - nasm -f elf64 casm.asm -o casm.o - ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc casm.o c.o -o casm -``` - -이제 우리의 세 번째 hello world를 실행할 수 있습니다. diff --git a/ko_doc/post/ko_asm_8.md b/ko_doc/post/ko_asm_8.md deleted file mode 100644 index 3123f5f..0000000 --- a/ko_doc/post/ko_asm_8.md +++ /dev/null @@ -1,202 +0,0 @@ -# X86_64 어셈블리와 친해지기 [파트 8] - -파트 8이자 마지막 파트에서는 어셈블리에서 비정수 숫자를 다루는 방법을 살펴보겠습니다. 부동 소수점 데이터를 처리하는 방법에는 몇 가지가 있습니다: - -* FPU (부동 소수점 유닛) -* SSE (스트림 SIMD 확장) - -먼저 부동 소수점 숫자가 메모리에 어떻게 저장되는지 살펴보겠습니다. 부동 소수점 데이터 유형에는 세 가지가 있습니다: - -* 단정도 (single-precision) -* 배정도 (double-precision) -* 확장 배정도 (double-extended precision) - -인텔의 64-IA-32 아키텍처 소프트웨어 개발자 매뉴얼 1권에 따르면: - -``` -이 데이터 유형의 데이터 형식은 IEEE 표준 754에 명시된 이진 부동 소수점 산술 형식에 직접적으로 해당됩니다. -``` - -단정도 부동 소수점 숫자는 메모리에 다음과 같이 저장됩니다: - -* 부호 (sign) - 1 비트 -* 지수 (exponent) - 8 비트 -* 가수 (mantissa) - 23 비트 - -예를 들어, 다음과 같은 숫자가 있을 때: - - | sign | exponent | mantissa - |-------|----------|------------------------- - | 0 | 00001111 | 110000000000000000000000 - -지수는 8 비트 부호가 있는 정수로 -128에서 127 사이이거나, 8 비트 부호가 없는 정수로 0에서 255 사이일 수 있습니다. -부호 비트가 0이므로 양수입니다. 지수는 00001111b 또는 10진수로 15입니다. 단정도 부동 소수점에서의 편향값은 127이므로, 지수에서 127을 빼야 합니다. - -즉, 15 - 127 = -112입니다. 정규화된 이진 정수 부분은 항상 1이므로 가수에는 그 소수 부분만 기록됩니다. -즉, 가수는 1.110000000000000000000000입니다. 결과 값은 다음과 같습니다: - -``` -value = mantissa * 2^-112 -``` - -배정도 숫자는 64 비트 메모리에 다음과 같이 저장됩니다: - -* 부호 (sign) - 1 비트 -* 지수 (exponent) - 11 비트 -* 가수 (mantissa) - 52 비트 - -결과 숫자는 다음과 같이 계산할 수 있습니다: - -``` -value = (-1)^sign * (1 + mantissa / 2 ^ 52) * 2 ^ exponent - 1023) -``` - -확장 배정도는 80 비트 숫자이며: - -* 부호 (sign) - 1 비트 -* 지수 (exponent) - 15 비트 -* 가수 (mantissa) - 112 비트 - -자세한 내용은 [여기](https://en.wikipedia.org/wiki/Extended_precision)에서 확인할 수 있습니다. -이제 간단한 예제를 살펴보겠습니다. - -## x87 FPU - -x87 부동 소수점 유닛(FPU)은 고성능 부동 소수점 처리를 제공합니다. -부동 소수점, 정수 및 패킹된 BCD 정수 데이터 유형과 부동 소수점 처리 알고리즘을 지원합니다. - -x87은 다음과 같은 명령어 세트를 제공합니다: - -* 데이터 전송 명령어 -* 기본 산술 명령어 -* 비교 명령어 -* 초월적 명령어 -* 상수 로드 명령어 -* x87 FPU 제어 명령어 - -물론 여기서 x87이 제공하는 모든 명령어를 살펴보지는 않겠지만, -추가 정보는 64-IA-32 아키텍처 소프트웨어 개발자 매뉴얼 1권의 8장을 참조하세요. 몇 가지 데이터 전송 명령어는 다음과 같습니다: - -* `FDL` - 부동 소수점 로드 -* `FST` - 부동 소수점 저장 (ST(0) 레지스터에) -* `FSTP` - 부동 소수점 저장 및 팝 (ST(0) 레지스터에) - -산술 명령어: - -* `FADD` - 부동 소수점 덧셈 -* `FIADD` - 정수를 부동 소수점에 추가 -* `FSUB` - 부동 소수점 뺄셈 -* `FISUB` - 부동 소수점에서 정수 뺄셈 -* `FABS` - 절대값 가져오기 -* `FIMUL` - 정수와 부동 소수점 곱셈 -* `FIDIV` - 정수와 부동 소수점 나눗셈 - -x87에는 10바이트 레지스터 8개가 링 스택 형태로 조직되어 있습니다. 스택의 맨 위는 ST(0) 레지스터이며, 나머지 레지스터는 ST(1), ST(2), ..., ST(7)입니다. 일반적으로 부동 소수점 데이터를 다룰 때 사용됩니다. - -예를 들어: - -```assembly -section .data - x dw 1.0 - -fld dword [x] -``` - -이는 x의 값을 스택에 푸시합니다. 연산자는 32비트, 64비트 또는 80비트일 수 있습니다. -일반 스택처럼 작동하며, fld로 다른 값을 푸시하면 x 값은 ST(1)에 있고, 새로운 값이 ST(0)에 위치하게 됩니다. -FPU 명령어는 이러한 레지스터를 사용할 수 있습니다. - -예를 들어: - -```assembly -;; -;; st0 값을 st3에 더하고 결과를 st0에 저장합니다 -;; -fadd st0, st3 - -;; -;; x와 y를 더하고 결과를 st0에 저장합니다 -;; -fld dword [x] -fld dword [y] -fadd -``` - -간단한 예제를 살펴보겠습니다. -원의 반지름을 가지고 원의 면적을 계산하고 출력하는 예제입니다: - -```assembly -extern printResult - -section .data - radius dq 1.7 - result dq 0 - - SYS_EXIT equ 60 - EXIT_CODE equ 0 - -global _start -section .text - -_start: - fld qword [radius] - fld qword [radius] - fmul - - fldpi - fmul - fstp qword [result] - - mov rax, 0 - movq xmm0, [result] - call printResult - - mov rax, SYS_EXIT - mov rdi, EXIT_CODE - syscall -``` - -이 예제의 동작을 이해해 보겠습니다: -우선 데이터 섹션에는 반지름 데이터와 결과를 저장할 변수들이 정의되어 있습니다. -그 후 시스템 호출 종료를 위한 두 상수가 정의되어 있습니다. 프로그램의 진입점인 _start에서 fld 명령어로 ST(0)과 ST(1) 레지스터에 반지름 값을 저장하고, fmul 명령어로 두 값을 곱합니다. - -이 연산 후에는 ST(0) 레지스터에 반지름의 제곱이 저장됩니다. -이후 fldpi 명령어로 π 값을 ST(0) 레지스터에 로드하고, fmul로 ST(0) (π)과 ST(1) (반지름의 제곱)을 곱하여 결과를 ST(0) 레지스터에 저장합니다. -이제 ST(0) 레지스터에 원의 면적이 있으므로 fstp 명령어로 결과를 저장소에 추출합니다. 다음으로, 결과를 C 함수에 전달하고 호출합니다. - -어셈블리 코드에서 C 함수를 호출할 때 x86_64 호출 규약을 알아야 합니다. -일반적으로 함수 매개변수는 레지스터 rdi (arg1), rsi (arg2) 등을 통해 전달되지만, 부동 소수점 데이터의 경우에는 특별한 레지스터 xmm0 - xmm15가 사용됩니다. -우선 xmmN 레지스터의 번호를 rax 레지스터에 넣고 (우리의 경우 0), 결과를 xmm0 레지스터에 넣습니다. 이제 C 함수 printResult를 호출할 수 있습니다: - -```C -#include - -extern int printResult(double result); - -int printResult(double result) { - printf("Circle radius is - %f\n", result); - return 0; -} -``` - -이 코드는 다음과 같이 빌드할 수 있습니다: - -``` -build: - gcc -g -c circle_fpu_87c.c -o c.o - nasm -f elf64 circle_fpu_87.asm -o circle_fpu_87.o - ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc circle_fpu_87.o c.o -o testFloat1 - -clean: - rm -rf *.o - rm -rf testFloat1 -``` - -그리고 실행하면: - -![result](/content/assets/result_asm_8.png) - -## 옮긴이의 말 - -이제까지 x86_64 어셈블리와 NASM 어셈블러의 다양한 기능을 살펴보았습니다. 매크로를 활용해 코드를 간결하게 하고, 반복 작업을 줄이는 방법을 익혔습니다. 어셈블리 언어는 복잡할 수 있지만, -어셈블리 언어를 배워가는 여정은 계속될 것입니다. 이 시리즈가 여러분의 학습에 도움이 되었기를 바랍니다. From e87a993bde3e800c9b45c4b3153eb0b6a92c8283 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 20:44:06 +0900 Subject: [PATCH 44/45] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 88fd433..2c686f8 100644 --- a/README.md +++ b/README.md @@ -70,14 +70,14 @@ Thanks to the volunteers, the posts about assembly programming are translated in ### Korean translation - * [X86_64 어셈블리와 친해지기 [파트 1]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_1.md) - * [X86_64 어셈블리와 친해지기 [파트 2]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_2.md) - * [X86_64 어셈블리와 친해지기 [파트 3]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_3.md) - * [X86_64 어셈블리와 친해지기 [파트 4]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_4.md) - * [X86_64 어셈블리와 친해지기 [파트 5]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_5.md) - * [X86_64 어셈블리와 친해지기 [파트 6]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_6.md) - * [X86_64 어셈블리와 친해지기 [파트 7]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_7.md) - * [X86_64 어셈블리와 친해지기 [파트 8]](https://github.com/maldron0309/asm/blob/master/ko_doc/post/ko_asm_8.md) + * [X86_64 어셈블리와 친해지기 [파트 1]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_1.md) + * [X86_64 어셈블리와 친해지기 [파트 2]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_2.md) + * [X86_64 어셈블리와 친해지기 [파트 3]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_3.md) + * [X86_64 어셈블리와 친해지기 [파트 4]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_4.md) + * [X86_64 어셈블리와 친해지기 [파트 5]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_5.md) + * [X86_64 어셈블리와 친해지기 [파트 6]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_6.md) + * [X86_64 어셈블리와 친해지기 [파트 7]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_7.md) + * [X86_64 어셈블리와 친해지기 [파트 8]](https://github.com/maldron0309/asm/blob/korean/content/ko_asm_8.md) ## Contribution From 20416c5094a477210f0dc5d9caba4c5808c0c237 Mon Sep 17 00:00:00 2001 From: Maldron Date: Tue, 20 Aug 2024 21:36:32 +0900 Subject: [PATCH 45/45] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 56d8bb6..9dada86 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,3 +9,4 @@ Special thanks to all the people who helped to develop this project: - [JRMcCoy](https://github.com/JRMcCoy) - [knoxknox](https://github.com/knoxknox) - [tirkarthi](https://github.com/tirkarthi) +- [maldron0309](https://github.com/maldron0309)