Construção do executável1 Construção de ficheiros executáveis Compilação Ligação...
Transcript of Construção do executável1 Construção de ficheiros executáveis Compilação Ligação...
Construção do executável 1
Construção de ficheiros Construção de ficheiros executáveisexecutáveis
CompilaçãoLigação
Carregamento (loading)Bibliotecas estáticas e dinâmicas
Construção do executável 2
Construção e execução de programasConstrução e execução de programasConstrução e execução de programasConstrução e execução de programas
Compilador Linker
dados
código
stack
Loader
Memória(espaço lógico ou virtual)
Ficheirosfonte
Bibliotecasexternas
00101 11010 00101
obj
00101 11010 00101
obj
00101 11010 00101
obj
00101 11010 00101
exe
00101 11010 00101
lib
Unidades decompilação
Construção do executável 3
Ligação de unidades de compilaçãoLigação de unidades de compilaçãoLigação de unidades de compilaçãoLigação de unidades de compilação
...int x;
void main( ){...foo( )...x = ......}
...int y;
...void foo( ){ y = x; ... bar( )}
void bar( ){ ...}
Código main: ... r1 &foo ... call foo ... r2 x (300) ...
Dados ... x: ...
Importações
Exportações
Recolocações
foo
x
x
80
0h
30
0h
50
0h
Código foo: r1 x r2 y (400) ... call bar (1000) ... bar: ...
Dados ... y: ...
Importações
Exportações
Recolocações
x
foobary
bary
16
00
h
40
0h
60
0h
10
00
h
40
0h
Construção do executável 4
Ligação de unidades de compilação Ligação de unidades de compilação (cont.)(cont.)
Ligação de unidades de compilação Ligação de unidades de compilação (cont.)(cont.)
Código main: ... r1 &foo (c00) ... call foo (c00) ... r2 x (2300) ... foo: r1 x (2300) r2 y (2900) ... call bar (1800) ... bar: ...
Dados ... x: ... y: ...
1e
00
h
c00
h1
80
0h
20
00
hb
00
h
90
0h 30
0h
Imagem executável
Dados
Heap
Código
Serviços do SO
Stack
bibliotecasdinâmicas
Memória(lógica ou virtual)
“Loading”
4 G
B
endereçoscrescentes
Construção do executável 5
Bibliotecas estáticas e dinâmicasBibliotecas estáticas e dinâmicasBibliotecas estáticas e dinâmicasBibliotecas estáticas e dinâmicas Bibliotecas estáticas
O código é incorporado no ficheiro executável pelo “linker”Se a mesma biblioteca for usada por vários programas o código é
repetido nos ficheiros executáveisO código também é repetido na memória nos vários processos que
as utilizaremSimples de criar e com execução ligeiramente mais rápida
Bibliotecas dinâmicasO código reside apenas no ficheiro da bibliotecaÉ carregado e mapeado no processo quando da execução de um
programaO código é partilhado por todos os processos que o utilizam (pode
haver diversas cópias dos dados)“Loading” e inicialização mais complicados e demoradosLigeiramente menos eficientes
Construção do executável 6
Bibliotecas dinâmicasBibliotecas dinâmicasBibliotecas dinâmicasBibliotecas dinâmicas
.
.
.
.
.
.
Código DLL
CódigoCódigo
Dados Dados
Código DLL Código DLLDados proc. 1
Dados proc. 2Dados DLL Dados DLL
Memória físicaMapeamento(tabelas de
páginas)
Mapeamento(tabelas de
páginas)
Memória lógica(processo 1)
Memória lógica(processo 2)
Construção do executável 7
Bibliotecas dinâmicas - Bibliotecas dinâmicas - implementaçãoimplementação
Bibliotecas dinâmicas - Bibliotecas dinâmicas - implementaçãoimplementação
...---call foo: push gp t9 *(gp+A) jalr t9 pop gp
---load x: t0 *(gp+C)
---load y: t0 *(gp+B) t0 *t0...
x
gpAB
C
...foo: gp t9+E-D......---load y: t0 *(gp+F) t0 *t0...
y
gpF
D
E
tabela deligação
(linkage table)
1 cópia porprocesso
1 cópia porprocesso
(adjacente)
código da DLL(partilhado)
Organização da memória em run-time 8
Organização da memória em Organização da memória em run-run-timetime
Memória de dadosRegistos de activação
Procedimentos embutidos e acessos não locais
Passagem de parâmetros
Organização da memória em run-time 9
Execução de programasExecução de programasExecução de programasExecução de programas Geralmente é possível fazer as seguintes suposições acerca
da execução de um programa:Alguns blocos de código constituem procedimentos (ou funções) que
podem ser invocados de diversos locais no programa;Quando um procedimento é invocado, o controlo regressa após a sua
execução, ao ponto imediatamente após a invocação; Esta suposição leva a que o fluxo de controlo entre procedimentos possa
ser modelizado através de uma árvore.
Os procedimentos podem conter dados locais e também (de forma embutida) outros procedimentos (nalgumas linguagens).
Durante a execução de um procedimento constitui-se o que se designa por uma activação do procedimento.
Num dado instante podem existir várias activações de procedimentos (inclusivé do mesmo, em linguagens recursivas).
Organização da memória em run-time 10
Algumas definiçõesAlgumas definiçõesAlgumas definiçõesAlgumas definições Tempo de vida de uma activação de procedimento: intervalo
durante o qual essa activação existe; compreende:a execução de todas as instruções do procedimento;os tempos de vida das activações dos procedimentos invocados por
ele.
Tempo de vida de uma variável: intervalo durante o qual existe o espaço de memória necessário ao armazenamento dos valores associados à variável; se a variável for local a um procedimento, a regra é fazer corresponder o seu tempo de vida ao tempo de vida da activação do procedimento (há excepções).
A associação de um nome ao local de armazenamento, e a associação deste local ao seu conteúdo (valor), designam-se genericamente por binding (ligação) da variável.
O scope (alcance) de uma variável, é o conjunto de locais, no código, onde é possível aceder a essa variável.
Noções estáticasNoções estáticas Noções dinâmicas (execução)Noções dinâmicas (execução)
procedimentoprocedimento activaçãoactivaçãonomenome bindingbindingscopescope tempo de vidatempo de vida
Organização da memória em run-time 11
ExemploExemploExemploExemploprogram sort;program sort; var a : array[0..10] of integer; var a : array[0..10] of integer;
procedure readarray;procedure readarray; var i : integer; var i : integer; begin begin for i:=1 to 9 do read(a[i]) for i:=1 to 9 do read(a[i]) end; end;
function partition(y, z : integer) : integer;function partition(y, z : integer) : integer; var i, j, x, v : integer; var i, j, x, v : integer; begin … begin … end; end;
procedure quicksort(m, n : integer);procedure quicksort(m, n : integer); var i : integer; var i : integer; begin begin if (n > m) then begin if (n > m) then begin i := partition(m, n); i := partition(m, n); quicksort(m, i-1); quicksort(i+1, n) quicksort(m, i-1); quicksort(i+1, n) end end end; end;
beginbegin a[0] := -9999; a[10] := 9999; a[0] := -9999; a[10] := 9999; readarray; readarray; quicksort(1, 9) quicksort(1, 9) end. end.
s
r q(1,9)
p(1,9) q(1,3) q(5,9)
p(1,3) q(1,0) q(2,3) p(5,9) q(5,5) q(7,9)
p(2,3) q(2,1) q(3,3) p(7,9) q(7,7) q(9,9)
Árvore de activação para o programa escrito ao ladoÁrvore de activação para o programa escrito ao lado
Em cada instante só existem os registos de activação de Em cada instante só existem os registos de activação de procedimentos que estão no caminho desde a raiz até ao procedimentos que estão no caminho desde a raiz até ao que está em execução no momento.que está em execução no momento.
Quando a activação da chamada quicksort(2,3) (q(2,3)) está Quando a activação da chamada quicksort(2,3) (q(2,3)) está em execução apenas existem as activações de s, q(1,9) e em execução apenas existem as activações de s, q(1,9) e q(1,3).q(1,3).
Organização da memória em run-time 12
Organização da memória de dadosOrganização da memória de dadosOrganização da memória de dadosOrganização da memória de dados A organização da memória de dados depende grandemente
da resposta às seguintes perguntas, para uma dada linguagem:A linguagem é recursiva ?O que acontece aos nomes locais quando termina a activação de um
procedimento ?Um procedimento pode aceder a nomes não locais ?Como são passados os parâmetros de um procedimento ?É possível passar procedimentos como parâmetros ?É possível retornar um procedimento como resultado ?É possível alocar memória dinamicamente ?É necessário libertar explicitamente memória alocada
dinamicamente ? Para a maioria das linguagens actualmente em utilização,
as activações dos procedimentos é perfeitamente embutida pelo que é comum adoptar-se uma organização geral destas activações baseada numa stack (geralmente suportada directamente pelo hardware do processador).
Organização da memória em run-time 13
Organização da memória de dados Organização da memória de dados (cont.)(cont.)
Organização da memória de dados Organização da memória de dados (cont.)(cont.)
Dados estáticosDados estáticos - Esta parte da memória contém os - Esta parte da memória contém os dados globais (constantes e variáveis inicializadas ou dados globais (constantes e variáveis inicializadas ou não) cujo tamanho pode ser determinado inteiramente não) cujo tamanho pode ser determinado inteiramente durante a compilação. Na compilação geram-se durante a compilação. Na compilação geram-se apenas “offsets” para o início desta área de memória.apenas “offsets” para o início desta área de memória.
StackStack - Implementado no stack suportado - Implementado no stack suportado directamente pelo processador; contém os directamente pelo processador; contém os chamados registos de activação dos procedimentos, chamados registos de activação dos procedimentos, refletindo a estrutura intrínseca das suas chamadas e refletindo a estrutura intrínseca das suas chamadas e retornos; as variáveis locais dos procedimentos são retornos; as variáveis locais dos procedimentos são criadas e destruídas nesta área.criadas e destruídas nesta área.
HeapHeap - Área utilizada para todo o restante - Área utilizada para todo o restante armazenamento de dados. São colocados aqui os armazenamento de dados. São colocados aqui os dados alocados dinamicamente, ou variáveis locais dados alocados dinamicamente, ou variáveis locais cujo tamanho só é conhecido durante a execução, ou cujo tamanho só é conhecido durante a execução, ou ainda cujo tempo de vida não se coaduna com o ainda cujo tempo de vida não se coaduna com o armazenamento nos registos de activação.armazenamento nos registos de activação.
códigoendereçosmais baixos
endereçosmais altos
dados estáticosou globais
stack
heap
Organização da memória em run-time 14
Registos de activaçãoRegistos de activaçãoRegistos de activaçãoRegistos de activação
Chamada de procedimento:Chamada de procedimento:
……push parpush parnn
……push parpush par11
push accesspush accesscall proccall procadd sp,n1add sp,n1……
No procedimento proc:No procedimento proc:
push bppush bpmov bp,sp código demov bp,sp código desub sp,n2 entradasub sp,n2 entradapushapusha…………popapopaadd sp,n2 códigoadd sp,n2 códigopop bp de saídapop bp de saídaretret
valor de retorno
parâmetrosdo
procedimento
apontador de acesso
endereço de retorno
salvaguardade registos doprocessador
dados locais
temporários
apontador de controloregisto deactivação
apontador de controlo
frame pointer(bp)
stack pointer(sp)
chamador
hardware
chamado
Organização da memória em run-time 15
Alocação de registos de activaçãoAlocação de registos de activaçãoAlocação de registos de activaçãoAlocação de registos de activação Consideram-se geralmente três estratégias diferentes
para alocação dos registos de activação dos procedimentos:Alocação estática, onde os registos de activação de todos os
procedimentos são colocados na zona de dados estáticos;Este tipo de alocação só é possível se a linguagem não for recursiva e mantiver os valores das variáveis locais de umas chamadas para as outras;
Alocação na stack, a mais geralmente utilizada;Alocação no heap, necessária para quando o tempo de vida das
variáveis locais ultrapassa o da activação do respectivo procedimento, ou quando este ultrapassa o do chamador.
Considere-se o seguinte código em pseudo-Considere-se o seguinte código em pseudo-C:C:
int (*)() f(int x) {int (*)() f(int x) { int g(int y) {return x+y} int g(int y) {return x+y} return g; return g;}}
main() {main() { int (*h)() = f(3); int (*h)() = f(3); int (*j)() = f(4); int (*j)() = f(4); int z = h(5); int z = h(5); int w = j(7); int w = j(7);}}
O registo de activação de f() não pode ser O registo de activação de f() não pode ser descartado após as chamadas f(3) e f(4), descartado após as chamadas f(3) e f(4), porque a função g() no seu interior depende porque a função g() no seu interior depende do parâmetro com que f() foi chamado.do parâmetro com que f() foi chamado.
A linguagem ML permite estas construções.A linguagem ML permite estas construções.
Organização da memória em run-time 16
Alocação na stackAlocação na stackAlocação na stackAlocação na stackÉ a forma mais utilizada de alocação
dos registos de activação;
Pode requerer espaços para a stack consideráveis;
Os parâmetros devem ser colocados na stack pela ordem inversa, se se permitirem definições de procedimentos com número variável de argumentos (a la C);
Requer acesso indirecto se existirem parâmetros cujo comprimento dependa de um valor passado como parâmetro (ex: passagem de arrays em Algol e arrays conformantes em Pascal).
valor de retorno
parâmetrosdo
procedimento
apontador de acesso
endereço de retorno
salvaguardade registos doprocessador
dados locais
temporários
apontador de controlo frame pointer(bp)
stack pointer(sp)
array A
apontador para A
procedure p(A : array[ j..k : integer ] of integer ) begin var i : integer; for i := j to k do A[ i ] := 2*i; end;
Organização da memória em run-time 17
Acesso a variáveis não locaisAcesso a variáveis não locaisAcesso a variáveis não locaisAcesso a variáveis não locais Grande parte das linguagens utiliza o que se designa por
estrutura de blocos; os blocos estão embutidos uns nos outros;
O scope (alcance) de uma variável, numa estrutura de blocos, está sujeito à regra do embutimento mais próximo. Assim:O scope de uma declaração num bloco B abrange todo o bloco B;Um nome x, acedido em B mas não declarado em B, pertencerá ao
scope de uma declaração de x num bloco mais externo B’, que contenha essa declaração e seja o bloco mais próximo de B (de dentro para fora)
Algumas linguagens não permitem o embutimento de funçõesB0B0 B1B1
B2B2
B3B3
Neste tipo de blocos inseridos dentro dos Neste tipo de blocos inseridos dentro dos procedimentos, só existe um registo de activação procedimentos, só existe um registo de activação para todos os blocos:para todos os blocos:
aa00
bb00
bb11
aa22, b, b33
Variáveis locais a Variáveis locais a main( )main( )
main() {main() { int a, b; int a, b; { int b; { int b; { int a; { int a; … } … } { int b; { int b; … } … } … } … }… }… }
Organização da memória em run-time 18
Acesso a variáveis não locais Acesso a variáveis não locais (embutidas)(embutidas)
Acesso a variáveis não locais Acesso a variáveis não locais (embutidas)(embutidas)
Nas linguagens em que é permitido o embutimento de Nas linguagens em que é permitido o embutimento de procedimentos há necessidade de aceder a variáveis procedimentos há necessidade de aceder a variáveis locais existentes em registos de activação anteriores.locais existentes em registos de activação anteriores.
Esses registos são acedidos através de um apontador Esses registos são acedidos através de um apontador de acesso que funciona como mais um parâmetro de acesso que funciona como mais um parâmetro (escondido) nas chamadas dos procedimentos.(escondido) nas chamadas dos procedimentos.
No entanto esses registos podem não estar contíguos No entanto esses registos podem não estar contíguos na stack. Vejam-se os seguintes exemplos:na stack. Vejam-se os seguintes exemplos:
program sort;program sort; var a : array[0..10] of integer; var a : array[0..10] of integer; x : integer; x : integer;
procedure readarray;procedure readarray; var i : integer; var i : integer; begin … a … end; begin … a … end;
procedure exchange(i, j : integer);procedure exchange(i, j : integer); begin begin x := a[i]; a[i] := a[j]; a[j] := x x := a[i]; a[i] := a[j]; a[j] := x end; end;
procedure quicksort(m, n : integer);procedure quicksort(m, n : integer); var k, v : integer; var k, v : integer;
function partition(y, z : integer) : integer;function partition(y, z : integer) : integer; var i, j : integer; var i, j : integer; begin begin … a … v … exchange(i, j); … … a … v … exchange(i, j); … end; end;
begin … end;begin … end;
begin … end.begin … end.
s
a, x
acesso
nil
q(1, 9)
k, v
s
a, x
acesso
nil
q(1, 3)
k, v
acessoq(1, 9)
k, v
acessop(1, 3)
i, j
s
a, x
acesso
nil
q(1, 3)
k, v
acessoq(1, 9)
k, v
acessop(1, 3)
i, j
s
a, x
acesso
nil
q(1, 3)
k, v
acessoq(1, 9)
k, v
acessoe(1, 3)
Organização da memória em run-time 19
Uso dos apontadores de acessoUso dos apontadores de acessoUso dos apontadores de acessoUso dos apontadores de acessoDefine-se profundidade de embutimento de um procedimento como
o número de níveis de embutimento que é necessário transpor para chegar a esse procedimento; os procedimentos mais externos têm profundidade 1; no exemplo anterior temos sort - 1, exchange - 2, e partition - 3.
Estabelcimento dos apontadores de acesso:
Utilização dos apontadores de acesso:
Os apontadores de acesso são colocados na stack (registo de activação) pelo Os apontadores de acesso são colocados na stack (registo de activação) pelo procedimento chamador; Se o procedimento p com profundidade np chama o procedimento chamador; Se o procedimento p com profundidade np chama o procedimento x de profundidade nx então:procedimento x de profundidade nx então: - para np < nx (deverá ser nx = np + 1) o apontador de acesso é o apontador de controlo - para np < nx (deverá ser nx = np + 1) o apontador de acesso é o apontador de controlo (frame pointer) actual;(frame pointer) actual; - para np - para np nx o apontador de acesso a colocar na stack obtém-se seguindo np - nx + 1 nx o apontador de acesso a colocar na stack obtém-se seguindo np - nx + 1 apontadores de acesso, começando no do próprio procedimento; (ver figura anterior)apontadores de acesso, começando no do próprio procedimento; (ver figura anterior)
O procedimento p com profundidade np pretende aceder a uma variável x, declarada num O procedimento p com profundidade np pretende aceder a uma variável x, declarada num procedimento com profundidade nx; deverá sempre ser np procedimento com profundidade nx; deverá sempre ser np nx; nx; - se np = nx, então x está no registo de activação corrente; - se np = nx, então x está no registo de activação corrente; - se np > nx, então x está num registo de activação cujo endereço se obtém seguindo - se np > nx, então x está num registo de activação cujo endereço se obtém seguindonp-nx apontadores de acesso, a partir do registo de activação actual. (ver figura anterior)np-nx apontadores de acesso, a partir do registo de activação actual. (ver figura anterior)
Organização da memória em run-time 20
Procedimentos como parâmetrosProcedimentos como parâmetrosProcedimentos como parâmetrosProcedimentos como parâmetrosQuando é possível passar procedimentos como parâmetros, o
procedimento passado pode não estar na cadeia de embutimentos do procedimento que o vai usar; neste caso quando da passagem do procedimento, deve também passar-se o apontador de acesso correspondente. Veja-se o exemplo seguinte:
program param;program param;
procedure b(function h(n: integer): integer);procedure b(function h(n: integer): integer); begin writeln(h(2)) end; begin writeln(h(2)) end;
procedure c;procedure c; var m: integer; var m: integer;
function f(n: integer) : integer;function f(n: integer) : integer; begin f := m + n end; begin f := m + n end;
begin m := 0; b(f) endbegin m := 0; b(f) end
beginbegin c c end. end.
param
c
m
<f, acesso>
b
h = f
n
Organização da memória em run-time 21
ScopeScope dinâmico dinâmicoScopeScope dinâmico dinâmicoCertas linguagens não seguem as regras de Certas linguagens não seguem as regras de scopescope enunciadas atrás ( enunciadas atrás (scopescope estático ou léxico), mas estático ou léxico), mas determinam que quando há um acesso a uma variável não local, esse acesso refere-se a uma variável com o determinam que quando há um acesso a uma variável não local, esse acesso refere-se a uma variável com o mesmo nome que tenha sido criada o mais recentemente possível na stack.mesmo nome que tenha sido criada o mais recentemente possível na stack.
Se o programa seguinte utilizasse este tipo de Se o programa seguinte utilizasse este tipo de scopescope dinâmico, a sua saída seria: dinâmico, a sua saída seria: 0.250 0.125 0.250 0.125 0.250 0.125 0.250 0.125
A implementação deste A implementação deste scopescope dinâmico é imediata: em vez de se utilizarem os apontadores de acesso dinâmico é imediata: em vez de se utilizarem os apontadores de acesso utilizam-se os apontadores de controlo, que ligam os registos de activação pela ordem com que foram utilizam-se os apontadores de controlo, que ligam os registos de activação pela ordem com que foram criados; usa-se a variável que esteja definida no registo de activação que menos distar do actual.criados; usa-se a variável que esteja definida no registo de activação que menos distar do actual.
program dynamic;program dynamic; var r: real; var r: real;
procedure show;procedure show; begin writeln(r:5:3) end; begin writeln(r:5:3) end;
procedure small;procedure small; var r: real; var r: real; begin r := 0.125; show end; begin r := 0.125; show end;
beginbegin r := 0.250; r := 0.250; show; small; writeln; show; small; writeln; show; small; writeln show; small; writeln end. end.
Organização da memória em run-time 22
Passagem de parâmetros (1)Passagem de parâmetros (1)Passagem de parâmetros (1)Passagem de parâmetros (1) Passagem por valor
Uma cópia dos valores dos parâmetros é colocada nos registos de activação.
Passagem por referênciaAquilo que é colocado nos registos de activação é o endereço do
local onde sencontram os valores dos parâmetros (variáveis simples); o acesso a esses valores faz-se indirectamente; assim é possível passar para o chamador novos valores através dos parâmetros.
Passagem por copy-restore linkageO chamador passa para os registos de activação os r-values dos
parâmetros, ao mesmo tempo que toma nota dos respectivos l-values; quando a chamada regressa, e antes de eliminar os parâmetros, os respectivos valores são colocados nos l-values anteriormente calculados.
Organização da memória em run-time 23
Passagem de parâmetros (2)Passagem de parâmetros (2)Passagem de parâmetros (2)Passagem de parâmetros (2) Passagem por nome
Aqui o que é colocado nos registos de activação são subrotinas (thunks) capazes de calcular o l-value e o r-value do parâmetro correspondente. Sempre que há um acesso ao parâmetro essa subrotina é chamada. Tudo se passa como se não houvesse qualquer chamada e o código chamado fosse colocado directamente no local de chamada (como uma macro).real procedure sum(expr, i, low, high) value low, high; real expr; integer i, low, high;begin real rtn; rtn := 0; for i := low step 1 until high do rtn := rtn + expr; sum := rtn;end sum
...y := sum(3*x*x-5*x+2, x, 1, 10);...
101
2 253x
xxy