Denne delen forklarer noen av begrunnelsen og de tekniske detaljene bak den overordnede byggemetoden. Det er ikke nødvendig å umiddelbart forstå alt i denne delen. Det meste av denne informasjonen vil være klarere etter å ha utført en faktisk konstruksjon. Denne delen kan refereres til når som helst under prosessen.
Det overordnede målet for Kapittel 5 og Kapittel 6 er å produsere et midlertidig område som inneholder et kjent sett med verktøy som kan isoleres fra vertssystemet. Ved bruk av chroot kommandoene, kompilasjonene i de resterende kapitlene vil være isolert inne i det miljøet, og sikre en ren, problemfri bygging av det nye LFS systemet. Byggeprosessen er designet for å minimere risikoen for nye lesere og gi den mest pedagogiske verdien samtidig.
Byggeprosessen baserer seg på krysskompilering. Krysskompilering brukes normalt for å bygge en kompilator og dens verktøykjede for en annen maskin enn den som brukes til byggingen. Dette er strengt tatt ikke nødvendig for LFS, siden maskinen der det nye systemet skal kjøre er den samme som den brukt til byggingen. Men krysskompilering har den store fordelen at alt som er krysskompilert ikke avhenger av vertsmiljøet.
LFS boken er ikke, og inneholder ikke en generell veiledning til å bygge en kryss (eller lokal) verktøykjede. Ikke bruk kommandoen i boken for en kryssverktøykjede som skal brukes til andre formål enn å bygge LFS, med mindre du virkelig forstår hva du gjør.
Det er kjent at installasjon av GCC pass 2 vil bryte verktøykjeden. Vi anser det ikke som en feil fordi GCC pass 2 er den siste pakken som skal krysskompileres i boken, og vi vil ikke «fikse» det til vi virkelig trenger å krysskompilere en pakke etter GCC pass 2 i fremtiden.
Krysskompilering involverer noen begreper som fortjener en seksjon for seg selv. Selv om denne delen kan utelates i en første lesning, å komme tilbake til det senere vil være gunstig for din fulle forståelse av prosessen.
La oss først definere noen begreper som brukes i denne sammenhengen.
er maskinen der vi bygger programmer. Merk at denne maskinen er også referert til som «verten».
er maskinen/systemet der de bygde programmene skal kjøres. Merk at denne bruken av «verten» ikke er den samme som i andre seksjoner.
brukes kun for kompilatorer. Det er maskinen kompilatoren produserer kode for. Det kan være forskjellig fra både bygg og vertent.
Som et eksempel, la oss forestille oss følgende scenario (noen ganger referert til som «Canadian Cross»). vi har en kompilator bare på en treg maskin, la oss kalle det maskin A, og kompilatoren ccA. Vi kan også ha en rask maskin (B), men uten kompilator, og vi ønsker å produsere kode for en annen treg maskin (C). Å bygge en kompilator for maskin C, ville vi ha tre trinn:
Stadie | Bygg | Vert | Mål | Handling |
---|---|---|---|---|
1 | A | A | B | bygg krysskompilator cc1 med ccA på maskin A |
2 | A | B | C | bygg krysskompilator cc2 med cc1 på maskin A |
3 | B | C | C | bygg kompilator ccC med cc2 på maskin B |
Deretter kan alle de andre programmene som trengs av maskin C kompileres ved å bruke cc2 på den raske maskinen B. Merk at med mindre B kan kjøre programmer produsert for C, er det ingen måte å teste de bygde programmene før maskinen C selv kjører. For eksempel, for å teste ccC, vil vi kanskje legge til en fjerde trinn:
Stadie | Bygg | Vert | Mål | Handling |
---|---|---|---|---|
4 | C | C | C | bygge om og teste ccC ved å bruke seg selv på maskin C |
I eksemplet ovenfor er bare cc1 og cc2 krysskompilatorer, det vil si de produserer kode for en annen maskin enn de de kjører på. De andre kompilatorene ccA og ccC produserer kode for maskinen de kjører på. Slike kompilatorer kalles lokale kompilatorer.
Alle de krysskompilerte pakkene i denne boken bruker en autoconf basert byggesystem. Det autoconf baserte byggesystemet godtar systemtyper i formen cpu-vendor-kernel-os, referert til som systemtripletten. Siden leverandørfeltet ofte er irrelevant, autoconf lar deg utelate det.
En klok leser kan lure på hvorfor en «triplett» refererer
til et firekomponents navn. Kjernefeltet og os-feltet begynte som
et enkelt «system» felt. Et slikt trefeltsskjema er
fortsatt gyldig i dag for noen systemer, for eksempel,
x86_64-unknown-freebsd
. Men to
systemer kan dele samme kjerne og fortsatt være for forskjellige
for å bruke den samme tripletten for å beskrive dem. For eksempel
Android kjørende på en mobiltelefon er helt forskjellig fra
Ubuntu som kjører på en ARM64 server, selv om de begge kjører på
samme type CPU (ARM64) og bruker samme kjerne (Linux).
Uten et emuleringslag kan du ikke kjøre en kjørbar fil for en
server på en mobiltelefon eller omvendt. Så «system» feltet har
blitt delt inn i kjerne- og os-felt, for å angi disse systemene
entydig. I vårt eksempel, Android systemet er angitt aarch64-unknown-linux-android
, og Ubuntu
systemet er angitt aarch64-unknown-linux-gnu
.
Ordet «triplett» forblir innebygd i leksikonet. En
enkel måte å bestemme din systemtriplett er å kjøre config.guess skript som følger
med kilden for mange pakker. Pakk ut binutils sine kilder, kjør
skriptet ./config.guess
, og merk
utdaten. For eksempel, for en 32-bits Intel-prosessor utdataen
vil være i686-pc-linux-gnu. På et 64-bit system
blir det x86_64-pc-linux-gnu. På de fleste
Linux systemer den enklere gcc
-dumpmachine kommando vil gi deg lignende
informasjon.
Vær også oppmerksom på navnet på plattformens dynamiske lenker,
ofte referert til som den dynamiske lasteren (ikke å forveksle
med standard lenker ld som er en del av binutils).
Den dynamiske lenkeren levert av Glibc finner og laster de delte
bibliotekene som trengs av et program, forbereder programmet for
kjøring, og deretter kjører det. Navnet på dynamisk lenker for en
32-bits Intel-maskin er ld-linux.so.2
; og er ld-linux-x86-64.so.2
for 64-bits systemer. En
sikker måte å bestemme navnet på den dynamiske lenkeren på er å
inspisere en tilfeldig binær fra vertssystemet ved å kjøre:
readelf -l <navn på binær>
| grep interpreter
og legg merke til utdataen.
Den autoritative referansen som dekker alle plattformer er i
a Glibc wiki
page.
Det er to hovedpunkter for en krysskompilering:
Ved produksjon og bearbeiding skal maskinkoden bli utført på «verten,» kryssverktøykjeden må bli brukt. Merk at den opprinnelige verktøykjeden fra «bygget» kan fortsatt påkalles for å generere maskinkode som skal bli brukt på «bygget.» For eksempel byggesystemet kan kompilere en generator med den opprinnelige verktøykjeden, og deretter generere en C kildefil med generatoren, og kompiler til slutt C kildefilen med kryssverktøykjeden slik at den genererte koden vil kunne kjøre på «verten.»
Med et autoconf basert byggesystem er dette kravet sikret ved
å bruke --host
parameteren for å spesifisere «vertens» triplett.
Med denne bryteren byggesystemet vil bruke
verktøykjedekomponentene med prefiks
for å generere og behandle maskinkoden for «verten»; f.eks.
kompilatoren vil være <vertstripletten>
<vertstripletten>
-gcc
og readelf
verktøyet vil være <vertstripletten>
-readelf.
Byggesystemet skal ikke prøve å kjøre en generert maskinkode
som skal kjøres på «verten.» For eksempel, når du bygger et
verktøy naturlig, kan dets manualside være generert ved å
kjøre verktøyet med --help
bryteren og behandle
utgangen, men generelt er det ikke mulig å gjøre det for en
krysskompilering da verktøyet kan feile å kjøre på
«bygget»: det er åpenbart umulig å kjøre
ARM64 maskinkode på en x86 CPU (uten emulator).
Med et autoconf basert byggesystem er dette kravet oppfylt i
«krysskompileringsmodusen» hvor de
valgfrie funksjonene som krever å kjøre maskinkode for
«verten» i løpet av byggetiden er
deaktivert. Når «vertens» triplett er eksplisitt
spesifisert, «krysskompileringsmodus» er aktivert
hvis og bare hvis enten configure skriptet klarer
ikke å kjøre et dummy program kompilert inn i «vertens»
maskinkode, eller «byggets» triplett er eksplisitt
spesifisert via --build
bryteren og den er
forskjellig fra «vertens» triplett.
For å krysskompilere en pakke for det midlertidige LFS systemet,
navnet på systemtripletten justeres litt ved å endre "vendor"
feltet i LFS_TGT
variabel så det sier
"lfs" og LFS_TGT
er da spesifisert som
«vertens»
triplett via --host
, så
kryssverktøykjeden vil bli brukt til å generere og behandle
maskinkode som kjører som en del av det midlertidige LFS systemet.
Og vi må også aktivere «krysskompileringsmodusen»: til tross for at
«vertens»
maskinkode, dvs. maskinkoden for LFS sitt midlertidig system, er i
stand til å kjøre på gjeldende CPU, kan det referere til et
bibliotek som ikke er tilgjengelig på «bygget» (vertens
distro), eller noen kode eller data eksisterer ikke eller definert
annerledes i biblioteket selv om det tilfeldigvis er tilgjengelig.
Når du krysskompilerer en pakke for det midlertidige LFS systemet,
kan vi ikke stole på configure skript for å oppdage
dette problemet med dummy program: dummyen bruker bare noen få
komponenter i libc
som vertsdistro
sin libc
sannsynligvis gir (med
mindre, vertsdistroen bruker en annen libc
implementering som Musl), så det vil ikke
mislykkes som de virkelig nyttige programmene trolig ville. Derfor
må vi spesifisere eksplisitt «byggets» triplett for å aktivere «krysskompileringsmodusen.» Verdien vi bruker
er bare standard, dvs. den originale systemtripletten fra
config.guess utdata,
men «krysskompileringsmodusen» avhenger av en
eksplisitt spesifikasjon som vi har diskutert.
Vi bruker --with-sysroot
alternativet når vi bygger krysslinkeren og krysskompilatoren for å
fortelle dem hvor de skal finne de nødvendige filene for
«verten.»
Dette sikrer at nesten ingen av de andre programmene bygget i
Kapittel 6
kan lenke til biblioteker på «bygget.» Ordet «nesten» brukes pga
libtool, en
«kompatibilitets» innpakning av kompilatoren
og linkeren for autoconf baserte byggesystemer, kan prøve å være
for smart og feilaktig sende alternativer som tillater linkeren å
finne biblioteker til «bygget.» For å forhindre denne feilen, må vi
slette libtool arkivet (.la
) filer og
fikse en utdatert libtool kopi sendt med Binutils koden.
Stadie | Bygg | Vert | Mål | Handling |
---|---|---|---|---|
1 | pc | pc | lfs | Bygg krysskompilator cc1 ved å bruke cc-pc på pc |
2 | pc | lfs | lfs | Bygg kompilator cc-lfs ved å bruke cc1 på pc |
3 | lfs | lfs | lfs | Bygge om (og kanskje teste) cc-lfs ved å bruke seg selv på lfs |
I tabellen ovenfor, «på pc» betyr at kommandoene kjøres på en maskin som bruker den allerede installerte distribusjonen. «på lfs» betyr at kommandoene kjøres i et chroot-miljø.
Dette er ennå ikke slutten på historien. C-språket er ikke bare en kompilator; den definerer også et standardbibliotek. I denne boken er det GNU C-biblioteket, kalt glibc, som brukes (det finnes et alternativ, "musl"). Dette biblioteket må kompileres for LFS-maskinen; det vil si å bruke krysskompilatoren cc1. Men kompilatoren selv bruker et internt bibliotek som gir komplekse subrutiner for funksjoner som ikke er tilgjengelige i assembler-instruksjonssettet. Dette interne biblioteket heter libgcc, og det må være koblet til glibc for at biblioteket skal være fullt funksjonelt. Videre standardbiblioteket for C++ (libstdc++) må også være koblet til glibc. Løsningen på dette kylling og egg problemet er først å bygge en nedgradert cc1-basert libgcc, mangler noen funksjoner som tråder og unntakshåndtering, og da å bygge glibc ved å bruke denne nedgraderte kompilatoren (glibc selv er ikke nedgradert), og også for å bygge libstdc++. Dette siste biblioteket vil mangle noe av funksjonaliteten til libgcc.
Resultatet av det foregående avsnittet er at cc1 ikke er i stand til å bygge et fullt funksjonell libstdc++ med den nedgraderte libgcc, men cc1 er den eneste kompilatoren som er tilgjengelig for å bygge C/C++-bibliotekene under trinn 2. Som vi har diskutert, kan vi ikke kjøre cc-lfs på pc (vertsdistroen) fordi det kan kreve noe bibliotek, kode eller data som ikke er tilgjengelig på «bygget» (vertsdistroen). Så når vi bygger gcc trinn 2, overstyrer vi bibliotekets søkebane for å koble libstdc++ mot den nylig gjenoppbygde libgcc i stedet for den gamle, nedgraderte konstruksjonen. Dette gjør den ombygde libstdc++ fullt funksjonell.
I Kapittel 8 (eller «stage 3»), alle pakkene som trengs for LFS systemet er bygget. Selv om en pakke allerede er installert i LFS systemet i et tidligere kapittel, bygger vi fortsatt pakken på nytt. Hovedårsaken til å gjenoppbygge disse pakkene er å gjøre dem stabile: hvis vi installerer en LFS pakke på nytt på et fullført LFS system, det reinstallerte innholdet i pakken skal være det samme som innholdet i den samme pakken når den først installeres i Kapittel 8. De midlertidige pakkene installert i Kapittel 6 eller Kapittel 7 kan ikke tilfredsstille dette kravet, fordi noen valgfrie funksjoner til dem er deaktivert på grunn av enten de manglende avhengigheter eller «krysskompileringsmodus.» I tillegg er en mindre grunn til å gjenoppbygge pakkene å kjøre testpakker.
Krysskompilatoren vil bli installert i en separat $LFS/tools
mappe, siden den ikke vil være en del
av det endelige systemet.
Binutils installeres først fordi configure kjøringer av både GCC og Glibc utfører forskjellige funksjonstester på assembleren og lenker for å bestemme hvilke programvarefunksjoner som skal aktiveres eller deaktiveres. Dette er viktigere enn man kanskje først er klar over. En feilkonfigurert GCC eller Glibc kan resultere i en subtilt ødelagt verktøykjede, hvor virkningen av et slikt brudd ikke vises før mot slutten av konstruksjonen av hele distribusjonen. En feil i testserien vil vanligvis fremheve denne feilen før det utføres for mye tilleggsarbeid.
Binutils installerer sin assembler og lenker på to steder,
$LFS/tools/bin
og $LFS/tools/$LFS_TGT/bin
. Verktøyene i en
plassering er hardlenket til den andre. En viktig fasett av
lenkeren er bibliotekets søkerekkefølge. Detaljert informasjon kan
fås fra ld ved å gi
den --verbose
flagget. For
eksempel, $LFS_TGT-ld --verbose |
grep SEARCH vil illustrere gjeldende søkestier og
rekkefølgen deres. (Merk at dette eksempelet kan kjøres som vist
kun mens du er logget på som bruker lfs
. Hvis du kommer tilbake til denne siden
senere, bytt ut $LFS_TGT-ld med ld).
Den neste pakken som er installert er GCC. Et eksempel på hva som kan bli sett under kjøringen av configure er:
checking what assembler to use... /mnt/lfs/tools/i686-lfs-linux-gnu/bin/as
checking what linker to use... /mnt/lfs/tools/i686-lfs-linux-gnu/bin/ld
Dette er viktig av grunnene nevnt ovenfor. Det viser også at GCCs konfigureringsskript ikke søker i STI (PATH) mapper for å finne hvilke verktøy det skal bruke. Imidlertid under selve kjøringen av gcc er ikke de samme søkestiene nødvendigvis brukt. For å finne ut hvilke standardlenker gcc vil bruke, kjør: $LFS_TGT-gcc -print-prog-name=ld. (En gang til, fjern $LFS_TGT- prefikset hvis du kommer tilbake til dette seinere.)
Detaljert informasjon kan fås fra gcc med å gi alternativet
-v
på kommandolinjen under
kompilering av et program. For eksempel, $LFS_TGT-gcc -v example.c
(eller
uten $LFS_TGT- hvis
du kommer tilbake senere) vises detaljert informasjon om
forprosessoren, kompileringen og sammenstillings stadier, inkludert
gcc sine søkestier
for inkluderte deklarasjoner og deres rekkefølge.
Neste, desinfiserte Linux API deklarasjoner (headers). Disse tillater standard C-bibliotek (Glibc) å bruke funksjoner som Linux kjernen vil gi.
Neste kommer glibc. Dette er den første pakken vi krysskompilerer.
Vi bruker --host=$LFS_TGT
alternativet for å få byggesystemet til å bruke verktøyene med
prefiks $LFS_TGT-
, og --build=$(../scripts/config.guess)
alternativet for å aktivere «krysskompileringsmodusen» som vi har
diskutert. DESTDIR
variabelen brukes til
å tvinge installasjonen inn i LFS filsystemet.
Som nevnt ovenfor, blir standard C++-biblioteket kompilert som neste, etterfulgt i Kapittel 6 av andre programmer som må krysskompileres for å bryte sirkulære avhengigheter på byggetidspunktet. Trinnene for disse pakkene ligner på trinnene for glibc.
Ved slutten av Kapittel 6
den lokale lfs kompilatoren er installert. Første binutils-pass2
blir bygget, med den samme DESTDIR
mappen som de andre programmene, deretter konstrueres den andre
passeringen av GCC, og utelater libstdc++ og andre ikke-viktige
biblioteker.
Når du kommer inn i chroot-miljøet i Kapittel 7, de midlertidige installasjonene av programmer som trengs for riktig betjening av verktøykjeden utføres. Fra dette tidspunktet og fremover er kjerneverktøykjeden selvstendig og selvhostet. I Kapittel 8, endelige versjoner av alle pakker som trengs for et fullt funksjonelt system bygges, testes og installert.