Programação: Syscalls no Mac OS X

Interrompemos nossa programação normal para tratar de algo que é extremamente interessante, mas só para um micro-nicho de um subnicho de um nicho: os desenvolvedores de software voltados ao Mac e que têm interesse em compreender e manipular o chamado "baixo nível" do sistema.

O Rudá Moura (que já contribuiu aqui no BR-Mac anteriormente no artigo "Rudix: pacotes GNU e para UNIX adicionais para o Mac") é um integrante deste seleto grupo, e agora compartilha conosco um artigo explicando como se pode fazer uma chamada de sistema (syscall) do Mac OS X, diretamente a partir de um código em Assembly (que ele também demonstra como montar e tornar executável).

Continue lendo para ter acesso à versão integral do texto, que também está publicado no site do Rudá!

Chamadas de Sistema do Mac OS X
Por Rudá Moura*

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               # 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!
"
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

* O autor convidado Rudá Moura é entusiasta da linguagem Python, desenvolvedor UNIX e aficcionado por Macs.
 

Comentar

Comentários arquivados

Mais acessados:

Artigos recentes: