[libasm] strlen 함수를 어셈블리어로 짠다면?

이대현·2020년 11월 27일
4

42SEOUL

목록 보기
16/24

1. strlen 함수를 어셈블리어로 짠다면?

size_t		ft_strlen(const char *s)
{
	size_t	i;

	i = 0;
	while (s[i] != '\0')
		i++;
	return (i);
}

인자로 받은 문자열의 길이를 구하는 ft_strlen 함수이다. 시작주소부터 +1씩 증가하다가 널문자(0)를 만나면 반복문을 탈출하고 구한 길이를 반환한다. 얘를 어셈블리어로 변환해보자!

어셈블리 프로그램을 짜려면 먼저 함수 호출 규약을 알아야 한다. 더 자세한 내용은 여기 참고.

  1. 매개변수로 받은 s의 주소는 rdi 레지스터에 들어온다.
  2. rdi 레지스터의 값은 BYTE [rdi]로 참조한다.
  3. rax를 통해 리턴 값을 반환한다.

위 레지스터를 활용해 어셈블리어로 코드를 짜면 아래와 같다.

어셈블리 파일 내부 함수에 _(언더바)를 붙이는 이유

일종의 약속(코딩 컨벤션)으로, 언더바를 붙이지 않으면 C에서 어셈블리어로 작성한 함수를 사용할 수 없다. 출처

어셈블리에서는 기본적으로 모든 코드가 private이다. 이때 다른 모듈이 해당 코드에 접근할 수 있게 하기 위해서 global instruction을 이용하여 심볼에 다른 코드가 접근할 수 있도록 해 준다. 이렇게 명시하지 않는다면 링커에서 아무런 심볼을 찾을 수 없다는 오류가 발생한다. 출처

그 외에 section, label, 명령어, 레지스터가 낯설다면 이전 글 참고!


2. 어셈블리 프로그램 실행하기

C언어 강좌-2 C언어 컴파일 과정

42에서는 Assembler로 nasm을 쓴다. 만약 nasm 설치가 안되어 있다면 brew install nasm

2.1. 컴파일

$ nasm -f macho64 ft_strlen.s

2.2. 링킹

$ ld -lSystem ft_strlen.o

2.3. 실행

$ ./a.out

3. 시스템 콜(syscall)

syscall 명령어를 통해, 시스템 상에 미리 선언되어 있는 함수를 어셈블리 프로그램에서도 사용할 수 있다. 아래 예제는 write()함수를 syscall하는 예제이다.

시스템 콜 함수는 고유의 번호(0x2000004)를 갖고 있으며, OS마다 조금씩 다르다. Linux는 여기 참고, MacOS는 여기 참고.

_main:
    mov rax, 0x2000004 ;시스템콜 함수를 write로 변경
    syscall ;시스템콜(write) 호출
  • 이 때 syscall 할 함수의 번호를 rax에 미리 넣어줘야한다.
  • syscall 함수의 반환값 또한 rax에 저장된다.
  • 자주 사용하는 syscall numbers (mac)
    • 0x2000001 - exit()
    • 0x2000002 -
    • 0x2000003 - read()
    • 0x2000004 - write()
    • 0x2000005 - open()
    • 0x2000006 - close()

3.1. ___error 함수를 이용한 에러 처리

만약 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에 해당하는 숫자가 들어갈 수 있도록 작성해야한다.

extern ___error
    
	jc _err
	ret
	
_err:
	push rax
	call ___error
	pop rdx
	mov [rax], rdx
	mov rax, -1
	ret

참고


profile
삽질의 기록들 👨‍💻

0개의 댓글