bzimage가 만들어지는 과정을 살펴 보고 이를 따라가면서 Makefile의 자세한 내용을 알아본다. 정확한 것은 2.4.2절을 참조하기 바란다.
시작은 물론 $(topDIR)/Makefile로 부터 시작한다.
커널 makefile은 몇 부분으로 나눌 수 있다.
기본 정보 정의
커널 설정
커널 소스 의존성 만들기
모듈 만들기
커널 실행 파일 만들기
모듈 설치하기
시작에 앞서 사용되는 Makefile들을 설명해 놓는다. 이것 들을 참조로 추적해나가므로 시작하기에 앞서 한번 쯤 훑어 보는 것도 좋을 것이다.
아래 makefile에 (1), (2)와 같이 표시된 것은 아래 줄에 대한 설명을 달아 놓은 것으로 Makefile의 끝 부분에 붙어 있는 설명을 참조해가면서 분석하면되겠다.
version = 2 patchlevel = 4 sublevel = 16 extraversion = kernelrelease=$(veRSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) (1) arch := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) (2) kernelpath=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//") (3) config-shell := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ else echo sh; fi ; fi) (4) topdir := $(shell /bin/pwd) hpath = $(topdIR)/include findhpath = $(hpatH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net (5) hostcc = gcc hostcflags = -wall -Wstrict-prototypes -O2 -fomit-frame-pointer (6) cross-compile = (7) # # include the make variables (CC, etc...) # as = $(cross-coMPILE)as ld = $(cross-coMPILE)ld cc = $(cross-coMPILE)gcc cpp = $(cc) -e ar = $(cross-coMPILE)ar nm = $(cross-coMPILE)nm strip = $(cross-COMPILE)strip objcopy = $(crosS-COMPILE)objcopy objdump = $(crosS-COMPILE)objdump makefiles = $(topdIR)/.config genksyms = /sbin/genksyms depmod = /sbin/depmod modflags = -dmoduLE cflags-kernel = perl = perl (8) export version paTCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \ config-shell topDIR HPATH HOSTCC HOSTCFLAGS CROSS-COMPILE AS LD CC \ cpp ar nm strip OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL all: do-it-all (9) # # make "config" the default target if there is no configuration file or # "depend" the target if there is no top-level dependency information. # ifeq (.config,$(wildcard .config)) include .config ifeq (.depend,$(wildcard .depend)) include .depend do-it-all: version vmlinux else (10) configuration = depend do-it-all: depend endif else (11) configuration = config do-it-all: config endif (12) # # install-path specifies where to place the updated kernel and system map # images. uncomment if you want to place them anywhere other than root. # #export install-paTH=/boot (13) # # install-mod-path specifies a prefix to MODLIB for module directory # relocations required by build roots. This is not defined in the # makefile but the arguement can be passed to make if needed. # modlib := $(instaLL-MOD-PATH)/lib/modules/$(KERNELRELEASE) export modlib # # standard cflags # cppflags := -d--keRNEL-- -I$(HPATH) cflags := $(cppflaGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ -fomit-frame-pointer -fno-strict-aliasing -fno-common aflags := -d--asseMBLY-- $(CPPFLAGS) (14) # # root-dev specifies the default root-device when making the image. # this can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case # the default of fLOPPY is used by 'build'. # this is i386 specific. # export root-dev = CURRENT (15) # # if you want to preset the SVGA mode, uncomment the next line and # set svga-mode to whatever number you want. # set it to -dsvga-MODE=NORMAL-VGA if you just want the EGA/VGA mode. # the number is the same as you would ordinarily press at bootup. # this is i386 specific. # export svga-mode = -DSVGA-MODE=NORMAL-VGA (16) # # if you want the RAM disk device, define this to be the size in blocks. # this is i386 specific. # #export ramdisk = -DRAMDISK=512 (17) core-files =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o networks =net/network.o libs =$(topdir)/lib/lib.a subdirs =kernel drivers mm fs net ipc lib (18) drivers-n := drivers-y := drivers-m := drivers- := drivers-$(config-aCPI) += drivers/acpi/acpi.o drivers-$(config-pARPORT) += drivers/parport/driver.o drivers-y += drivers/char/char.o \ drivers/block/block.o \ drivers/misc/misc.o \ drivers/net/net.o \ drivers/media/media.o drivers-$(config-aGP) += drivers/char/agp/agp.o drivers-$(config-dRM) += drivers/char/drm/drm.o drivers-$(config-nUBUS) += drivers/nubus/nubus.a drivers-$(config-iSDN) += drivers/isdn/isdn.a drivers-$(config-nET-FC) += drivers/net/fc/fc.o drivers-$(config-aPPLETALK) += drivers/net/appletalk/appletalk.o drivers-$(config-tR) += drivers/net/tokenring/tr.o drivers-$(config-wAN) += drivers/net/wan/wan.o drivers-$(config-aRCNET) += drivers/net/arcnet/arcnetdrv.o drivers-$(config-aTM) += drivers/atm/atm.o drivers-$(config-iDE) += drivers/ide/idedriver.o drivers-$(config-fC4) += drivers/fc4/fc4.a drivers-$(config-sCSI) += drivers/scsi/scsidrv.o drivers-$(config-fUSION-BOOT) += drivers/message/fusion/fusion.o drivers-$(config-iEEE1394) += drivers/ieee1394/ieee1394drv.o ifneq ($(config-cd-NO-IDESCSI)$(CONFIG-BLK-DEV-IDECD)$(CONFIG-BLK-DEV-SR)$(CONFIG-PARIDE-PCD),) drivers-y += drivers/cdrom/driver.o endif drivers-$(config-sOUND) += drivers/sound/sounddrivers.o drivers-$(config-pCI) += drivers/pci/driver.o drivers-$(config-mTD) += drivers/mtd/mtdlink.o drivers-$(config-pCMCIA) += drivers/pcmcia/pcmcia.o drivers-$(config-nET-PCMCIA) += drivers/net/pcmcia/pcmcia-net.o drivers-$(config-nET-WIRELESS) += drivers/net/wireless/wireless-net.o drivers-$(config-pCMCIA-CHRDEV) += drivers/char/pcmcia/pcmcia-char.o drivers-$(config-dIO) += drivers/dio/dio.a drivers-$(config-sBUS) += drivers/sbus/sbus-all.o drivers-$(config-zORRO) += drivers/zorro/driver.o drivers-$(config-fC4) += drivers/fc4/fc4.a drivers-$(config-aLL-PPC) += drivers/macintosh/macintosh.o drivers-$(config-mAC) += drivers/macintosh/macintosh.o drivers-$(config-iSAPNP) += drivers/pnp/pnp.o drivers-$(config-sGI-IP22) += drivers/sgi/sgi.a drivers-$(config-vT) += drivers/video/video.o drivers-$(config-pARIDE) += drivers/block/paride/paride.a drivers-$(config-hAMRADIO) += drivers/net/hamradio/hamradio.o drivers-$(config-tC) += drivers/tc/tc.a drivers-$(config-uSB) += drivers/usb/usbdrv.o drivers-$(config-iNPUT) += drivers/input/inputdrv.o drivers-$(config-i2O) += drivers/message/i2o/i2o.o drivers-$(config-iRDA) += drivers/net/irda/irda.o drivers-$(config-i2C) += drivers/i2c/i2c.o drivers-$(config-pHONE) += drivers/telephony/telephony.o drivers-$(config-mD) += drivers/md/mddev.o drivers-$(config-bLUEZ) += drivers/bluetooth/bluetooth.o drivers-$(config-hOTPLUG-PCI) += drivers/hotplug/vmlinux-obj.o (19) drivers := $(driveRS-y) (20) # files removed with 'make clean' clean-files = \ kernel/ksyms.lst include/linux/compile.h \ vmlinux system.map \ .tmp* \ drivers/char/consolemap-deftbl.c drivers/video/promcon-tbl.c \ drivers/char/conmakehash \ drivers/char/drm/*-mod.c \ drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist \ drivers/zorro/devlist.h drivers/zorro/gen-devlist \ drivers/sound/bin2hex drivers/sound/hex2hex \ drivers/atm/fore200e-mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \ drivers/scsi/aic7xxx/aicasm/aicasm-gram.c \ drivers/scsi/aic7xxx/aicasm/aicasm-scan.c \ drivers/scsi/aic7xxx/aicasm/y.tab.h \ drivers/scsi/aic7xxx/aicasm/aicasm \ drivers/scsi/53c700-mem.c \ net/khttpd/make-times-h \ net/khttpd/times.h \ submenu* # directories removed with 'make clean' clean-dirs = \ modules (21) # files removed with 'make mrproper' mrproper-files = \ include/linux/autoconf.h include/linux/version.h \ drivers/net/hamradio/soundmodem/sm-tbl-{afsk1200,afsk2666,fsk9600}.h \ drivers/net/hamradio/soundmodem/sm-tbl-{hapn4800,psk4800}.h \ drivers/net/hamradio/soundmodem/sm-tbl-{afsk2400-7,afsk2400-8}.h \ drivers/net/hamradio/soundmodem/gentbl \ drivers/sound/*-boot.h drivers/sound/.*.boot \ drivers/sound/msndinit.c \ drivers/sound/msndperm.c \ drivers/sound/pndsperm.c \ drivers/sound/pndspini.c \ drivers/atm/fore200e-*-fw.c drivers/atm/.fore200e-*.fw \ .version .config* config.in config.old \ scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \ scripts/lxdialog/*.o scripts/lxdialog/lxdialog \ .menuconfig.log \ include/asm \ .hdepend scripts/mkdep scripts/split-include scripts/docproc \ $(topdir)/include/linux/modversions.h \ kernel.spec # directories removed with 'make mrproper' mrproper-dirs = \ include/config \ $(topdir)/include/linux/modules (22) include arch/$(arcH)/Makefile (23) export cppflags cFLAGS AFLAGS export networks dRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS (24) .s.s: $(cpp) $(aflags) -traditional -o $*.s $<.s.o: $(cc) $(aflags) -traditional -c -o $*.o $< version: dummy @rm -f include/linux/compile.h boot: vmlinux @$(make) cflags="$(CFLAGS) $(CFLAGS-KERNEL)" -C arch/$(ARCH)/boot (25) vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(ld) $(linkflagS) $(HEAD) init/main.o init/version.o \ --start-group \ $(core-files) \ $(drivers) \ $(networks) \ $(libs) \ --end-group \ -o vmlinux $(nm) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map (26) symlinks: rm -f include/asm ( cd include ; ln -sf asm-$(ARCH) asm) @if [ ! -d include/linux/modules ]; then \ mkdir include/linux/modules; \ fi (27) oldconfig: symlinks $(config-shell) scripts/Configure -d arch/$(ARCH)/config.in xconfig: symlinks $(make) -c scripts kconfig.tk wish -f scripts/kconfig.tk menuconfig: include/linux/version.h symlinks $(make) -c scripts/lxdialog all $(config-shell) scripts/Menuconfig arch/$(ARCH)/config.in config: symlinks $(config-shell) scripts/Configure arch/$(ARCH)/config.in include/config/marKER: scripts/split-include include/linux/autoconf.h scripts/split-include include/linux/autoconf.h include/config @ touch include/config/MARKER (28) linuxsubdirs: $(patsubst %, -dir-%, $(SUBDIRS)) (29) $(patsubst %, -dir-%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER $(make) cflags="$(CFLAGS) $(CFLAGS-KERNEL)" -C $(patsubst -dir-%, %, $@) $(topdir)/include/linux/version.h: include/linux/version.h $(topdir)/include/linux/compile.h: include/linux/compile.h newversion: . scripts/mkversion > .tmpversion @mv -f .tmpversion .version (30) include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion @echo -n \#define UTS-VERSION \"\#`cat .version` > .ver @if [ -n "$(confIG-SMP)" ] ; then echo -n " SMP" >> .ver; fi @if [ -f .name ]; then echo -n \-`cat .name` >> .ver; fi @echo ' '`date`'"' >> .ver @echo \#define lINUX-COMPILE-TIME \"`date +%T`\" >> .ver @echo \#define lINUX-COMPILE-BY \"`whoami`\" >> .ver @echo \#define lINUX-COMPILE-HOST \"`hostname`\" >> .ver @if [ -x /bin/dnsdomainname ]; then \ echo \#define LINUX-COMPILE-DOMAIN \"`dnsdomainname`\"; \ elif [ -x /bin/domainname ]; then \ echo \#define LINUX-COMPILE-DOMAIN \"`domainname`\"; \ else \ echo \#define LINUX-COMPILE-DOMAIN ; \ fi >> .ver @echo \#define lINUX-COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver @mv -f .ver $@ (31) include/linux/version.h: ./Makefile @echo \#define uTS-RELEASE \"$(KERNELRELEASE)\" > .ver @echo \#define lINUX-VERSION-CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver @echo '#define kERNEL-VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >> .ver @mv -f .ver $@ init/version.o: init/version.c include/linux/compile.h include/config/MARKER $(cc) $(cflags) $(CFLAGS-KERNEL) -DUTS-MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c (32) init/main.o: init/main.c include/config/MARKER $(cc) $(cflags) $(CFLAGS-KERNEL) $(PROFILING) -c -o $*.o $< (33) fs lib mm ipc kernel drivers net: dummy $(make) cflags="$(CFLAGS) $(CFLAGS-KERNEL)" $(subst $@, -dir-$@, $@) (34) # emacs, vi용 tag를 만든다. tags: dummy etags `find include/asm-$(ARCH) -name '*.h'` find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs etags -a find $(subdirs) init -name '*.[ch]' | xargs etags -a # exuberant ctags works better with -I tags: dummy ctagsf=`ctags --version | grep -i exuberant >/dev/null && echo "-I --initdata,--exitdata,EXPORT-SYMBOL,EXPORT-SYMBOL-NOVERS"`; \ ctags $$ctagsf `find include/asm-$(ARCH) -name '*.h'` && \ find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs ctags $$CTAGSF -a && \ find $(subdirs) init -name '*.[ch]' | xargs ctags $$CTAGSF -a ifdef config-modulES ifdef config-modveRSIONS modflags += -dmodvERSIONS -include $(HPATH)/linux/modversions.h endif (35) .phony: modules modules: $(patsubst %, -mod-%, $(SUBDIRS)) .phony: $(patsubst %, -mod-%, $(SUBDIRS)) $(patsubst %, -mod-%, $(SUBDIRS)) : include/linux/version.h include/config/MARKER $(make) -c $(patsubst -mod-%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING-MODULES=1 modules .phony: modules-install modules-install: -modinst- $(patsubst %, -modinst-%, $(SUBDIRS)) -modinst-post .phony: -modinst- -modinst-: @rm -rf $(modlib)/kernel @rm -f $(modlib)/build @mkdir -p $(modlIB)/kernel @ln -s $(topdir) $(MODLIB)/build (36) # if system.map exists, run depmod. This deliberately does not have a # dependency on system.map since that would run the dependency tree on # vmlinux. this depmod is only for convenience to give the initial # boot a modules.dep even before / is mounted read-write. However the # boot script depmod is the master version. ifeq "$(strip $(inSTALL-MOD-PATH))" "" depmod-opts := else depmod-opts := -b $(INSTALL-MOD-PATH) -r endif .phony: -modinst-post -modinst-post: -modinst-post-pcmcia if [ -r system.map ]; then $(DEPMOD) -ae -F System.map $(depmod-opts) $(KERNELRELEASE); fi # backwards compatibilty symlinks for people still using old versions # of pcmcia-cs with hard coded pathnames on insmod. Remove # -modinst-post-pcmcia for kernel 2.4.1. .phony: -modinst-post-pcmcia -modinst-post-pcmcia: cd $(modlib); \ mkdir -p pcmcia; \ find kernel -path '*/pcmcia/*' -name '*.o' | xargs -i -r ln -sf ../{} pcmcia .phony: $(patsubst %, -modinst-%, $(SUBDIRS)) $(patsubst %, -modinst-%, $(SUBDIRS)) : $(make) -c $(patsubst -modinst-%, %, $@) modules-install # modules disabled.... else modules modules-install: dummy @echo @echo "the present kernel configuration has modules disabled." @echo "type 'make config' and enable loadable module support." @echo "then build a kernel with module support enabled." @echo @exit 1 endif clean: archclean find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' \) -type f -print \ | grep -v lxdialog/ | xargs rm -f rm -f $(clean-fiLES) rm -rf $(clean-dIRS) $(make) -c documentation/DocBook clean mrproper: clean archmrproper find . \( -size 0 -o -name .depend \) -type f -print | xargs rm -f rm -f $(mrproper-FILES) rm -rf $(mrpropeR-DIRS) $(make) -c documentation/DocBook mrproper (37) distclean: mrproper rm -f core `find . \( -not -type d \) -and \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -type f -print` TAGS tags backup: mrproper cd .. && tar cf - linux/ | gzip -9 > backup.gz sync sgmldocs: chmod 755 $(topdIR)/scripts/docgen chmod 755 $(topdIR)/scripts/gen-all-syms chmod 755 $(topdIR)/scripts/kernel-doc $(make) -c $(topDIR)/Documentation/DocBook books psdocs: sgmldocs $(make) -c documentation/DocBook ps pdfdocs: sgmldocs $(make) -c documentation/DocBook pdf htmldocs: sgmldocs $(make) -c documentation/DocBook html sums: find . -type f -print | sort | xargs sum > .SUMS dep-files: scripts/mkdep archdep include/linux/version.h scripts/mkdep -- init/*.c > .depend scripts/mkdep -- `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend $(make) $(patsubst %,-sfdep-%,$(SUBDIRS)) -FASTDEP-ALL-SUB-DIRS="$(SUBDIRS)" ifdef config-modveRSIONS $(make) update-modverfile endif ifdef config-modveRSIONS modverfile := $(toPDIR)/include/linux/modversions.h else modverfile := endif export modverfile depend dep: dep-files checkconfig: find * -name '*.[hcS]' -type f -print | sort | xargs $(PERL) -w scripts/checkconfig.pl checkhelp: find * -name [cc]onfig.in -print | sort | xargs $(PERL) -w scripts/checkhelp.pl checkincludes: find * -name '*.[hcS]' -type f -print | sort | xargs $(PERL) -w scripts/checkincludes.pl ifdef configuratioN ..$(configuration): @echo @echo "you have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'" @echo $(make) $(configURATION) @echo @echo "successful. Try re-making (ignore the error that follows)" @echo exit 1 #dummy: ..$(configURATION) dummy: else dummy: endif (38) include rules.make (39) # # this generates dependencies for the .h files. # scripts/mkdep: scripts/mkdep.c $(hostcc) $(hostCFLAGS) -o scripts/mkdep scripts/mkdep.c scripts/split-include: scripts/split-include.c $(hostcc) $(hostCFLAGS) -o scripts/split-include scripts/split-include.c (40) # # rpm target # # if you do a make spec before packing the tarball you can rpm -ta it # spec: . scripts/mkspec > kernel.spec # # build a tar ball, generate an rpm from it and pack the result # there arw two bits of magic here # 1) the use of /. to avoid tar packing just the symlink # 2) removing the .dep files as they have source paths in them that # will become invalid # rpm: clean spec find . \( -size 0 -o -name .depend -o -name .hdepend \) -type f -print | xargs rm -f set -e; \ cd $(topdir)/.. ; \ ln -sf $(topdir) $(KERNELPATH) ; \ tar -cvz --exclude CVS -f $(KERNELPATH).tar.gz $(KERNELPATH)/. ; \ rm $(kernelpath) ; \ cd $(topdir) ; \ . scripts/mkversion > .version ; \ rpm -ta $(topdir)/../$(KERNELPATH).tar.gz ; \ rm $(topdir)/../$(KERNELPATH).tar.gz |
예를 들어 ACPI 기능을 사용하지 않는다고 했을 경우엔 아래 줄이 driverS- += drivers/acpi/acpi.o가 된다. .config의 내용을 한번 읽어보면 금방 이해될 것이다.
patsubst는 $(patsubst PATTERN, REPLACEMENT, TEXT)의 형식으로 TEXT에서 patterN과 일치하는 부분을 REPLACEMENT로 교체한다.
#define uts-versioN "#11 2002. 01. 25. (금) 16:35:16 KST" #define linux-compILE-TIME "16:35:16" #define linux-compILE-BY "root" #define linux-compILE-HOST "halite" #define linux-compILE-DOMAIN "" #define linux-compILER "gcc version 2.95.3 20010315 (release)" |
# #define uts-releASE "2.4.16" # #define linux-veRSION-CODE 132112 # #define kernel-vERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) |
.phony를 사용하면 정의된 이름이 file이 아님을 알려주고 퍼포먼스를 올려준다. 자세한 것은 'info make'를 해서 참조 바란다.
(1) # # i386/makefile # # this file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. Remember to do have actions # for "archclean" and "archdep" for cleaning up and making dependencies for # this architecture # # this file is subject to the terms and conditions of the GNU General Public # license. see the file "COPYING" in the main directory of this archive # for more details. # # copyright (c) 1994 by Linus Torvalds # # 19990713 artur Skawina <skawina@geocities.com> # added '-march' and '-mpreferred-stack-boundary' support # (2) ld=$(cross-compile)ld -m elf-i386 objcopy=$(cross-coMPILE)objcopy -O binary -R .note -R .comment -S ldflags=-e stext linkflags =-t $(toPDIR)/arch/i386/vmlinux.lds $(LDFLAGS) cflags += -pipe (3) # prevent gcc from keeping the stack 16 byte aligned cflags += $(shell if $(CC) -mpreferred-stack-boundary=2 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mpreferred-stack-boundary=2"; fi) ifdef config-m386 cflags += -march=i386 endif ifdef config-m486 cflags += -march=i486 endif ifdef config-m586 cflags += -march=i586 endif ifdef config-m586tSC cflags += -march=i586 endif ifdef config-m586mMX cflags += -march=i586 endif ifdef config-m686 cflags += -march=i686 endif ifdef config-mpentIUMIII cflags += -march=i686 endif ifdef config-mpentIUM4 cflags += -march=i686 endif (4) ifdef config-mk6 cflags += $(shell if $(CC) -march=k6 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=k6"; else echo "-march=i586"; fi) endif (5) ifdef config-mk7 cflags += $(shell if $(CC) -march=athlon -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=athlon"; else echo "-march=i686 -malign-functions=4"; fi) endif ifdef config-mcrusOE cflags += -march=i686 -malign-functions=0 -malign-jumps=0 -malign-loops=0 endif ifdef config-mwincHIPC6 cflags += -march=i586 endif ifdef config-mwincHIP2 cflags += -march=i586 endif ifdef config-mwincHIP3D cflags += -march=i586 endif ifdef config-mcyriXIII cflags += -march=i586 endif head := arch/i386/kernel/head.o arch/i386/kernel/init-task.o subdirs += arch/i386/kernel arch/i386/mm arch/i386/lib core-files := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE-FILES) libs := $(topdir)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a ifdef config-math-EMULATION subdirs += arch/i386/math-emu drivers += arch/i386/math-emu/math.o endif arch/i386/kernel: dummy $(make) linuxsubdirs SUBDIRS=arch/i386/kernel arch/i386/mm: dummy $(make) linuxsubdirs SUBDIRS=arch/i386/mm makeboot = $(make) -C arch/$(ARCH)/boot vmlinux: arch/i386/vmlinux.lds force: ; .phony: zimage bzimage compressed zlilo bzlilo zdisk bzdisk install \ clean archclean archmrproper archdep zimage: vmlinux @$(makeboot) zimage (6) bzimage: vmlinux @$(makeboot) bzimage compressed: zimage zlilo: vmlinux @$(makeboot) booTIMAGE=zImage zlilo tmp: @$(makeboot) booTIMAGE=bzImage zlilo bzlilo: vmlinux @$(makeboot) booTIMAGE=bzImage zlilo zdisk: vmlinux @$(makeboot) booTIMAGE=zImage zdisk bzdisk: vmlinux @$(makeboot) booTIMAGE=bzImage zdisk install: vmlinux @$(makeboot) booTIMAGE=bzImage install archclean: @$(makeboot) clean archmrproper: archdep: @$(makeboot) dep |
objcopy는 입력에서 .note, .comment 섹션을 삭제하고 리로케이션 정보와 심볼 정보를 삭제한다. 출력 포맷은 binary. 링크할 땐 $(TOPDIR)/arch/i386/vmlinux.lds란 파일에 기록되어 있는 방법을 따라 링크한다.
사용되는 옵션은 다음과 같은 의미를 갖는다.
-mpreferred-stack-boundary=2 : 스택을 22 byte로 정렬하도록 한다. (=4면 24)
-S : 컴파일 스테이지까지만 하고 어셈블은 하지 않는다.
-xc : c 언어로 컴파일 한다.
/dev/null이 $(CC)의 입력으로 지정됐으므로 /dev/null을 읽어 컴파일한다. /dev/null을 읽으면 EOF를 돌려주므로 컴파일된 출력은 다음과 같을 것이지만 바로 /dev/null로 출력되어 화면에는 나타나지 않는다.
컴파일되면 다음과 같은 결과가 나온다.
.file "null" .version "01.01" gcc2-compiled.: .ident "gcc: (gNU) 2.95.3 20010315 (release)" |
# # arch/i386/boot/makefile # # this file is subject to the terms and conditions of the GNU General Public # license. see the file "COPYING" in the main directory of this archive # for more details. # # copyright (c) 1994 by Linus Torvalds # boot-incl = $(topdIR)/include/linux/config.h \ $(topdir)/include/linux/autoconf.h \ $(topdir)/include/asm/boot.h (1) zimage: $(configurE) bootsect setup compressed/vmlinux tools/build $(objcopy) compressed/vmlinux compressed/vmlinux.out tools/build bootsect setup compressed/vmlinux.out $(ROOT-DEV) > zImage (2) bzimage: $(configuRE) bbootsect bsetup compressed/bvmlinux tools/build $(objcopy) compressed/bvmlinux compressed/bvmlinux.out tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT-DEV) > bzImage compressed/vmlinux: $(TOPDIR)/vmlinux @$(make) -c compressed vmlinux compressed/bvmlinux: $(TOPDIR)/vmlinux @$(make) -c compressed bvmlinux zdisk: $(bootimage) dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0 zlilo: $(configure) $(BOOTIMAGE) if [ -f $(instalL-PATH)/vmlinuz ]; then mv $(INSTALL-PATH)/vmlinuz $(INSTALL-PATH)/vmlinuz.old; fi if [ -f $(instalL-PATH)/System.map ]; then mv $(INSTALL-PATH)/System.map $(INSTALL-PATH)/System.old; fi cat $(bootimage) > $(INSTALL-PATH)/vmlinuz cp $(topdir)/system.map $(INSTALL-PATH)/ if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi install: $(configuRE) $(BOOTIMAGE) sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL-PATH)" (3) tools/build: tools/build.c $(hostcc) $(hostCFLAGS) -o $@ $< -I$(TOPDIR)/include bootsect: bootsect.o $(ld) -ttext 0x0 -s --oformat binary -o $@ $< bootsect.o: bootsect.s $(as) -o $@ $< bootsect.s: bootsect.S Makefile $(BOOT-INCL) $(cpp) $(cppflagS) -traditional $(SVGA-MODE) $(RAMDISK) $< -o $@ (4) bbootsect: bbootsect.o $(ld) -ttext 0x0 -s --oformat binary $< -o $@ bbootsect.o: bbootsect.s $(as) -o $@ $< (5) bbootsect.s: bootsect.S Makefile $(BOOT-INCL) $(cpp) $(cppflagS) -D--BIG-KERNEL-- -traditional $(SVGA-MODE) $(RAMDISK) $< -o $@ setup: setup.o $(ld) -ttext 0x0 -s --oformat binary -e begtext -o $@ $< setup.o: setup.s $(as) -o $@ $< setup.s: setup.s video.S Makefile $(BOOT-INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h $(cpp) $(cppflagS) -D--ASSEMBLY-- -traditional $(SVGA-MODE) $(RAMDISK) $< -o $@ (6) bsetup: bsetup.o $(ld) -ttext 0x0 -s --oformat binary -e begtext -o $@ $< bsetup.o: bsetup.s $(as) -o $@ $< bsetup.s: setup.s video.S Makefile $(BOOT-INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h $(cpp) $(cppflagS) -D--BIG-KERNEL-- -D--ASSEMBLY-- -traditional $(SVGA-MODE) $(RAMDISK) $< -o $@ dep: clean: rm -f tools/build rm -f setup bootsect zImage compressed/vmlinux.out rm -f bsetup bbootsect bzImage compressed/bvmlinux.out @$(make) -c compressed clean |
zimage는 gzip으로 압축되고 하위 1M 메모리 내에 적재될 수 있는 크기의 커널 [1]
bzimage는 gzip으로 압축되고 하위 1M 메모리 내에 적재될 수 없는 크기의 커널
$(objcOPY)는 $(TOPDIR)/arch/i386/Makefile에서 정의된 것을 따른다. 즉 'objcoPY=$(CROSS-COMPILE)objcopy -O binary -R .note -R .comment -S'가 된다.
-ttext 0x0 : 코드의 시작을 0번지 부터 시작한다고 하고 링크한다. 이렇게 하면 링크된 최종 출력물은 특별한 위치를 가리지 않고 메모리의 아무 위치에나 적재가 가능하고 실행 가능해진다.
-s : 출력물에서 심볼 정보를 모두 없앤다.
--oformat binary : 출력물의 포맷을 바이너리로 한다.
# # linux/arch/i386/boot/compressed/Makefile # # create a compressed vmlinux image from the original vmlinux # head = head.o system = $(topdir)/vmlinux objects = $(head) misc.o zldflags = -e startup-32 (1) # zimage-offset is the load offset of the compression loader # bzimage-offset is the load offset of the high loaded compression loader # zimage-offset = 0x1000 bzimage-offset = 0x100000 zlinkflags = -ttext $(ZIMAGE-OFFSET) $(ZLDFLAGS) bzlinkflags = -ttext $(BZIMAGE-OFFSET) $(ZLDFLAGS) all: vmlinux vmlinux: piggy.o $(OBJECTS) $(ld) $(zlinkflaGS) -o vmlinux $(OBJECTS) piggy.o (2) bvmlinux: piggy.o $(OBJECTS) $(ld) $(bzlinkflAGS) -o bvmlinux $(OBJECTS) piggy.o (3) head.o: head.s $(cc) $(aflags) -traditional -c head.S misc.o: misc.c $(cc) $(cflags) -c misc.c (4) piggy.o: $(system) tmppiggy=-tmp-$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ $(objcopy) $(sysTEM) $$tmppiggy; \ gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "sections { .data : { input-len = .; LONG(input-data-end - input-data) input-data = .; *(.data) input-data-end = .; }}" >gt; $$tmppiggy.lnk; \ $(ld) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk clean: rm -f vmlinux bvmlinux -tmp-* |
$$$$piggy의 4개의 $는 4자리의 임의의 숫자로 채워진다. 즉 tmppiggy=-tmp-1234piggy와 같이된다. 더불어 $$tmppiggy.gz 은 -tmp-1234piggy.gz, tmppiggy.lnk는 -tmp-1234piggy.lnk와 같이 된다.
piggy.o는 head.o, misc.o와 합쳐져 하나의 다른 file로 만들어져야하므로 다시 링커를 통해 elf-i386 포맷으로 만들어진다.
$(objcOPY)에 사용된 옵션은 다음과 같다.
-O : output format. 여기선 binary
-R : 지정된 section 이름을 지운다. .note, .comment는 없앤다.
-S : input file을 지정한다.
-m elf-i386 : ld가 elf-i386을 emulation 하도록 지정한다.
-r : relocatable, 메모리에 적재될 때 재배치 가능하도록 한다.
-b binary : input file의 format을 말한다. 여기선 $$tmppiggy.gz은 binary
-T : linker script file을 지정한다.
-b elf32-i386 : output을 elf-i386 format으로 지정한다.
$(topdir)/arch/i386/boot/tools/build는 커널 이미지 만드는 과정의 최종 단계에서 몇 개의 파일을 합쳐 하나의 커널 이미지를 만들어낸다. 이런 일을 담당하는 프로그램을 분석해야 이미 나온 부팅 과정에서의 동작을 이해할 수 있을 것이다.
최종 만들어지는 이미지는 그림 2-2이 된다. 부팅할 때 bootsect는 build에 의해 기록된 루트 디바이스, setup의 크기, 압축 커널의 크기를 바탕을 부팅 절차를 계속 진행한다.
/* * $id: chap2.sgml,v 1.8 2002/02/15 15:59:43 halite Exp $ * * copyright (c) 1991, 1992 Linus Torvalds * copyright (c) 1997 Martin Mares */ /* * this file builds a disk-image from three different files: * * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest * - setup: 8086 machine code, sets up system parm * - system: 80386 code for actual system * * it does some checking that all files are of the correct type, and * just writes the result to stdout, removing headers and padding to * the right amount. It also writes some system data to stderr. */ /* * changes by tytso to allow root device specification * high loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 * cross compiling fixes by Gertjan van Wingerde, July 1996 * rewritten by martin Mares, April 1997 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/sysmacros.h> #include <unistd.h> #include <fcntl.h> #include <asm/boot.h> typedef unsigned char byte; typedef unsigned short word; typedef unsigned long u32; #define default-maJOR-ROOT 0 #define default-miNOR-ROOT 0 /* minimal number of setup sectors (see also bootsect.S) */ #define setup-sectS 4 byte buf[1024]; int fd; int is-big-kernel; void die(const char * str, ...) { va-list args; va-start(args, str); vfprintf(stderr, str, args); fputc('\n', stderr); exit(1); } void file-open(const char *name) { if ((fd = open(name, O-RDONLY, 0)) < 0) die("unable to open `%s': %m", name); } void usage(void) { die("usage: build [-b] bootsect setup system [rootdev] [> image]"); } int main(int argc, char ** argv) { unsigned int i, c, sz, setup-sectors; u32 sys-size; byte major-root, minor-root; struct stat sb; (1) if (argc > 2 && !strcmp(argv[1], "-b")) { is-big-kernel = 1; argc--, argv++; } if ((argc < 4) || (argc > 5)) usage(); if (argc > 4) { (2) if (!strcmp(argv[4], "CURRENT")) { if (stat("/", &sb)) { perror("/"); die("couldn't stat /"); } major-root = major(sb.st-dev); minor-root = minor(sb.st-dev); } else if (strcmp(argv[4], "FLOPPY")) { if (stat(argv[4], &sb)) { perror(argv[4]); die("couldn't stat root device."); } major-root = major(sb.st-rdev); minor-root = minor(sb.st-rdev); } else { major-root = 0; minor-root = 0; } } else { major-root = dEFAULT-MAJOR-ROOT; minor-root = dEFAULT-MINOR-ROOT; } fprintf(stderr, "Root device is (%d, %d)\n", major-root, minor-root); (3) file-open(argv[1]); i = read(fd, buf, sizeof(buf)); fprintf(stderr,"Boot sector %d bytes.\n",i); if (i != 512) die("boot block must be exactly 512 bytes"); if (buf[510] != 0x55 || buf[511] != 0xaa) die("boot block hasn't got boot flag (0xAA55)"); buf[508] = minor-root; buf[509] = major-root; if (write(1, buf, 512) != 512) die("write call failed"); close (fd); (4) file-open(argv[2]); /* Copy the setup code */ for (i=0 ; (c=read(fd, buf, sizeof(buf)))>0 ; i+=c ) if (write(1, buf, c) != c) die("write call failed"); if (c != 0) die("read-error on `setup'"); close (fd); (5) setup-sectors = (i + 511) / 512; /* Pad unused space with zeros */ /* for compatibility with ancient versions of LILO. */ if (setup-sectors < SETUP-SECTS) setup-sectors = SETUP-SECTS; fprintf(stderr, "Setup is %d bytes.\n", i); memset(buf, 0, sizeof(buf)); while (i < setup-sectors * 512) { c = setup-sectors * 512 - i; if (c > sizeof(buf)) c = sizeof(buf); if (write(1, buf, c) != c) die("write call failed"); i += c; } (6) file-open(argv[3]); if (fstat (fd, &sb)) die("unable to stat `%s': %m", argv[3]); sz = sb.st-size; fprintf (stderr, "System is %d kB\n", sz/1024); sys-size = (sz + 15) / 16; /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */ if (sys-size > (is-big-kernel ? 0x28000 : DEF-SYSSIZE)) die("system is too big. Try using %smodules.", is-big-kernel ? "" : "bzImage or "); if (sys-size > 0xefff) fprintf(stderr,"warning: kernel is too big for standalone boot " "from floppy\n"); while (sz > 0) { int l, n; l = (sz > sizeof(buf)) ? sizeof(buf) : sz; if ((n=read(fd, buf, l)) != l) { if (n < 0) die("error reading %s: %m", argv[3]); else die("%s: unexpected EOF", argv[3]); } if (write(1, buf, l) != l) die("write failed"); sz -= l; } close(fd); if (lseek(1, 497, SEEK-SET) != 497) /* Write sizes to the bootsector */ die("output: seek failed"); buf[0] = setup-sectors; if (write(1, buf, 1) != 1) die("write of setup sector count failed"); if (lseek(1, 500, SEEK-SET) != 500) die("output: seek failed"); buf[0] = (sys-size & 0xff); buf[1] = ((sys-size >> 8) & 0xff); if (write(1, buf, 2) != 2) die("write of image length failed"); return 0; /* Everything is OK */ } |
currenT는 /의 major, minor number를 사용한다. 필자의 리눅스 박스는 hda1이 /이므로 major=0x03, minor=0x01이 될것이다.
플로피가 루트 디바이스로 지정됐으면 major=minor=0이 된다.
command line에 아무 것도 지정되지 않으면 기본 값이 사용된다(기본 값은 사실 플로피와 같은 값을 갖는다).
또 508(0x1FC), 509(0x1FD) 번째 바이트에 루트 디바이스의 minor, major 번호를 써 넣는다.
수정 후 표준 출력으로 bootsect의 512 byte를 출력한다(원래 512 byte 였으므로 수정 내용을 포함해 그대로 출력될 것이다).
예를 들어 setup의 크기가 4768 byte라면 4768/512=9.3125 이므로 9 섹터를 차지하고 10번째 섹터는 다 사용하지 않고 조금만 사용하게 된다. 10번째 섹터의 경우 160 byte를 제외한 352 byte 만큼을 0으로 채워 넣는다.
1024 byte 단위로 읽어 표준 출력에 출력하고 bootsect의 497(0x1F1)에 setup이 몇 섹터를 차지하는지 기록하고 500(0x1F4)에 압축 커널의 크기를 16 byte 단위로 기록해준다.
[1] | 하위 1M에 대한 것은 2.2.2절을 참조한다. |