ทำความเข้าใจ ขั้นตอนการคอมไพล์ u-boot ตอนที่ 2

By | November 15, 2016

จากตอนที่แล้วเราได้ทำความรู้จักกับโครงสร้างไดเร็คทอรี่ และเข้าใจกับลำดับขั้นตอนการสร้างส่วนประกอบต่างๆของยูบูต ไปบางส่วนแล้วในตอนนี้เราจะมาทำความเข้าใจในรายละเอียดของการคอมไพล์แต่ละขั้นตอนลงไปอีก ซึ่งจะทำให้เราเข้าใจมากขึ้นไปอีก

การวิเคราะห์คอนโซลเอาท์พุตจากการรัน make <config>

ระหว่างการรันคำสั่ง make <config> จะปรากฏข้อความส่งออก จำนวนมาก จากคำสั่งต่างๆ ที่คำสั่ง make เรียกใช้ ข้อความที่ดูมากมายจนเกินกว่าจะทำความเข้าใจนั้น รวบรวมทุกขั้นตอนของการสร้างโปรแกรม ยูบูต อย่างละเอียด การทำความเข้าใจกับแต่ละคำสั่งสามารถทำให้เราเข้าใจขั้นตอนต่างๆได้มากขึ้นนอกเหนือจากลำดับขั้นตอนข้างต้น

เราสามารถเก็บข้อความส่งออกขณะทำการคอมไพล์ เป็นไฟล์เพื่อการวิเคราะห์ภายหลังด้วยคำสั่ง

$ make |tee build.log

คำสั่งในการสร้างส่วนประกอบของยูบูตที่น่าสนใจ (คัดมาเฉพาะบางส่วน)

การสร้าง 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
-OsOptimize 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

ขั้นตอนการคอมไพล์ยูบูตทั้งหมดสามารถสรุปได้ดังรูปด้านล่าง อาจยาวไปบ้างแต่หวังว่าบทความนี้จะช่วยอธิบายให้เข้าใจขั้นตอนต่างๆได้ดีขึ้นนะครับ

uboot_build_diagram_bgw

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *