🌱 Linker Script File cho Vi Điều Khiển - Hướng Dẫn Chi Tiết
Như đã đề cập trong bài Build Process, quá trình Linking và Locating là những bước thiết yếu. Linker script file là công cụ thể hiện quá trình này - nó cho Linker biết:
- Bộ nhớ khả dụng của vi điều khiển (FLASH, SRAM, thanh ghi đặc biệt)
- Cách sắp xếp từng section (text, data, bss, ...) trong bộ nhớ
- Mapping LMA → VMA (Load Memory Address → Virtual Memory Address)
- Các ký hiệu đặc biệt như __stack_end, __heap_start
💡 LMA vs VMA: LMA (Load Memory Address) là vị trí khi chương trình được nạp vào bộ nhớ (thường là FLASH), còn VMA (Virtual Memory Address) là vị trí mà chương trình sử dụng khi chạy (thường là SRAM hoặc FLASH).
Linker Script File là gì?
Linker Script File là một tệp cấu hình có vai trò cực kỳ quan trọng trong quá trình biên dịch phần mềm nhúng. Nó định nghĩa cách mà các phần khác nhau của chương trình (code, dữ liệu khởi tạo, dữ liệu chưa khởi tạo, stack, heap) được sắp xếp trong bộ nhớ vật lý của vi điều khiển.
![]() |
| Embedded Build Process - Linking and Locator |
Cấu Trúc Bộ Nhớ Trong Vi Điều Khiển
Trước khi viết Linker Script, ta cần hiểu rõ cấu trúc bộ nhớ của vi điều khiển. Ví dụ với STM32F103 (ARM Cortex-M3):
| Vùng Nhớ | Địa Chỉ | Kích Thước | Đặc Điểm |
|---|---|---|---|
| FLASH | 0x08000000 | 256 KB | Bộ nhớ chương trình, đọc được |
| SRAM | 0x20000000 | 64 KB | Bộ nhớ dữ liệu, đọc/ghi được |
| Thanh ghi | 0x40000000+ | Khác nhau | Memory-mapped I/O |
Các Thành Phần Của Linker Script File (GCC)
1. MEMORY Block
Định nghĩa các vùng nhớ khả dụng trên vi điều khiển:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
Trong đó:
rx= read, execute (FLASH: chỉ đọc và thực thi)rwx= read, write, execute (SRAM: đọc, ghi, thực thi)ORIGIN= địa chỉ bắt đầuLENGTH= kích thước vùng nhớ
2. SECTIONS Block
Định nghĩa cách các phần của chương trình được sắp xếp:
SECTIONS
{
.text :
{
KEEP(*(.isr_vector)) /* Vector bảng ngắt */
*(.text) /* Code */
*(.rodata) /* Read-only data */
} > FLASH
.data :
{
*(.data) /* Dữ liệu khởi tạo */
} > SRAM AT > FLASH
.bss :
{
*(.bss) /* Dữ liệu chưa khởi tạo */
} > SRAM
}
3. AT> Command (LMA Mapping)
Lệnh AT> được sử dụng để định nghĩa nơi section được lưu trữ (LMA) và nơi nó được sử dụng (VMA):
.data :
{
*(.data)
} > SRAM AT > FLASH
Ý nghĩa:
> SRAM= VMA: dữ liệu sẽ được sử dụng ở SRAM khi chạyAT > FLASH= LMA: dữ liệu sẽ được lưu ở FLASH khi nạp- Startup code sẽ copy dữ liệu từ FLASH → SRAM
Ví Dụ Thực Tế: STM32F103
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
/* Vector table và code ở FLASH */
.text :
{
KEEP(*(.isr_vector))
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
} > FLASH
/* Dữ liệu khởi tạo: lưu ở FLASH, copy vào SRAM */
.data :
{
__data_start__ = .;
*(.data)
*(.data*)
__data_end__ = .;
} > SRAM AT > FLASH
/* LMA của section data */
__data_lma = LOADADDR(.data);
/* BSS (chưa khởi tạo): chỉ ở SRAM */
.bss :
{
__bss_start__ = .;
*(.bss)
*(.bss*)
*(COMMON)
__bss_end__ = .;
} > SRAM
/* Stack ở cuối SRAM */
.stack :
{
. = ALIGN(8);
__stack_start__ = .;
. += 4K;
__stack_end__ = .;
} > SRAM
/* Heap */
.heap :
{
__heap_start__ = .;
. += 2K;
__heap_end__ = .;
} > SRAM
}
Symbols và Ký Hiệu Đặc Biệt
Linker script tạo ra các ký hiệu (symbols) mà C code có thể sử dụng:
/* Trong C code */
extern uint32_t __data_start__;
extern uint32_t __data_end__;
extern uint32_t __data_lma;
extern uint32_t __bss_start__;
extern uint32_t __bss_end__;
extern uint32_t __stack_end__;
void SystemInit(void)
{
/* Copy initialized data từ FLASH vào SRAM */
uint32_t size = &__data_end__ - &__data_start__;
memcpy(&__data_start__, &__data_lma, size);
/* Xóa BSS section */
size = &__bss_end__ - &__bss_start__;
memset(&__bss_start__, 0, size);
}
Công Cụ Phân Tích
| Công Cụ | Lệnh | Mục Đích |
|---|---|---|
| arm-none-eabi-size | arm-none-eabi-size file.elf |
Xem kích thước text, data, bss |
| arm-none-eabi-nm | arm-none-eabi-nm -n file.elf |
Liệt kê các symbol và địa chỉ |
| arm-none-eabi-objdump | arm-none-eabi-objdump -h file.elf |
Xem headers và sections |
Kết Luận
Linker Script File là một phần không thể thiếu trong lập trình nhúng. Hiểu rõ cách nó hoạt động giúp ta viết phần mềm hiệu quả, tránh được các lỗi khó phát hiện liên quan đến bộ nhớ. Với các ví dụ và best practices ở trên, bạn đã có đủ kiến thức để viết hoặc sửa đổi linker script cho các dự án nhúng của mình.
>>>>>> Follow ngay <<<<<<<
Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊

Rất bổ ích, Cảm ơn nhiều ạ
Trả lờiXóaMình cảm ơn rất nhiêu vì chuỗi bài viết này
Trả lờiXóaCảm ơn bạn đã ủng hộ blog
Xóa