จากตอนที่แล้วเราได้ทำความรู้จักกับโครงสร้างไดเร็คทอรี่ และเข้าใจกับลำดับขั้นตอนการสร้างส่วนประกอบต่างๆของยูบูต ไปบางส่วนแล้วในตอนนี้เราจะมาทำความเข้าใจในรายละเอียดของการคอมไพล์แต่ละขั้นตอนลงไปอีก ซึ่งจะทำให้เราเข้าใจมากขึ้นไปอีก
การวิเคราะห์คอนโซลเอาท์พุตจากการรัน make <config>
ระหว่างการรันคำสั่ง make <config> จะปรากฏข้อความส่งออก จำนวนมาก จากคำสั่งต่างๆ ที่คำสั่ง make เรียกใช้ ข้อความที่ดูมากมายจนเกินกว่าจะทำความเข้าใจนั้น รวบรวมทุกขั้นตอนของการสร้างโปรแกรม ยูบูต อย่างละเอียด การทำความเข้าใจกับแต่ละคำสั่งสามารถทำให้เราเข้าใจขั้นตอนต่างๆได้มากขึ้นนอกเหนือจากลำดับขั้นตอนข้างต้น
เราสามารถเก็บข้อความส่งออกขณะทำการคอมไพล์ เป็นไฟล์เพื่อการวิเคราะห์ภายหลังด้วยคำสั่ง
$ make |
คำสั่งในการสร้างส่วนประกอบของยูบูตที่น่าสนใจ (คัดมาเฉพาะบางส่วน)
การสร้าง start.o
_start เป็นฟังก์ชั่นแรกที่ถูกเรียก (Reset entry point) เมื่อยูบูตเริ่มการทำงาน อยู่ในออปเจคไฟล์ start.o
สำหรับบอร์ด STM32F429I-DISCOVERY ซอสโค้ดจะอยู่ที่ cpu/arm_cortexm3/start.c ไฟล์ start.o จะถูกลิ้งค์เข้าไปเป็นส่วนแรก และมีความสำคัญมากเนื่องจากเป็นส่วนที่กำหนดค่าเริ่มต้นให้กับซีพียู
คำสั่งในการคอมไพล์ start.o
make -C cpu/arm_cortexm3 start.o make[2]: Entering directory `/home/embmaker/u-boot_stm32f429_discovery/cpu/arm_cortexm3' arm-none-eabi-gcc -Os -mthumb -mcpu=cortex-m3 -fsigned-char -fno-builtin-puts -fno-common -ffixed-r8 -D__KERNEL__ -I/home/embmaker/u-boot_stm32f429_discovery/include -fno-builtin -ffreestanding -isystem /usr/lib/gcc/arm-none-eabi/4.8.2/include -pipe -DCONFIG_ARM -D__ARM__ -DCONFIG_MEM_NVM_BASE="0x08000000" -DCONFIG_MEM_NVM_LEN="(1024 * 1024 * 2)" -DCONFIG_MEM_NVM_UBOOT_OFF="0x0" -DCONFIG_MEM_RAM_BASE="0x20000000" -DCONFIG_MEM_RAM_LEN="(20 * 1024)" -DCONFIG_MEM_RAM_BUF_LEN="(88 * 1024)" -DCONFIG_MEM_MALLOC_LEN="(16 * 1024)" -DCONFIG_MEM_STACK_LEN="(4 * 1024)" -I/home/embmaker/u-boot_stm32f429_discovery/cpu/arm_cortexm3 -Wall -Wstrict-prototypes -fno-stack-protector -o start.o start.c -c |
ความหมายของแต่ละออปชั่นของการคอมไพล์ start.o
-Os | Optimize space usage เป็นการบอกคอมไพล์เลอร์ในสร้างโค้ดโดยเน้นให้มีขนาดเล็กที่สุดเนื่องจากใน ระบบฝังตัวนั้น ทรัพยากรค่อนข้างมีจำกัด โดยเฉพาะ เฟลซเมมโมรี่ |
-mthumb | เป็นการบอกคอมไพล์เลอร์ในสร้างโค้ดรองรับ 16-bit Thumb instruction set |
-mcpu=cortex-m3 | ระบุรุ่นของซีพียูแบบเฉพาะเจาะจงเพื่อความเข้ากันได้ ในที่นี้คือ cortex-m3 |
-fsigned-char | กำหนดให้ใช้ตัวแปร char แบบคิดเครื่องหมาย |
-fno-builtin-puts | กำหนดให้ไม่ใช้ puts ฟังก์ชั่นภายในของคอมไพล์เลอร์ |
-fno-common | กำหนดให้คอมไพล์เลอร์แยกตัวแปรโกลบอลออกจาก common area |
-ffixed-r8 | กำหนดให้โค้ดที่สร้าง ไม่เรียกใช้งาน r8 register |
-fno-builtin | กำหนดให้ไม่ใช้ ฟังก์ชั่นภายในของคอมไพล์เลอร์ |
-ffreestanding | เป็นการบอกคอมไพล์เลอร์ว่าโค้ดที่คอมไพล์อยู่เป็น freestanding environment คือไม่ได้มีไลบารี่มาตรฐานรองรับและ ฟังก์ชั่นเริ่มต้นอาจจะไม่ใช่ main |
-isystem | ระบุให้ไดเรคทอรี่ที่ตามหลังออปชั่นนี้เป็นไลบารี่ระบบ เนื่องจากเราทำการคอมไพล์แบบข้ามแพลตฟอร์มจึงจำเป็นต้องใช้ไลบารี่ระบบจาก cross compiler โดยลำดับการสแกนจะเริ่มจาก ไดเร็คทอรี่ ที่กำหนดจากออปชั่น -I ทั้งหมดก่อนแล้วจึงตามด้วยไดเร็คทอรี่นี้ หากยังไม่เจอจึงไปค้นหาจากระบบเป็นอันสุดท้าย |
-pipe | บอกให้คอมไพล์เลอร์ใช้ไปป์แทนการใช้ไฟล์ ในการส่งค่าระหว่างกันขณะทำการคอมไพล์ ไม่มีผลกับไฟล์เอ้าพุท |
-DCONFIG_* | ประกาศค่าให้กับมาโคร สำหรับใช้ในขั้นตอนพรีโปรเซสเซอร์ |
-I/.. | ระบุให้ไดเรคทอรี่ที่ตามหลังออปชั่นนี้ใช้ในการหาสแกนหาเฮดเดอร์ไฟล์ |
-Wall | เปิดการเตือนทุกส่วนที่เกิดขึ้นจากการคอมไพล์ |
-Wstrict-prototypes | เตือนหากมีการประกาศฟังก์ชั่นโดยไม่ได้ระบุชนิดของอาร์กิวเม้นท์ |
-fno-stack-protector | ไม่ใช้โค้ดตรวจสอบป้องกัน สแต็ค เมมโมรี่เต็ม ช่วยให้ลดขนาดโค้ดลง |
การสร้างไลบรารี่ ต่างๆ
ทุกฟังก์ชั่นที่ถูกเรียกใช้ ในยูบูต ถูกสร้างและรวบรวมในรูปแบบของสเตติกไลบรารี่ โดยแบ่งออกเป็นหมวดหมู่ตามไดเร็คทอรี่ ที่กล่าวข้างต้น แล้วจึงนำมารวมกันภายหลังโดย ลิ้งค์เกอร์
ขั้นตอนในการสร้างสเตติกไลบรารี่ประกอบด้วยการคอมไพล์ออปเจคไฟล์ต่างๆ และการนำมารวมกันเป็นสแตติก ไลบรารี่
คำสั่งในการคอมไพล์ออปเจคไฟล์
arm-none-eabi-gcc -Os -mthumb -mcpu=cortex-m3 -fsigned-char -fno-builtin-puts -fno-common -ffixed-r8 -D__KERNEL__ -I/home/embmaker/u-boot_stm32f429_discovery/include -fno-builtin -ffreestanding -isystem /usr/lib/gcc/arm-none-eabi/4.8.2/include -pipe -DCONFIG_ARM -D__ARM__ -DCONFIG_MEM_NVM_BASE="0x08000000" -DCONFIG_MEM_NVM_LEN="(1024 * 1024 * 2)" -DCONFIG_MEM_NVM_UBOOT_OFF="0x0" -DCONFIG_MEM_RAM_BASE="0x20000000" -DCONFIG_MEM_RAM_LEN="(20 * 1024)" -DCONFIG_MEM_RAM_BUF_LEN="(88 * 1024)" -DCONFIG_MEM_MALLOC_LEN="(16 * 1024)" -DCONFIG_MEM_STACK_LEN="(4 * 1024)" -I/home/embmaker/u-boot_stm32f429_discovery/cpu/arm_cortexm3 -Wall -Wstrict-prototypes -fno-stack-protector -o objx.o objx.c -c |
จะเห็นว่าออปชั่นต่างๆ ที่ใช้ไม่แตกต่างจากการคอมไพล์ start.o แต่อาจมีแตกต่างกันบ้างในบางออปเจคไฟล์
คำสั่งในการรวมออปเจคไฟล์ออกมาเป็นไลบรารี่
ขั้นตอนนี้จะใช้ คำสั่ง ar ซึ่งเป็น เครื่องมือจัดการ ไบนารี่ไฟล์ (binary utility) ในการสร้างจนออกมาเป็นสเตติกไลบรารี่ รูปแบบคำสั่งเป็นดังนี้
$ arm-none-eabi-ar crv libxxx.a obj1.o obj2.o obj3.o obj4.o obj5.o … objN.o |
ความหมายของแต่ละออปชั่น ของคำสั่ง ar
-c | ระบุให้สร้างแฟ้มเก็บถาวร หมายถึง แฟ้มข้อมูลที่เก็บไว้อย่างเป็นระเบียบ (archive file) โดยปกติ ไฟล์ archive จะถูกสร้างทุกครั้งที่มีการรันคำสั่งนี้อยู่แล้ว แต่อาจจะมีข้อความเตือนทุกครั้งหากมีไฟล์นี้อยู่แล้ว การระบุออปชั่นนี้ จะทำให้ข้อความเตือนนี้ไม่แสดงออกมา |
-r | ระบุให้ใส่ไฟล์ obj1.o obj2.o obj3.o ... ลงในแฟ้มเก็บถาวร หากไฟล์ที่ระบุในออปชั่นนี้ ไม่มีอยู่จริง คำสั่ง ar จะคืนค่าผิดพลาดให้ระบบและยกเลิกการทำงาน |
-v | ระบุให้แสดงรายละเอียดการทำงานออกมาทางคอนโซล (verbose mode) |
จากนั้นเราก็จะได้สเตติกไลบรารี่ สำหรับขั้นตอนต่อไป
การสร้าง uboot
ขั้นตอนนี้เป็นขั้นตอนสุดท้ายในการ นำออปเจ็คไฟล์และสเตติกไลบรารี่ทั้งหมดมารวมกัน และสร้างดัชนีสำหรับเรียกใช้ฟังก์ชั่นต่างๆอย่างสมบูรณ์
ไฟล์เอ้าพุตที่ได้ออกมาจากขั้นตอนนี้จะอยู่ในรูปแบบ ELF ใช้สำหรับการดีบักโปรแกรมด้วย gdb
สำหรับไฟล์ที่จะนำไปใช้ในการโปรแกรมลงแฟลซเมมโมรี่ (uboot.bin , uboot.srec , uboot.hex) จะถูกสร้างขึ้นจากไฟล์ ELF ภายหลังโดยคำสั่ง obj-copy
คำสั่งในการลิ้งค์ และออปชั่นที่ใช้
$ arm-none-eabi-ld -Bstatic -T u-boot.lds $UNDEF_SYM cpu/arm_cortexm3/start.o --start-group lib_generic/libgeneric.a lib_generic/lzma/liblzma.a lib_generic/lzo/liblzo.a cpu/arm_cortexm3/libarm_cortexm3.a cpu/arm_cortexm3/stm32/libstm32.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a fs/yaffs2/libyaffs2.a fs/ubifs/libubifs.a net/libnet.a disk/libdisk.a drivers/bios_emulator/libatibiosemu.a drivers/block/libblock.a drivers/dma/libdma.a drivers/fpga/libfpga.a drivers/gpio/libgpio.a drivers/hwmon/libhwmon.a drivers/i2c/libi2c.a drivers/input/libinput.a drivers/misc/libmisc.a drivers/mmc/libmmc.a drivers/mtd/libmtd.a drivers/mtd/nand/libnand.a drivers/mtd/onenand/libonenand.a drivers/mtd/ubi/libubi.a drivers/mtd/spi/libspi_flash.a drivers/net/libnet.a drivers/net/phy/libphy.a drivers/pci/libpci.a drivers/pcmcia/libpcmcia.a drivers/power/libpower.a drivers/spi/libspi.a drivers/rtc/librtc.a drivers/serial/libserial.a drivers/twserial/libtws.a drivers/usb/gadget/libusb_gadget.a drivers/usb/host/libusb_host.a drivers/usb/musb/libusb_musb.a drivers/usb/phy/libusb_phy.a drivers/video/libvideo.a drivers/watchdog/libwatchdog.a common/libcommon.a libfdt/libfdt.a api/libapi.a post/libpost.a board/stm/stm32f429-discovery/libstm32f429-discovery.a --end-group -L /usr/lib/gcc/arm-none-eabi/4.8.2/armv7-m -lgcc -Map u-boot.map -o u-boot |
ความหมายของแต่ละออปชั่น ของคำสั่ง ld
-Bstatic | กำหนดไม่ให้ลิ้งค์กับแชร์ไลบรารี่ ให้ลิ้งค์แต่เฉพาะสแตติก ไลบรารี่เท่านั้น |
-T u-boot.lds | กำหนดให้ใช้ u-boot.lds เป็น ลิ้งค์เกอร์สคริปแทนดีฟอลท์ลิ้งค์เกอร์สคริป |
$UNDEF_SYM | กำหนดให้สัญลักษณ์ (symbol) ในตัวแปร $UNDEF_SYM เป็น undefined เพื่อที่จะบังคับให้การลิ้งค์ของไลบรารี่โมดูลเป็นตัวกำหนดค่าให้เองสำหรับโมดูลที่เพิ่มเข้าไป ** คำสั่งสำหรับการสร้างรายชื่อ undefined symbol UNDEF_SYM=`arm-none-eabi-objdump -x board/stm/stm32f429-discovery/libstm32f429-discovery.a lib_generic/libgeneric.a lib_generic/lzma/liblzma.a lib_generic/lzo/liblzo.a cpu/arm_cortexm3/libarm_cortexm3.a cpu/arm_cortexm3/stm32/libstm32.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a fs/yaffs2/libyaffs2.a fs/ubifs/libubifs.a net/libnet.a disk/libdisk.a drivers/bios_emulator/libatibiosemu.a drivers/block/libblock.a drivers/dma/libdma.a drivers/fpga/libfpga.a drivers/gpio/libgpio.a drivers/hwmon/libhwmon.a drivers/i2c/libi2c.a drivers/input/libinput.a drivers/misc/libmisc.a drivers/mmc/libmmc.a drivers/mtd/libmtd.a drivers/mtd/nand/libnand.a drivers/mtd/onenand/libonenand.a drivers/mtd/ubi/libubi.a drivers/mtd/spi/libspi_flash.a drivers/net/libnet.a drivers/net/phy/libphy.a drivers/pci/libpci.a drivers/pcmcia/libpcmcia.a drivers/power/libpower.a drivers/spi/libspi.a drivers/rtc/librtc.a drivers/serial/libserial.a drivers/twserial/libtws.a drivers/usb/gadget/libusb_gadget.a drivers/usb/host/libusb_host.a drivers/usb/musb/libusb_musb.a drivers/usb/phy/libusb_phy.a drivers/video/libvideo.a drivers/watchdog/libwatchdog.a common/libcommon.a libfdt/libfdt.a api/libapi.a post/libpost.a | sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq` *** ค่าของตัวแปร $UNDEF_SYM จากคำสั่งด้านบน -u__u_boot_cmd_base -u__u_boot_cmd_bdinfo -u__u_boot_cmd_bootm -u__u_boot_cmd_bootp -u__u_boot_cmd_cmp -u__u_boot_cmd_cp -u__u_boot_cmd_cptf -u__u_boot_cmd_crc32 -u__u_boot_cmd_end -u__u_boot_cmd_go -u__u_boot_cmd_help -u__u_boot_cmd_loadb -u__u_boot_cmd_loady -u__u_boot_cmd_loop -u__u_boot_cmd_md -u__u_boot_cmd_mm -u__u_boot_cmd_mtest -u__u_boot_cmd_mw -u__u_boot_cmd_nm -u__u_boot_cmd_printenv -u__u_boot_cmd_question_mark -u__u_boot_cmd_rarpboot -u__u_boot_cmd_reset -u__u_boot_cmd_run -u__u_boot_cmd_saveenv -u__u_boot_cmd_setenv -u__u_boot_cmd_start -u__u_boot_cmd_tftpboot -u__u_boot_cmd_version |
--start-group libxxA.a libxxB.a ... libxxN.a –end-group | สร้างกลุ่มของไลบรารี่เพื่อช่วยลิ้งค์เกอร์ในการค้นหาสัญลักษณ์วนซ้ำไปจนกว่าจะพบทั้งหมด วิธีนี้จะช่วยในกรณีที่มีการเรียกใช้สัญลักษณ์แบบเป็นวงกลม ( circular references issue ). |
-L /usr/lib/gcc/arm-none-eabi/4.8.2/armv7-m | กำหนดให้ใช้เฮดเดอร์ไฟล์จาก /usr/lib/gcc/arm-none-eabi/4.8.2/armv7-m ด้วย |
-Map u-boot.map | กำหนดให้สร้างรายละเอียดค่าตำแหน่งในเมมโมรี่ของแต่ละฟังก์ชั่นใน ไฟล์ u-boot.map |
ขั้นตอนการคอมไพล์ยูบูตทั้งหมดสามารถสรุปได้ดังรูปด้านล่าง อาจยาวไปบ้างแต่หวังว่าบทความนี้จะช่วยอธิบายให้เข้าใจขั้นตอนต่างๆได้ดีขึ้นนะครับ