[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Vafan?



> > > Av rent intresse, vad är SONAME?
> > 
> > Namnet på biblioteket som loadern använder när den skall resolva
> > symbolnamn.
> > 
> > $ ldd /bin/ld
> > 	librt.so.1 => /lib/librt.so.1 (0x4001a000)
> > 	libc.so.6 => /lib/libc.so.6 (0x4002b000)
> > 	libpthread.so.0 => /lib/libpthread.so.0 (0x40146000)
> > 	/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
> > libc.so.6, librt.so.1 etc. är SONAME.
> 
> Jag tackar och bockar!
> 
> > Anledning att du byter SONAME är att biblioteken inte är binär
> > kompatibla på ett eller annat vis. Alltså fungerar med stor sanolikhet
> > inte ett program som är länkat mot libvorbisfile0 med libvorbisfile3.
> > Detta är givetvis beroende på vilken del av ABI som används av
> > programmet i fråga.
> 
> Där dök den upp igen! Denna märkliga akronym, ABI, som dök upp i Min Verklighet för ett litet tag sedan när jag såg att den senaste versionen av gcc hade... öh... ändrat något som hade med ABI att göra. Det var för svårt för mig att förså vad det var... ARGH! Vad jag känner mig dum ibland.
> 
> > Hoppas det blev något klarare.
> 
> Absolut, tack än en gång.
> 
> /Fredrik Persson
> 
> 

Känn dig inte dum redan. Ett välkännt faktum är att ju mer man kan om
en sak, destå mer blir man medveten om hur lite man egentligen vet!


Ett exempel på ett ABI är:

  http://www.caldera.com/developers/gabi/2000-07-17/contents.html

som anger System V's ABI. Man tar upp begrepp som objektfiler,
programladdning och dynamisk länkning.

ABI är utrikiska (USAiska närmare bestämt) och är en förkortning för
"Application Binary Interface", vilket inte gör en så speciellt mycket
klokare (se ovanstående mening).

Men på svenska betyder det ungefär:

Tänk dig att du gör ett program på din dator.
Du kompilerar (shit, ännu en term (jäklar "shit" va visst utrikiska))
och kör sedan programmet:

  $ gcc hello.c -o hello
  $ ./hello
  hello world!

Det du utgår från är källkod (t.ex någonting du skrivit i C), typ:
Filen hello.c:

  #include "stdio.h"

  int main( int argc, char * argv[] ) {
    printf( "hello world!\n");
    return 0;
  }

** preprocessorn **

gcc gör om detta i olika steg. Första steget är "preprocessorn"
(-E stoppar gcc efter preprocessorn (gcc gör inte preprocessorsteget
själv utan låter ett annat program "cpp" göra det),
sed-commandot tar bort tomma rader och head;echo; tail visar de
första och sista raderna med ... mellan av utskriften):

  $ gcc -E hello.c | sed -e '/^[ \t]*$/d' | (head -5; echo ...; tail -6)
  # 1 "hello.c"
  # 1 "/usr/include/stdio.h" 1 3
  # 1 "/usr/include/features.h" 1 3
  # 142 "/usr/include/features.h" 3
  # 208 "/usr/include/features.h" 3
  ...
  extern void funlockfile (FILE *__stream)  ;
  # 1 "hello.c" 2
  int main( int argc, char * argv[] ) {
    printf( "hello world!\n");
    return 0;
  }

Vilket fortfarande är c-kod fast utan #include och #define.
Raderna typ # 1 "hello.c" är till för att kompilatorn ska kunna veta
var i din fil ev. fel skedde.
Raden med "extern" fanns i någon av include-filerna
(stdio.h har andra #include i sig).

** kompilering **

Nästa steg är att "kompilera", i gcc's fall att göra om det till
assembler (-S gör att gcc produserar en .s (assembler) -fil,
sköts av "cc1"):

  $ gcc -S hello.c                                                      
  $ cat hello.s 
	  .file   "hello.c"
	  .version        "01.01"
  gcc2_compiled.:
  .section        .rodata
  .LC0:
	  .string "hello world!\n"
  .text
	  .align 4
  .globl main
	  .type    main,@function
  main:
	  pushl %ebp
	  movl %esp,%ebp
	  subl $8,%esp
	  addl $-12,%esp
	  pushl $.LC0
	  call printf
*	  addl $16,%esp
	  xorl %eax,%eax
*	  jmp .L2
*	  .p2align 4,,7
* .L2:
	  leave
	  ret
  .Lfe1:
	  .size    main,.Lfe1-main
	  .ident  "GCC: (GNU) 2.95.4 20011002 (Debian prerelease)"

Assembler är så nära binärkod vi kan komma medan det fortfarande är
"läsbart".

** optimering **

Efter detta kan gcc "optimera" (göra den mindre och/eller snabbare)
koden (t.ex med "gcc -O2 ...").
Raderna jag markerat med "*" ovan tas bort av gcc om du kör med -O2.

** assemblering **

Nästa steg är att "assemblera", vilket sköts av "as". Du får då
en så kallad objektfil (.o):

  $ gcc -c hello.c
  $ cat -v hello.o | fold -w60 -
  ^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^A^@^C^@^A^@^@^@^@^@^@^@^@^@^@^
  @^L^A^@^@^@^@^@^@4^@^@^@^@^@(^@^K^@^H^@^@^@^@^@^@^@^@^@^@^@^
  @^@UM-^IM-eM-^CM-l^XM-^KE^HM-^IEM-|M-^CM-DM-th^@^@^@^@M-hM-|
  M-^?M-^?M-^?M-^CM-D^P1M-@M-k^@M-IM-CM-^MM-4&^@^@^@^@M-^MM-<'
  ^@^@^@^@^H^@^@^@^@^@^@^@^A^@^@^@01.01^@^@^@hello world!
  ^@^@GCC: (GNU) 2.95.4 20011002 (Debian prerelease)^@^@.symta
  b^@.strtab^@.shstrtab^@.rel.text^@.data^@.bss^@.note^@.rodat
  a^@.comment^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^
  @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^_^@^@^@^A^@^@^@^F^@^
  @^@^@^@^@^@@^@^@^@0^@^@^@^@^@^@^@^@^@^@^@^P^@^@^@^@^@^@^@^[^
  @^@^@   ^@^@^@^@^@^@^@^@^@^@^@M-^X^C^@^@^P^@^@^@        ^@^@
  ^@^A^@^@^@^D^@^@^@^H^@^@^@%^@^@^@^A^@^@^@^C^@^@^@^@^@^@^@p^@
  ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^D^@^@^@^@^@^@^@+^@^@^@^H^@^@^@^
  C^@^@^@^@^@^@^@p^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^D^@^@^@^@^@^@
  ^@0^@^@^@^G^@^@^@^@^@^@^@^@^@^@^@p^@^@^@^T^@^@^@^@^@^@^@^@^@
  ^@^@^A^@^@^@^@^@^@^@6^@^@^@^A^@^@^@^B^@^@^@^@^@^@^@M-^D^@^@^
  @^N^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@>^@^@^@^A^@^@^@^@^@
  ^@^@^@^@^@^@M-^R^@^@^@0^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^
  @^Q^@^@^@^C^@^@^@^@^@^@^@^@^@^@^@M-B^@^@^@G^@^@^@^@^@^@^@^@^
  @^@^@^A^@^@^@^@^@^@^@^A^@^@^@^B^@^@^@^@^@^@^@^@^@^@^@M-D^B^@
  ^@M-0^@^@^@
  ^@^@^@  ^@^@^@^D^@^@^@^P^@^@^@  ^@^@^@^C^@^@^@^@^@^@^@^@^@^@
  ^@t^C^@^@$^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@
  ^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^D^@M-qM-^?^@^@^
  @^@^@^@^@^@^@^@^@^@^C^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^C^@^
  @^@^@^@^@^@^@^@^@^@^@^@^C^@^D^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^A
  ^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^F^@^@^@^@^@^@^@^@^@^@^@^@^@^C
  ^@^E^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^G^@^X^@^@^@^@^@^@^@"^@^@^
  @^R^@^A^@^]^@^@^@^@^@^@^@^@^@^@^@^P^@^@^@^@hello.c^@gcc2_com
  piled.^@main^@printf^@^P^@^@^@^A^F^@^@^U^@^@^@^B
  ^@^@

Den är ju inte direkt läsbar.
Den innehåller instruktionerna (binärkoden) som du specade i c-koden
på ett sådant sätt som processorn förstår.

** länkning **

Men det räcker inte med det för att kunna köra filen:

  $ chmod 755 hello.o 
  $ ./hello.o 
  bash: ./hello.o: cannot execute binary file
  $

eftersom filen inte är uppbyggd så att man köra den direkt.

Den innehåller ett anrop på printf man inte vad prinf gör.
#include <stdio.h> som fanns i källkoden talar bara om hur man anropar
printf (dvs. API't), men inte hur man gör när man gör "printf".
Dessutom, printf vet hur den ska skriva ut, men den har inte någon
hum om hur man får stdout att bli skärmen. Till det behövs någon
kontakt med omgivningen (görs av lib som heter libcrt och liknande,
gör gcc -v hello.c för att se vilka program som gcc använder och deras
argumentlista, där ser också du vilka lib som automagiskt följer med).

 API betyder "Application Programming Interface" eller något liknande,
 och anger hur man gör i källkoden
 (typ: "#include <stdio.h>" och printf("hello world!\n")) för att 
 utnyttja det (printf i det här fallet) som någon annan gjort.

Man har istället gjort .o filer så att det ska vara lätt att
kombinera dem med varandra. Detta kallas för att länka filerna.
Program för att hantera objektfiler finns i binutils:

  $ echo `cat /var/lib/dpkg/info/binutils.list | grep /usr/bin/ |
  > sed -e 's|/usr/bin/||g'` | fold -w60 -b
  size objdump ar strings ranlib objcopy addr2line readelf nm
  strip c++filt as gprof ld

(Kolla gärna upp deras mansidor eller kör "info binutils").
Det finns olika objektfilsformat. Det som används nu kallas ELF.
Ett gammalt format heter COFF, och ett annat hetar a.out.
Kör objdump -i för att se alla som binutils känner till.

 http://www.linuxjournal.com/article.php?sid=1059
 http://www.linuxjournal.com/article.php?sid=1060

berättar lite om elf-formatet.

Nåväl, objektfiler är indelade i något som kallas för "sections"
(det är väl taget från litteraturen, meningarna i en bok är grupperade
i "sections" dvs. stycken).

I en "section" som kallas för "text" finns binärkoden, och i "symtab"
finns en lista över alla symboler i objektfilen. En symbol är helt
enkelt en funktion, variabel eller något annat som vi behöver veta
vilken adress den befinner sig på. För man behöver i vårt falla para ihop
anropet "printf" men adressen i något lib där printf's kod finns. Och
då tittar men i alla libben som gcc angett, i deras sektionen symtab
om det finns någon symbol som heter printf som har ett värde, dvs.
koden finns där på den adressen (hmm, lätt förenklat förståss).

Nå, var var vi. Jo, efter assemblering måste vi "länka" objektfilerna.
Dvs. vi måste:
. para ihop alla anrop till funktioner och användningen av globala
  variabler som inte är definierade i samma objektfil
. slå ihop alla objektfiler som angets på kommandoraden
. lägga till alla statiska bibliotek (lib)
. lägga till info så att vi kan länka (igen) alla dynamisk bibliotek
  när vi ska köra programmet
. skriva ut detta på disken som filen/programmet "hello" (i mitt exempel)

** programladdning och dynamisk länkning **

Sedan när vi ska "köra" programmet, så måste vi ladda programet till
minnet och göra den sista dynamiska länkningen.

DVS.:

Ett ABI är en specifikation. Den ska se till att alla steg (från att
man har objektfiler till det att man kan köra programmet) fungerar.
För att detta ska fungera måste ABI't speca objektfilernas format,
programladdningen och den dynamiska länkning.

Hälsningar,
/Karl

-----------------------------------------------------------------------
Karl Hammar                    Aspö Data           karl@kalle.csb.ki.se
Lilla Aspö 2340               0173 140 57                       Nätverk
S-742 94 Östhammar            018 260 900            Datorer/Utrustning
Sweden                       010 270 26 67        Linux/Unix konsulting
-----------------------------------------------------------------------




Reply to: