ii. Verktøykjedens tekniske merknader

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.

Om Krysskompilering

[Note]

Note

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.

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.

bygg

er maskinen der vi bygger programmer. Merk at denne maskinen er også referert til som verten.

vert

er maskinen/systemet der de bygde programmene skal kjøres. Merk at denne bruken av verten ikke er den samme som i andre seksjoner.

mål

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.

Implementering av Krysskompilering for LFS

[Note]

Note

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.

For å forfalske en krysskompilering i LFS, navnet på vertstripletten justeres litt ved å endre "vendor" feltet i LFS_TGT variabel så det står "lfs". Vi bruker også --with-sysroot alternativet når du bygger krysslenkeren og krysskompilatoren for å fortelle dem hvor de skal finne de nødvendige vertsfilene. Dette sikrer at ingen av de andre programmene bygget i Kapittel 6 kan lenke til biblioteker på byggemaskinen. Kun to trinn er obligatoriske, og ett til for tester:

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 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 fase 2. Det er to grunner til at vi ikke umiddelbart bruker kompilator bygget i trinn 2, cc-lfs, for å bygge disse bibliotekene.

  • Generelt sett kan ikke cc-lfs kjøre på pc (vertssystemet). Selv om triplettene for pc og lfs er kompatible med hverandre, en kjørbar fil for lfs må avhenge av glibc-2.40; vertsdistroen kan bruke en annen implementering av libc (for eksempel musl), eller en tidligere utgivelse av glibc (for eksempel, glibc-2.13).

  • Selv om cc-lfs kan kjøre på pc, vil det å bruke det på pc skape en risiko for å koble til pc-bibliotekene, siden cc-lfs er en lokal kompilator.

Så når vi bygger gcc trinn 2, instruerer vi byggesystemet til å gjenoppbygge libgcc og libstdc++ med cc1, men vi kobler libstdc++ til den nye 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 av dem er bygget uten valgfrie avhengigheter, og autoconf kan ikke utføre noen funksjonsinnsjekker Kapittel 6 på grunn av krysskompilering, forårsaker at de midlertidige pakkene mangler valgfrie funksjoner, eller bruker suboptimale koderutiner. I tillegg en mindre grunn til gjenoppbygging av pakkene er å kjøre testpakkene.

Andre prosedyredetaljer

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. Det viktigste hensynet for å bygge glibc er kompilatoren, binære verktøy og kjernedeklarasjoner. Kompilatoren og binære verktøy er generelt ikke et problem siden glibc vil alltid bruke kompilatoren som er relatert til --host parameteret sendt til konfigureringsskriptet; f.eks. i vårt tilfelle vil kompilatoren være $LFS_TGT-gcc og readelf verktøyet vil være $LFS_TGT-readelf. Kjerne deklarasjonene kan være litt mer komplisert. Derfor tar vi ingen risiko og bruker den tilgjengelige konfigurasjonsbryteren for å fremtvinge riktig valg. Etter kjøring av configure, sjekk innholdet i config.make filen i build mappen for alle viktige detaljer. Disse elementene fremhever et viktig aspekt ved glibc pakken—en er veldig selvforsynt med tanke på byggemaskineriet og er generelt ikke avhengig av standardinnstillinger for verktøykjeder.

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. Installasjonstrinnet for alle disse pakkene bruker DESTDIR variabel for å tvinge installasjonen inn i LFS filsystemet.

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. På grunn av en merkelig logikk i GCC konfigureringsskript, CC_FOR_TARGET ender opp som cc når verten er den samme som målet, men er forskjellig fra byggesystemet. Det er derfor CC_FOR_TARGET=$LFS_TGT-gcc er erklært eksplisitt som et av konfigurasjonsalternativene.

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.