[libasm] strlen 함수를 어셈블리어로 짠다면
어셈블리어로 프로그램을 짜려면 먼저 함수 호출 규약을 알아야 한다. syscall 함수의 사용법과 에러 처리 방법도 함게 알아보자.
Last updated
어셈블리어로 프로그램을 짜려면 먼저 함수 호출 규약을 알아야 한다. syscall 함수의 사용법과 에러 처리 방법도 함게 알아보자.
Last updated
인자로 받은 문자열의 길이를 구하는 ft_strlen 함수이다. 시작주소부터 +1씩 증가하다가 널문자(0)를 만나면 반복문을 탈출하고 구한 길이를 반환한다. 얘를 어셈블리어로 변환해보자!
어셈블리 프로그램을 짜려면 먼저 함수 호출 규약을 알아야 한다. 더 자세한 내용은 여기 참고.
매개변수로 받은 s의 주소는 rdi
레지스터에 들어온다.
rdi
레지스터의 값은 BYTE [rdi]
로 참조한다.
rax
를 통해 리턴 값을 반환한다.
위 레지스터를 활용해 어셈블리어로 코드를 짜면 아래와 같다.
일종의 약속(코딩 컨벤션)으로, 언더바를 붙이지 않으면 C에서 어셈블리어로 작성한 함수를 사용할 수 없다. 출처
어셈블리에서는 기본적으로 모든 코드가 private이다. 이때 다른 모듈이 해당 코드에 접근할 수 있게 하기 위해서 global instruction을 이용하여 심볼에 다른 코드가 접근할 수 있도록 해 준다. 이렇게 명시하지 않는다면 링커에서 아무런 심볼을 찾을 수 없다는 오류가 발생한다. 출처
그 외에 section, label, 명령어, 레지스터가 낯설다면 이전 글 참고!
undefined
42에서는 Assembler로 nasm을 쓴다. 만약 nasm 설치가 안되어 있다면 brew install nasm
syscall 명령어를 통해, 시스템 상에 미리 선언되어 있는 함수를 어셈블리 프로그램에서도 사용할 수 있다. 아래 예제는 write()
함수를 syscall하는 예제이다.
시스템 콜 함수는 고유의 번호(0x2000004)를 갖고 있으며, OS마다 조금씩 다르다. Linux는 여기 참고, MacOS는 여기 참고.
이 때 syscall 할 함수의 번호를 rax
에 미리 넣어줘야한다.
syscall 함수의 반환값 또한 rax
에 저장된다.
자주 사용하는 syscall numbers (mac)
0x2000001 - exit()
0x2000002 -
0x2000003 - read()
0x2000004 - write()
0x2000005 - open()
0x2000006 - close()
만약 syscall 후 에러가 발생했다면 이는 ___error
함수를 이용해 처리해야한다.
syscall 함수는 오류가 있을 경우 rax
에 작은 음수(-1 ~ -4095)를 반환하며 동시에 carry flag가 참이 된다. 따라서 jc
(carry flag가 1일 때 점프)를 활용하면 에러 처리 구문(err)으로 넘어갈 수 있다.
rax
에 담긴 음수의 정체를 이해하는 것이 중요하다. 이 리턴값은, C언어에서 에러에 대한 정보를 나타내는 정보인 errno(쉽게 말해 미리 정의된 에러 번호)의 음수 값이다. 즉, syscall 후 에러 발생 시 rax에는 -errno(-4095 ~ -1)가 반환된다.
errno 가 궁금하다면 man 2 errno
어셈블리로 작성한 _err 구문이 올바르게 errno를 출력할 수 있도록 하기 위해서는 rax
에 음수 리턴값, [rax]
에 errno에 해당하는 숫자가 들어갈 수 있도록 작성해야한다.