Chamadas de Sistema do Mac OS X

Introdução

Este artigo tem como propósito explicar como se pode fazer uma chamada de sistema (syscall) do Mac OS X, através de linguagem de máquina (Intel i386). Uma chamada de sistema é o mecanismo padrão para obter recursos e funcionalidades básicas do sistema operacional, como por exemplo, ler e escrever dados em um descritor de arquivos.

Os exemplos desses artigos foram testados em um MacBook (Core Duo) e um MacbookPro (Core 2 Duo) rodando Mac OS X versão 10.6.7.

Mecanismo

O Mac OS X segue o estilo de chamadas de sistema do FreeBSD, de forma que os parâmetros são postos na pilha, o número da syscall é gravado no registrador EAX e então, chama-se a instrução int $0x80.

O valor de retorno da syscall ou status está contida no registrador EAX e deve-se restaurar a pilha ao seu estado original a chamada.

É adequado chamar int $0x80 indiretamente através de uma subrotina, em vez de usar diretamente a instrução. O motivo é por questão de alinhamento de pilha. Faremos uso então de uma subrotina de nome _syscall, assim definida:

_syscall:
        int $0x80
        ret

O próximo passo é conhecer quais são as chamadas de sistema disponíveis (os valores decimais) e parâmetros esperados. Estas chamadas são definidas em um arquivo de template do componente XNU, de nome syscalls.master.

Vamos fazer uso das chamadas de nome exit, write e sync, que possuem as seguintes assinaturas:

1 AUE_EXIT ALL { void exit(int rval); }
4 AUE_NULL ALL { user_ssize_t write(
	int fd, user_addr_t cbuf, user_size_t nbyte); }
36 AUE_SYNC ALL { int sync(void); } 

Efetuar chamada

O exemplo abaixo é o fragmento de uma chamada de sistema para sync, que avisa ao sistema operacional para esvaziar/sincronizar qualquer buffer que esteja em memória para o disco. Esta syscall não aceita parâmetros e não retorna nenhuma informação de execução.

	.text
_syscall:
	int $0x80
	ret

	.globl start
start:
	movl $36, %eax		# sync = 36
	call _syscall		# sync()

sync.s

Nota: diferentemente dos programas em C, programas em código de máquina possuem como ponto de entrada o símbolo start.

Retorno de status

Exemplo simples de uma chamada de sistema que retorna imediatamente ao shell do usuário o valor de status 42, o número mágico da resposta do sentido da vida (e tudo mais). O valor de retorno (rval) deve ser empilhado como um parâmetro.

        .text
_syscall:
        int $0x80
        ret

        .globl start
start:
        pushl $42               # rval = 42
        movl $1,%eax            # exit = 1
        call _syscall           # exit(42)

retval.s

Para transformar este fonte em um arquivo executável, faz-se:

$ as -arch i386 retval.s -o retval.o
$ ld retval.o -o retval

E o resultado após a execução é o status 42 para o shell, que pode ser obtido com a variável do shell $?.

$ ./retval
$ echo $?
42

Hello World

Para um exemplo mais complexo, vejamos um programa que escreve a mensagem Hello World! na saída padrão (STDOUT) e retorna ao shell o valor 0. Os parâmetros são empilhados seguindo a ordem da direita para a esquerda na assinatura da chamada do sistema.

        .text
_syscall:
        int $0x80
        ret

        .globl start
start:
        pushl len               # nbyte = len
        pushl $msg              # cbuf = msg
        pushl $1                # fd = STDOUT = 1
        movl $4,%eax            # write = 4
        call _syscall           # write(fd=1,cbuf=&msg,nbyte=len)
        addl $12,%esp           # restore stack (3 * 4 = 12)
        pushl $0
        movl $1,%eax
        call _syscall		# exit(0)

        .data
msg:
        .ascii "Hello World!\n"
len:
        .long . - msg

hello.s

Para gerar o programa, faz-se:

$ as -arch i386 hello.s -o hello.o
$ ld hello.o -o hello

O resultado pode ser observado como segue:

$ ./hello 
Hello World!
$ echo $?
0