24/11/2013 Linux Programação

Estou criando uma ferramenta que carrega bibliotecas dinâmicas. No Windows o carregamento das bibliotecas dinâmicas (as dll) inclui o diretório atual (ou pasta corrente). No Linux não, o carregamento ocorre somente nos caminhos padrão do sistema. Há uma variável de ambiente que ajuda nisso, a LD_LIBRARY_PATH. Porém seu uso pode ser inconveniente por algumas razões, vou citar três:

  1. Segurança: o carregamento das bibliotecas ocorre primeiro no LD_LIBRARY_PATH, o que afeta todo o sistema trazendo o risco de bibliotecas com código malicioso;
  2. Performance: para cada biblioteca que um aplicativo usa serão consultados todos os locais em LD_LIBRARY_PATH, que se tiver uma lista extensa ou locais na rede pode levar a uma situação caótica;
  3. Inconsistência: os programas podem acabar carregando versões erradas das bibliotecas, levando a resultados imprevisíveis.

A lista de problemas poderia ser bem mais extensa, mas vamos nos ater ao meu caso. A ferramenta tem por objetivo simplificar as coisas, e precisar configurar uma variável de ambiente previamente não facilita em nada. A solução seria a própria ferramenta definir a variável de ambiente e carregar as bibliotecas. Mas esta é outra coisa que não funciona. O sistema verifica a LD_LIBRARY_PATH uma vez ao iniciar o programa e modificar a variável de ambiente não mudará nada a partir dai.

Mas relaxe, porque tem solução. Modificar a variável de ambiente, reiniciar o programa e carregar a biblioteca com dlopen. Vamos para a prática:

libhello.c:

#include <stdio.h>

void hello() {
	printf("Hello World!\n");
}

Para compilar: gcc -shared -fPIC libhello.c -o libhello.so

hello.c:

#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>

typedef void (*type_hello)();

int main(int argc, char *argv[]) {
	char *s = getenv("LD_LIBRARY_PATH");
	if (s != NULL && strcmp(s, ".") != 0) {
		setenv("LD_LIBRARY_PATH", ".", 1);
		execv(argv[0], argv);
	}
	void *lib = dlopen("./libhello.so", RTLD_NOW);
	if (lib == NULL) return 1;
	type_hello hello = (type_hello)dlsym(lib, "hello");
	if (hello != NULL) hello();
	dlclose(lib);
	return 0;
}

Para compilar: gcc -o hello hello.c -ldl

Um exemplo também com Free Pascal:

hello.pas:

uses dynlibs;

type
	thello = procedure;

var
	hello: pointer;
	handle: thandle = 0;

function execv(const path: pchar; const argv: ppchar): integer; cdecl; external;
function getenv(const name: pchar): pchar; cdecl; external;
function setenv(const name, value: pchar; overwrite: boolean): integer; cdecl; external;

begin
	if getenv('LD_LIBRARY_PATH') <> '.' then
	begin
		setenv('LD_LIBRARY_PATH', '.', true);
		execv(argv[0], argv);
	end;
	handle := loadlibrary('./libhello.so');
	if handle = 0 then halt(1);
	hello := getprocaddress(handle, 'hello');
	if hello <> nil then thello(hello)();
	freelibrary(handle);
end.

Para compilar: fpc hello

E enfim, é isso. Para saber mais sobre: LD_LIBRARY_PATH.

br_lemes, o Elfo insano (Múltipla Personalidade)