Esta seção trata da implementação da camada de emulação do Linux® no sistema operacional FreeBSD. Ele primeiro descreve a parte dependente da máquina falando sobre como e onde a interação entre o usuário e o kernel é implementada. Ele fala sobre syscalls, signals, ptrace, traps, correção de pilha. Esta parte discute o i386, mas ele é escrito geralmente para que outras arquiteturas não sejam muito diferentes. A próxima parte é a parte independente da máquina do Linuxulator. Esta seção abrange apenas o tratamento de i386 e ELF. A.OUT está obsoleto e não foi testado.
A manipulação de Syscall é principalmente escrita em linux_sysvec.c
, que cobre a maioria das rotinas apontadas na estrutura sysentvec
. Quando um processo Linux® executado no FreeBSD emite um syscall, a rotina syscall geral chama a rotina prepsyscall do linux para a ABI do Linux®.
Linux® passa argumentos via registradores de syscalls (isso porque ele é limitado a 6 parametros no i386) enquanto o FreeBSD usa uma pilha. A rotina prepsyscall do Linux® deve copiar parametros dos registradores para a pilha. A ordem dos registradores é: %ebx
, %ecx
, %edx
, %esi
, %edi
, %ebp
. O fato é que isso é verdadeiro apenas para a maioria das syscalls. Algumas (mais provavelmente clone
) usam uma ordem diferente, mas é demasiadamente facil de arrumar inserindo um parametro dummy no prototype linux_clone
.
Cada syscall implementada no Linuxulator deve ter seu protótipo com vários flags no syscalls.master
. A forma do arquivo é:
... AUE_FORK STD { int linux_fork(void); } ... AUE_CLOSE NOPROTO { int close(int fd); } ...
A primeira coluna representa o número da syscall. A segunda coluna é para suporte de auditoria. A terceira coluna representa o tipo da syscall. É STD
, OBSOL
, NOPROTO
e UNIMPL
. STD
é uma syscall padrão com protótipo e implementação completos. OBSOL
é obsoleto e define apenas o protótipo. NOPROTO
significa que a syscall é implementado em outro lugar, portanto, não precede o prefixo da ABI, etc. UNIMPL
significa que a syscall será substituída pela syscall nosys
(uma syscall apenas imprime uma mensagem sobre a syscall não sendo implementado e retornando ENOSYS
).
De um script syscalls.master
, gera três arquivos: linux_syscall.h
, linux_proto.h
e linux_sysent.c
. O linux_syscall.h
contém definições de nomes de syscall e seus valores numéricos, por exemplo:
... #define LINUX_SYS_linux_fork 2 ... #define LINUX_SYS_close 6 ...
O linux_proto.h
contém definições de estrutura de argumentos para cada syscall, por exemplo:
struct linux_fork_args { register_t dummy; };
E finalmente, linux_sysent.c
contém uma estrutura descrevendo a tabela de entrada do sistema, usada para realmente enviar um syscall, por exemplo:
{ 0, (sy_call_t *)linux_fork, AUE_FORK, NULL, 0, 0 }, /* 2 = linux_fork */ { AS(close_args), (sy_call_t *)close, AUE_CLOSE, NULL, 0, 0 }, /* 6 = close */
Como você pode ver, linux_fork
é implementado no próprio Linuxulator, então a definição é do tipo STD
e não possui argumento, que é exibido pela estrutura de argumento fictícia. Por outro lado, close
é apenas um apelido para o verdadeiro close(2) do FreeBSD para que ele não possua estrutura de argumentos do linux associada e na tabela de entrada do sistema ele não é prefixado com linux, pois ele chama o verdadeiro close(2) no kernel.
A camada de emulação do Linux® não está completa, pois algumas syscalls não estão implementadas corretamente e algumas não estão implementadas. A camada de emulação emprega um recurso para marcar syscalls não implementadas com a macro DUMMY
. Estas definições fictícias residem em linux_dummy.c
em uma forma de DUMMY(syscall);
, que é então traduzido para vários arquivos auxiliares de syscall e a implementação consiste em imprimir uma mensagem dizendo que esta syscall não está implementada. O protótipo UNIMPL
não é usado porque queremos ser capazes de identificar o nome da syscall que foi chamado para saber o que é mais importante implementar na syscalls.
A manipulação de sinais é feita geralmente no kernel do FreeBSD para todas as compatibilidades binárias com uma chamada para uma camada dependente de compatibilidade. A camada de compatibilidade do Linux® define a rotina linux_sendsig
para essa finalidade.
Esta rotina primeiro verifica se o signal foi instalado com um SA_SIGINFO
, caso em que chama a rotina linux_rt_sendsig
. Além disso, ele aloca (ou reutiliza um contexto de identificador de sinal já existente) e cria uma lista de argumentos para o manipulador de signal. Ele traduz o número do signal baseado na tabela de tradução do signal, atribui um manipulador, traduz o sigset. Em seguida, ele salva o contexto para a rotina sigreturn
(vários registradores, número da trap traduzida e máscara de signal). Finalmente, copia o contexto do signal para o espaço do usuário e prepara o contexto para que o manipulador de sinal real seja executado.
Esta rotina é similar a linux_sendsig
apenas a preparação do contexto do sinal é diferente. Adiciona siginfo
, ucontext
e algumas partes do POSIX®. Pode valer a pena considerar se essas duas funções não poderiam ser mescladas com um benefício de menos duplicação de código e, possivelmente, até mesmo execução mais rápida.
Muitos derivados do UNIX® implementam a syscall ptrace(2) para permitir vários recursos de rastreamento e depuração . Esse recurso permite que o processo de rastreamento obtenha várias informações sobre o processo rastreado, como registros de despejos, qualquer memória do espaço de endereço do processo, etc. e também para rastrear o processo, como em uma instrução ou entre entradas do sistema (syscalls e traps). ptrace(2) também permite definir várias informações no processo de rastreamento (registros, etc.). ptrace(2) é um padrão de toda o UNIX® implementado na maioria dos UNIX®es em todo o mundo.
Emulação do Linux® no FreeBSD implementa a habilidade ptrace(2) em linux_ptrace.c
. As rotinas para converter registradores entre Linux® and FreeBSD e a atual emulação de syscall, syscall ptrace(2). A syscall é um longo bloco de trocas que implementa em contraparte no FreeBSD para todo comando ptrace(2). Os comandos ptrace(2) são em sua maioria igual entre Linux® e FreeBSD então uma pequena modificação é necessária. Por exemplo, PT_GETREGS
em Linux® opera diretamente dos dados enquanto o FreeBSD usa um ponteiro para o dado e depois performa a syscall ptrace(2) (nativa), uma cópia deve ser feita pra preservar a semantica do Linux®.
A implementação de ptrace(2) no Linuxulator tem algumas fraquezas conhecidas. Houve pânico ao usar o strace
(que é um consumidor ptrace(2)) no ambiente Linuxulator. PT_SYSCALL
também não está implementado.
Sempre que um processo Linux® executado na camada de emulação captura a própria trap, ela é tratada de forma transparente com a única exceção da tradução de trap. Linux® e o FreeBSD difere de opinião sobre o que é uma trap, então isso é tratado aqui. O código é realmente muito curto:
static int translate_traps(int signal, int trap_code) { if (signal != SIGBUS) return signal; switch (trap_code) { case T_PROTFLT: case T_TSSFLT: case T_DOUBLEFLT: case T_PAGEFLT: return SIGSEGV; default: return signal; } }
O editor de links em tempo de execução do RTLD espera as chamadas tags AUX na pilha durante uma execve
, portanto, uma correção deve ser feita para garantir isso. Naturalmente, cada sistema RTLD é diferente, portanto, a camada de emulação deve fornecer sua própria rotina de correção de pilha para fazer isso. O mesmo acontece com o Linuxulator. O elf_linux_fixup
simplesmente copia tags AUX para a pilha e ajusta a pilha do processo de espaço do usuário para apontar logo após essas tags. Então, a RTLD funciona de maneira inteligente.
A camada de emulação Linux® em i386 também suporta os binários Linux® A.OUT. Praticamente tudo o que foi descrito nas seções anteriores deve ser implementado para o suporte A.OUT (além da tradução de traps e o envio de sinais). O suporte para binários A.OUT não é mais mantido, especialmente a emulação 2.6 não funciona com ele, mas isso não causa nenhum problema, já que os ports linux-base provavelmente não suportam binários A.OUT. Esse suporte provavelmente será removido no futuro. A maioria das coisas necessárias para carregar os binários Linux® A.OUT está no arquivo imgact_linux.c
.
All FreeBSD documents are available for download at https://download.freebsd.org/ftp/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.