Tag Archives: script

Sistema Operacional miniOS-0.04: Linker

Estou continuando a serie de posts sobre como fazer um sistema operacional, fazendo o meu próprio sistema.

Quando essa parte do tutorial estiver publicada no meu blog, você já poderão conferir no repositório os fontes dos arquivos.

Agora vamos falar um pouco sobre linker e porque precisamos nos preocupar com isso quando estamos codificando um sistema operacional.

Linker

Linker

Credito Wikipédia

Um linker é um programa que vai fazer a ligação entre os diversos módulos do programa. Esses módulos podem ser arquivos objeto gerado por códigos fonte, biblioteca externas ou qualquer coisa parecida. Ele vai transformar todos os símbolos do seu programa(como variáveis e funções) em endereços de memoria física, que pode ser tanto para um arquivo no computador quanto para a memoria.

Com toda a certeza, você deve saber que o computador não usa o nome da sua função que você declara em “C” para chamá-la/executá-la. Nem o nome das variáveis, que são acessadas através de endereços de memoria também. Isso também é uma afirmação valida para quando você programa em ASM, os rótulos que você usa não são usados diretamente. Quem traduz previamente esses nomes para endereços físicos e é responsável por ligar todos esses elementos é o programa conhecido como linker.

Para construir nosso SO, precisamos mudar um pouco o comportamento desse linker, através de um linker script(que posso encurtar para chamar apenas de linker, para facilitar).

Porque precisamos de um linker?

Precisamos que o kernel siga alguns padrões para que ele possa ser reconhecido e iniciado corretamente pelos gerenciadores de boot(tal como o LILO e GRUB). Para que isso aconteça, precisamos seguir o padrão “Multi Boot”, que vamos falar um pouco mais a frente o como vamos fazer isso.

Mais para o futuro, vamos precisar que construtores e destrutores sejam executados antes do inicio da função “main()” e depois que ela terminar. Para isso, através do linker, vamos precisar saber onde eles estão guardados na memoria.

Precisamos disso porque não existe maneira de garantir isso em ASM ou em C apenas, se existisse como, poderíamos usar o linker script padrão do sistema mesmo.

O que é o Multi Boot(mboot)?

Inicializar o sistema do computador é um trabalho complicado. Existem diversos hardwares com varias formas que ficaram ainda mais complexas com o passar dos anos por causa de modos de compatibilidade e outras coisas. Para que você não se preocupe com a inicialização do computador, foi criado os gerenciadores de boot. Para que o gerenciador de inicialização entenda que seu programa é um kernel iniciável de forma mais fácil, foi criado o padrão multi boot. Existem outras formas, mas vamos usar o mboot apenas.

Para que o gerenciador de boot saiba que seu kernel usa o padrão, e para que ele receba algumas informações necessárias, no começo do arquivo do kernel(ele procura nos primeiros 8K bytes) você deve colocar uma estrutura de dados que contem um numero magico(para que ele possa identificar o começo da estrutura entre os dados) e mais alguns dados. Tudo isso mais a frente vamos codificar diretamente em ASM, mas por enquanto, precisamos apenas garantir que o linker coloque a estrutura no começo.

O multi boot tem mais de uma versão, que se diferencia pelas informações que tem na estrutura binaria no começo do kernel. Como não vamos precisar das funcionalidades da segunda versão, vamos implementar apenas a primeira mesmo.

Para entender melhor em detalhes o formato binário do mboot, você pode consultar o manual da GNU.

Mas, por enquanto não vamos implementar nada além do linker script, só depois, então não se preocupe por estar um pouco abstrato demais.

Construindo nosso linker script

memory

Esquema da memoria do nosso kernel

O Nosso script vai ser algo parecido com isso:

ENTRY(start)

SECTIONS
{
	. = 1M ;
	.text : ALIGN(4096)
	{
		code = .; _code = .; __code = .;
		KEEP(*(.mboot*))
		*(.header*) ;
		*(.text*) ;

		*(.gnu.linkonce.t*) ;
	}
	code_end = .; _code_end = .; __code_end = .;

	.rodata : ALIGN(4096)
	{
		rodata = .; _rodata = .; __rodata = .;

		start_ctors = .;
		*(SORT(.ctors*))
		end_ctors = .;

		start_dtors = .;
		*(SORT(.dtors*))
		end_dtors = .;

		*(.rodata*)
		*(.gnu.linkonce.r*)
	}
	rodata_end = .; _rodata_end = .; __rodata_end = .;

	.data : ALIGN(4096)
	{
		data = .; _data = .; __data = .;

		*(.data)
		*(.gnu.linkonce.d*)
	}
	data_end = .; _data_end = .; __data_end = .;

	.bss : ALIGN(4096)
	{
		bss = .; _bss = .; __bss = .;
		sbss = .;
		*(COMMON)
		*(.bss)
		*(.gnu.linkonce.b*)
	}
	bss_end = .; _bss_end = .; __bss_end = .;

	end = .; _end = .; __end = .;
}

Vamos agora a uma explicação mais detalhada. Vamos focar em pontos importantes mais a frente.

ENTRY(start)

Primeiramente, precisamos dizer que a função que vamos iniciar chamando é a “start“. Porque não “main()“, como o tradicional? Porque vamos precisar executar alguns códigos antes de chamar a função “main()”, que vamos descobrir melhor em passos futuros.

	. = 1M ;

Depois, vamos colocar os dados do kernel apenas depois da posição de memoria 0x00100000, mas porque? O gerenciador de boot usa a mesma memoria que o kernel, então essa memoria pode estar sendo usada para outras finalidades ou para passar dados para o kernel que acabou de iniciar. Além do que essa memoria pode estar sendo mapeada para outras coisas.

Essa posição inicial é relativa quando o programa estiver na memoria, seguindo os padrões de um arquivo executável que o gerenciador de boot vai preparar para executar. Ou seja, o arquivo real do kernel não vai ter 1M sem nada no começo(vai existir uns poucos dados do formato do binário, na verdade), apenas vai existir esse 1M de diferença quando ele estiver sendo executado na memoria.

	.text : ALIGN(4096)
	{
	....
	.rodata : ALIGN(4096)
	{
	....
	.data : ALIGN(4096)
	{
	....
	.bss : ALIGN(4096)
	{
	....

Vamos passar por todas essas secções criadas de uma vez. Elas são padrão em programas, porque basicamente precisamos das seguintes seções em um programa:

  • .text O código executável do programa fica nessa área de memoria
  • .rodata Como é de se esperar, as variáveis constantes e globais ficam nessa área de memoria
  • .data Área das variáveis globais que são inicializadas em tempo de compilação
  • .bss Variáveis globais não inicializadas ficam aqui

Outras seções, como common, sbss, gnu.linkonce.* e outras são apenas variações dessas áreas, mas são usadas pelo compilador e estão ai apenas para facilitar e prevenir erros no futuro.

Também vale a pena ressaltar que essas secções são alinhadas com paginas de memoria do tamanho de 4K, para ser mais fácil fazer paginação no futuro e ser mais otimizado.

		KEEP(*(.mboot*))

Lembra que precisamos garantir que a estrutura que identifica o kernel como mboot no começo do arquivo do kernel que vai ser gerado? É aqui que garantimos isso. Todas as seções que comecem com .mboot vão ser colocadas nesse local, no começo. O KEEP garante que ele vai ser colocado ali, independente se o linker trocar de lugar essa secção(por causa de otimizações ou por não ser referenciado em nenhum outro lugar).

		start_ctors = .;
		*(SORT(.ctors*))
		end_ctors = .;

		start_dtors = .;
		*(SORT(.dtors*))
		end_dtors = .;

Mais um pedaço de código que vamos usar mais a frente. Vamos ter a indicação de onde começa e termina os construtores e destrutores de objetos globais, justamente para executá-los no momento certo.

	end = .; _end = .; __end = .;

Futuramente, para saber qual memoria esta disponível para ser usada, vamos precisar saber onde a memoria usada pelo kernel termina. Essa marcação vai ajudar nisso. Usamos vários nomes para essa variável por comodidade.

	code_end = .; _code_end = .; __code_end = .;
....
	rodata_end = .; _rodata_end = .; __rodata_end = .;
....
	data_end = .; _data_end = .; __data_end = .;
....
	bss_end = .; _bss_end = .; __bss_end = .;

Adicionei essas marcações apenas para facilitar encontrar o final das secções. Você poderia até fazer sem elas, mas faz mais sentido fazer com as marcações. E fica mais intuitivo quando for implementar o multi boot no ASM.

Palavras finais

O que temos que focar aqui é a estrutura de como o kernel vai ser usado montado na memoria(e também no arquivo) e o que precisamos para ser compatível com o multi boot.

Aprender a linguagem de script do linker é algo que não é necessário, apenas precisamos saber por alto ela para que possamos garantir algumas coisas.

Algumas coisas aqui só vão ser utilizados mais para a frente, e a questão do boot nos próximos posts.

Caso alguém note algo que eu escrevi errado, algum conceito que esteja errado e/ou alguma dica, pode mandar mensagem para minha pessoa de forma amigável, tanto por comentário quanto por qualquer outra forma.

PS:. BUG FIX

Certo, enquanto produzia a parte a frente do post, notei que alguns bugs(o que é estranho, porque eu não erro :p) existiam no código antigo. Como já dei “push” e voltar agora seria muito complicado, caso notem alguma coisa diferente no repositório do que esta no post, pegue a ideia do que esta no post e use a versão correta. A versão correta pode ser ou do código que o post esta mostrando, ou a versão que vai aparecer.

De qualquer forma, eu já acertei esse post. Mas fica uma lista do que era:

  • Erro no CMake.
    • Faltou incluir um código para compilar o kernel em outra pasta sem ser a do projeto.
    • Pequenos ajustes para que o CMake funcionasse sem a opção de uma biblioteca C, que eu vou adicionar no futuro.
  • O Linker faltou algumas marcações

Encare essa nota de rodapé como um log de alterações do futuro. Não precisa se preocupar com essa secção, e se encontrar algum erro no continuo espaço tempo, use o do futuro :p.

Advertisements

Apresentação SIASE

Apresentei um mini-curso no SIASE(não sei o link do site…), e minha apresentação foi essa aqui.

Estou postando porque disse que ia colocar o link da apresentação no meu blog, e para falar um pouco sobre a minha impressão.

Bom, o evento pecou um pouco em organização, mas as palestras foram interessantes e as ideias boas. Realmente esse tipo de evento é o que precisa ser feito periodicamente.

Com relação a cidade, Santana(que coincidentemente é a cidade onde meu pai nasceu), tive uma boa impressão. Como em 2 lugares excelentes, um chamado capim verde e o outro Xonkantes, algo assim, que realmente foram muito bons.

A hospedagem que ficamos foi muito boa também, ótima vista, bem localizada, e com um bom preço. O quarto tinha ar-condicionado também, que devido ao calor da cidade era bem necessário…

Também fui na casa de um parente de uma amiga, e tenho a dizer que o povo de lá é legal.

Ou seja, foi uma boa viagem, um bom mini-curso, pena que não consegui descansar o tempo necessário lá, porque não fui a passeio também né? 😉


Erro screen “Cannot open your terminal ‘/dev/pts/0’ – please check”

Quem nunca passou por esse erro no screen?

"Cannot open your terminal '/dev/pts/0' - please check"

Normalmente, isso ocorre quando você vira um outro usuário na maquina, simplificando, por causa das permissões do terminal que você esta.

Para resolver isso, eu encontrei uma ótima dica, basta fazer:

script /dev/null

E depois executar o comando do screen normalmente.


Minicurso na semana da computação

Semana da computação

Foi realizado essa semana a primeira semana da computação na UFAL.

Acho isso uma grande iniciativa, o curso de computação precisa de mais eventos como esse que adicionam discussões interessantes e mais assuntos a vida dos estudantes do IC. Apoio muito essas iniciativas a acho muito importante, parabéns aos organizadores!
Infelizmente teve algumas coisas que eu queria ver e não pude :p. Seja por estar preparando o meu minicurso de bash script, ou porque eu o estava “ministrando”. Bom, fazer o que …. C’est la vie.

Falando nisso….

Bom, falando nisso, já devem saber que eu apresentei um minicurso na semana da computação convidado pelo pessoal, só tenho a dizer muito obrigado pela oportunidade.

Como prometi, eu disponibilizei um pacote com toda a apresentação aqui. Para quem não quer baixar 11M, aqui tem a primeira parte da apresentação. E para quem quer a segunda parte, ela esta aqui. Bom proveito :D.

Só tenho a dizer que eu estou sempre aqui para qualquer duvida. Qualquer coisa sempre podem me mandar e-mail ou me contatar por qualquer outro meio.


Obtendo informações do sistema de forma portavel e facil com o getent

Cenário

Imagine que ter acesso a informações do sistema, como grupos, usuários,hostnames, etc…

Cada informação dessa pode ser obtida de forma diferente em sistemas diferentes, podendo se tornar um inferno para o programador, por exemplo, procurar os usuários em uma base de dados do sistema, e depois descobrir que os usuários estão em duas bases de dados diferentes.

Para esses e outros casos, quem programa em shell script tem uma boa alternativa chamada getent. Com o getent, você acessar entradas que estão no banco de dados administrativo do sistema sem depender de qual banco de dados o sistema esta usando.

Bom, e como usamos? Muito simples, o getent é usado da seguinte forma:

getent [Base de Dados] [chave]

Explicando:

Base de Dados: é a base de dados que você quer acessar :p. Se você esta procurando por algum usuário, use o “passwd“. Se você esta procurando pelo nome do host use “ahost“. Algumas das bases que o getent pode suportar são:

ahosts ahostsv4 ahostsv6 aliases ethers group hosts netgroup networks passwd protocols rpc services shadow

chave: Algo do bando de dados. Isso, como vocês podem perceber, varia de banco de dados a banco de dados. Por exemplo, a chave usada no banco de dados do passwd é um usuário. Um usuário também é usado no “shadow“, que fornece as senhas criptografadas do sistema. Mas, no “ahost” que mostra os nomes de cada ip, seria um ip a chave do banco de dados.

A chave filtra o banco de dados, deixando somente o que você precisa para trabalhar. Se você omite a chave, todo o banco de dados é mostrado. Tenha muito cuidado para saber onde você precisa de todo o banco de dados ou apenas uma entrada, para não ficar dando greps a toa no programa.


Livro: programação shell linux

Acabei de ler o livro: Programação Shell linux, do vovô do shell script. Eu acabei de ver a 5ª edição, só para esclarecer :p.

Bom, só tenho a dizer que o filme é bem legal. Explica tudo sobre shell script, e ainda de forma bem legal. Veja que eu li a 5ª edição, e já esta na 7ª edição ;).

Explica tudo mesmo, do básico, como variáveis, condicionais e comandos básicos, até coisas muito mais avançadas, como parâmetros, pipes e programas mais avançados.

Só tenho a dizer que gostei muito do livro, e recomendo. Posso dizer que o livro, junto com o livro do Aurélio(Esse eu vou falar depois…), é um tratado final sobre como programar em shell script.

 

Bom, fica ai a dica. Recomendo, e quem quiser eu tenho ;).


%d bloggers like this: