รหัสเริ่มต้นเปลือยโลหะสำหรับการเริ่มต้นภูมิภาค Cortex M3 .bss


10

ฉันได้พัฒนาแรงบันดาลใจจากที่นี่เป็นรหัสเริ่มต้นโลหะเปลือยสำหรับ arm cortex M3 อย่างไรก็ตามฉันพบปัญหาต่อไปนี้: สมมติว่าฉันประกาศตัวแปรโกลบอลที่ไม่ได้กำหนดค่าเริ่มต้น, พูดถึงประเภท char ที่ไม่ได้ลงนามใน main.c

#include ...
unsigned char var; 
...
int main()
{
 ...
}

สิ่งนี้ทำให้ภูมิภาค. bbs ใน STM32 f103 เริ่มต้นที่ _BSS_START = 0x20000000 และสิ้นสุดที่ _BSS_END = 0x20000001 ตอนนี้รหัสเริ่มต้น

    unsigned int * bss_start_p = &_BSS_START; 
    unsigned int * bss_end_p = &_BSS_END;

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }

พยายามที่จะเริ่มต้นให้เป็นศูนย์ทั้งภูมิภาค. BSS อย่างไรก็ตามภายในนั้นในขณะที่ลูปตัวชี้เพิ่มขึ้นด้วย 4 ไบต์ดังนั้นหลังจากหนึ่งขั้นตอน bss_start_p = 0x20000004 ดังนั้นมันจะแตกต่างจาก bss_end_p ซึ่งจะนำไปสู่การวนซ้ำไม่สิ้นสุดเป็นต้น

มีวิธีแก้ไขปัญหามาตรฐานนี้หรือไม่? ฉันคิดว่าจะ "บังคับ" อย่างใดมิติของภูมิภาค. bbs ที่จะเป็นทวีคูณของ 4? หรือฉันควรใช้ตัวชี้เพื่อถ่านที่ไม่ได้ลงชื่อเพื่อเดินผ่าน. bss ภูมิภาค? บางทีสิ่งที่ชอบ:

    unsigned char * bss_start_p = (unsigned char *)(&_BSS_START); 
    unsigned char * bss_end_p = (unsigned char *)(&_BSS_END);

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }
```

ใช้น้อยกว่า bootstraps ถูกเขียนขึ้นในการชุมนุมด้วยเหตุผล ก่อนอื่นตอนนี้คุณได้สร้างปัญหา. data มันเป็นเรื่องไก่และไข่ที่จะใช้ / สมมติว่า C ทำงานได้คุณต้องพึ่งพา. text, .bss และ. data ขั้นต่ำ แต่คุณกำลังเขียนรหัส C ที่ทำให้แน่ใจว่ารหัส C จะทำงานได้โดยใช้สิ่งต่าง ๆ ในรหัส C ที่ต้องการ bootstrap อาจจะเขียนในรหัส C ที่ขึ้นอยู่กับการทำงานของ C
old_timer

รหัสที่จะคัดลอก. data ไปนั้นคล้ายกับ. bss มาก แต่ถ้าคุณเขียนมันเหมือนโค้ดข้างต้นคุณจำเป็นต้องมี. data ที่คัดลอกมาเพื่อคัดลอก. data
old_timer

คำตอบ:


15

ในขณะที่คุณสงสัยว่าสิ่งนี้เกิดขึ้นเนื่องจากชนิดข้อมูล int ที่ไม่ได้ลงนามมีขนาด 4 ไบต์ แต่ละ*bss_start_p = 0;ข้อความสั่งจะล้างสี่ไบต์ของพื้นที่ bss

ช่วงหน่วยความจำ bss จะต้องจัดตำแหน่งอย่างถูกต้อง คุณสามารถกำหนด _BSS_START และ _BSS_END ได้ง่ายๆเพื่อให้ขนาดโดยรวมเป็นทวีคูณของสี่ แต่โดยทั่วไปแล้วสิ่งนี้จะถูกจัดการโดยการอนุญาตให้สคริปต์ linker กำหนดตำแหน่งเริ่มต้นและหยุด

ตัวอย่างเช่นนี่คือส่วน linker ในหนึ่งในโครงการของฉัน:

.bss (NOLOAD) : ALIGN(4)
{
    __bss_start__ = .;
    *(.bss)
    . = ALIGN(4);
    __bss_end__ = .;
} >RAM

ALIGN(4)งบดูแลสิ่ง

นอกจากนี้คุณอาจต้องการเปลี่ยน

while(bss_start_p != bss_end_p)

ถึง

while(bss_start_p < bss_end_p).

สิ่งนี้จะไม่ป้องกันปัญหา (เนื่องจากคุณอาจกำลังเคลียร์ 1-3 ไบต์มากกว่าที่คุณต้องการ) แต่มันสามารถลดผลกระทบได้ :)


@ CMarius เมื่อใคร่ครวญฉันคิดว่าความคิดตัวชี้ถ่านของคุณจะทำงานได้ดีแม้ว่ามันจะต้องใช้รอบมากขึ้น แต่ฉันไม่แน่ใจว่าจะมีปัญหาตามมาภายหลังกับพื้นที่หน่วยความจำถัดไปที่ไม่ได้จัดแนวดังนั้นฉันจะไม่พูดถึงมันในคำตอบของฉัน ...
274244

1
while(bss_start_p < bss_end_p - 1)ตามด้วยการล้างช่วงไบต์ที่เหลือของไบต์อย่างชาญฉลาดจะช่วยลดข้อกังวลล่าสุด
glglgl

4

ทางออกมาตรฐานคือmemset():

#include <string.h>
memset(&_BSS_START, 0, &_BSS_END - &_BSS_START)

ถ้าคุณไม่สามารถใช้ห้องสมุดมาตรฐานแล้วคุณจะต้องตัดสินใจว่าก็ ok ในกรณีของคุณรอบขนาดของพื้นที่หน่วยความจำได้ถึง 4 ไบต์และดำเนินการต่อโดยใช้unsigned int *; unsigned char *หรือหากคุณต้องการจะเข้มงวดเกี่ยวกับเรื่องนี้ซึ่งในกรณีนี้คุณจะต้องใช้

หากคุณทำขนาดให้กลมขึ้นเช่นในวงแรกของคุณbss_start_pอาจจบลงได้มากกว่าbss_end_pแต่ก็ง่ายที่จะจัดการกับการเปรียบเทียบน้อยกว่า<แทนที่จะทดสอบความไม่เท่าเทียมกัน

แน่นอนคุณสามารถเติมพื้นที่หน่วยความจำส่วนใหญ่ด้วยการถ่ายโอนแบบ 32 บิตและเพียงไม่กี่ไบต์สุดท้ายด้วยการถ่ายโอนแบบ 8 บิต แต่นั่นทำงานได้มากขึ้นเพื่อผลประโยชน์เพียงเล็กน้อยโดยเฉพาะที่นี่เมื่อเป็นรหัสเริ่มต้นเพียงชิ้นเดียว


1
memset()เห็นด้วยอย่างมากกับการใช้งานของ แต่การจัดตำแหน่ง 4 ไบต์จำเป็นต้องมีมากหรือน้อย ดังนั้นทำไมไม่ทำมัน?
Codo

3
ไม่ว่าในรูปทรงหรือรูปแบบใดเป็นโซลูชันมาตรฐานสำหรับ bootstrap เพื่อใช้ memset นั่นมันบ้ามาก
old_timer

คุณไม่ใช้ภาษาเดียวกันในการบูตภาษานั้น
old_timer

2
รหัส bootstrap และสคริปต์ linker แต่งงานกันอย่างมากคุณจะพบว่าสคริปต์ linker จัดตำแหน่งและปรับขนาด. bss อย่างน้อยขอบเขต 4 ไบต์เพื่อปรับปรุงการเติม (ใน bootstrap) โดย 4x มากกว่าไบต์ในเวลา (สมมติว่า (ขั้นต่ำ) บัส 32 บิตซึ่งเป็นเรื่องปกติสำหรับแขน แต่มีข้อยกเว้น)
old_timer

3
@old_timer, ฟังก์ชัน C มาตรฐานเพื่อตั้งค่าหน่วยความจำให้เป็นค่าเฉพาะmemset()และ C คือสิ่งที่พวกเขาดูเหมือนจะเขียนโปรแกรมระบบการใช้งานที่เรียบง่ายของmemset()มันก็ค่อนข้างมากแค่ห่วงนั้นมันไม่เหมือนมันขึ้นอยู่กับสิ่งอื่นมาก เนื่องจากเป็นไมโครคอนโทรลเลอร์ฉันจึงสันนิษฐานว่าไม่มีการเชื่อมโยงแบบไดนามิกหรือเกิดขึ้น (และดูที่การเชื่อมโยงนั่นไม่ใช่มันเป็นเพียงการเรียกไปยังmain()ลูป zeroing นั้น) ดังนั้นคอมไพเลอร์ควรจะสามารถวางลงที่memset()นั่นได้ พร้อมกับàฟังก์ชั่นอื่น ๆ (หรือนำไปใช้แบบอินไลน์)
ilkkachu

4

การเปลี่ยนแปลงเพียงเพื่อ!= <นั่นเป็นวิธีที่ดีกว่าอยู่แล้วเนื่องจากมันเกี่ยวข้องกับปัญหาเช่นนี้


3

มีเว็บไซต์และตัวอย่างอื่น ๆ อีกนับไม่ถ้วน หลายพันถ้าไม่นับหมื่น มีไลบรารี c ที่รู้จักกันดีพร้อมสคริปต์ตัวเชื่อมโยงและรหัส boostrap, newlib, glibc โดยเฉพาะ แต่มีคนอื่นที่คุณสามารถค้นหาได้ Bootstraping C กับ C ไม่มีเหตุผล

คำถามของคุณได้รับคำตอบว่าคุณกำลังพยายามเปรียบเทียบสิ่งที่อาจไม่ถูกต้องอาจไม่เริ่มในขอบเขตที่รู้จักหรือสิ้นสุดในขอบเขตที่ทราบ ดังนั้นคุณสามารถทำสิ่งที่น้อยกว่า แต่ถ้ารหัสไม่ได้ทำงานกับการเปรียบเทียบที่แน่นอนนั่นหมายความว่าคุณเป็นศูนย์. อดีตที่ผ่านมาในส่วนถัดไปซึ่งอาจหรืออาจไม่ก่อให้เกิดสิ่งที่ไม่ดีเกิดขึ้นดังนั้นเพียงแค่แทนที่ด้วยน้อยกว่า การแก้ไขปัญหา.

ดังนั้นที่นี่จะไป TL; DR เป็นเรื่องปกติ คุณไม่ต้องบูตภาษาด้วยภาษานั้นคุณสามารถหนีไปได้ แต่คุณกำลังเล่นด้วยไฟเมื่อคุณทำเช่นนั้น หากคุณเป็นเพียงการเรียนรู้วิธีการทำเช่นนี้คุณจะต้องอยู่ในความระมัดระวังไม่โชคดีหรือข้อเท็จจริงที่คุณยังไม่ได้เปิด

สคริปต์ตัวเชื่อมโยงและรหัส bootstrap มีความสัมพันธ์ที่ใกล้ชิดมากพวกเขาแต่งงานเข้าร่วมที่สะโพกคุณไม่ได้พัฒนาสิ่งใดสิ่งหนึ่งที่นำไปสู่ความล้มเหลวครั้งใหญ่ และน่าเสียดายที่สคริปต์ตัวเชื่อมโยงถูกกำหนดโดยตัวเชื่อมโยงและภาษาแอสเซมบลีที่กำหนดโดยแอสเซมเบลอร์เมื่อคุณเปลี่ยนชุดเครื่องมือคาดว่าจะต้องเขียนใหม่ทั้งสอง ทำไมต้องประกอบภาษา มันไม่ต้องการ bootstrap ภาษาที่คอมไพล์โดยทั่วไปทำ C ถ้าคุณไม่ต้องการ จำกัด การใช้งานของคุณในการเริ่มต้นด้วยบางสิ่งที่ง่ายมากที่มีข้อกำหนดเฉพาะของ toolchain น้อยที่สุดคุณไม่คิดว่าตัวแปร. bss เป็นศูนย์ (ทำให้โค้ดอ่านน้อยลงถ้าตัวแปรไม่เคยเริ่มต้นในภาษานั้น พยายามหลีกเลี่ยงสิ่งนี้ไม่เป็นความจริงสำหรับตัวแปรในท้องที่ดังนั้นคุณจึงต้องใช้มันเหมือนเมื่อคุณใช้มัน แล้วทำไมเราถึงพูดถึง. bss และ. data ??? (กลมดีสำหรับการทำงานในระดับนี้ แต่นั่นเป็นหัวข้ออื่น)) กฎอื่น ๆ สำหรับการแก้ปัญหาง่ายๆคืออย่าเริ่มต้นตัวแปรในการประกาศทำในรหัส ใช่เบิร์นส์แฟลชมากขึ้นโดยทั่วไปคุณมีจำนวนมากไม่ใช่ว่าตัวแปรทั้งหมดจะเริ่มต้นด้วยค่าคงที่ต่อไปซึ่งจะจบลงด้วยคำแนะนำในการบริโภค

คุณสามารถบอกได้จากการออกแบบคอร์เท็กซ์ -m ว่าพวกเขาอาจกำลังคิดว่าไม่มีรหัสบูทสแตรปเลยไม่สนับสนุน. data หรือ. bbs คนส่วนใหญ่ที่ใช้ globals ไม่สามารถอยู่โดยที่นี่

ฉันสามารถทำให้มันน้อยลง แต่เป็นตัวอย่างการทำงานที่น้อยที่สุดสำหรับคอร์เท็กซ์ -ms ทั้งหมดโดยใช้ gnu toolchain ฉันจำไม่ได้ว่ารุ่นใดที่คุณสามารถเริ่มต้นด้วย 5.xx หรือมากกว่านั้นจนถึง 9.xx ปัจจุบันฉันสลับสคริปต์ลิงเกอร์ที่อื่นประมาณ 3 xx หรือ 4.xx เมื่อฉันเรียนรู้มากขึ้นและเมื่อ gnu เปลี่ยนบางสิ่งที่ทำให้ฉันเสียครั้งแรก

บูต:

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

จุดเข้าสู่รหัส C:

void bounce ( unsigned int );

unsigned int a;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

สคริปต์ลิงเกอร์

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

สิ่งเหล่านี้อาจเล็กลงและยังใช้งานได้เพิ่มสิ่งพิเศษบางอย่างที่นี่เพื่อดูในที่ทำงาน

การสร้างและลิงก์ที่ปรับให้เหมาะสม

00000000 <_start>:
   0:   20001000
   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

00000014 <reset>:
  14:   f000 f804   bl  20 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
    ...

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

สำหรับผู้ค้าบางรายที่คุณต้องการใช้ 0x08000000 หรือ 0x01000000 หรือที่อยู่อื่นที่คล้ายคลึงกันเนื่องจากมีการทำแผนที่แฟลชและสะท้อนเป็น 0x00000000 ในโหมดการบูตบางโหมด บางตัวมีแฟลชมิรเรอร์จำนวนมากที่ 0x00000000 ดังนั้นคุณจึงต้องการให้จุดตารางเวกเตอร์ที่พื้นที่แฟลชของแอปพลิเคชันไม่เป็นศูนย์ เนื่องจากมันเป็นตารางเวกเตอร์ที่ใช้ทุกอย่างทำงานได้

ก่อนอื่นเยื่อหุ้มสมอง -ms เป็นเพียงนิ้วโป้งเครื่องเท่านั้นและด้วยเหตุผลใดก็ตามที่พวกเขาบังคับใช้ที่อยู่ฟังก์ชั่นนิ้วหัวแม่มือหมายถึง lsbit เป็นคี่ รู้ว่าเครื่องมือของคุณคำสั่ง. thumb_func บอกแอสเซมเบลอร์ gnu ว่าเลเบลถัดไปเป็นแอดเดรสของฟังก์ชันนิ้วหัวแม่มือ การทำสิ่ง +1 ในตารางจะนำไปสู่ความล้มเหลวอย่าถูกล่อลวงให้ทำถูกต้อง มีวิธีการประกอบ gnu อื่น ๆ ที่จะประกาศฟังก์ชั่นนี้เป็นวิธีการที่น้อยที่สุด

   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

มันจะไม่บูตหากคุณไม่ได้ตารางเวกเตอร์ที่ถูกต้อง

เนื้อหาต้องการเฉพาะเวกเตอร์ตัวชี้สแต็ก (สามารถใส่สิ่งใดก็ได้ในนั้นถ้าคุณต้องการตั้งค่าตัวชี้สแต็กด้วยตัวคุณเองในโค้ด) และเวกเตอร์รีเซ็ต ฉันใส่สี่ที่นี่โดยไม่มีเหตุผลพิเศษ มักจะใส่ 16 แต่ต้องการย่อตัวอย่างนี้

ดังนั้น bootstrap C ขั้นต่ำสุดที่ต้องทำคืออะไร? 1. ตั้งค่าพอยน์เตอร์สแต็ค 2. zero .bss 3. คัดลอก. data 4. branch ไปยังหรือเรียกจุดเข้า C

จุดเข้า C มักจะเรียกว่า main () แต่เครื่องมือบางอันจะเห็น main () และเพิ่มขยะพิเศษในโค้ดของคุณ ฉันใช้ชื่ออื่นโดยเจตนา YMMV

ไม่จำเป็นต้องใช้สำเนาของ. data หากเป็น ram ทั้งหมด การเป็นไมโครคอนโทรลเลอร์คอร์เท็กซ์ -m เป็นไปได้ในทางเทคนิค แต่ไม่น่าเป็นไปได้ดังนั้นจึงจำเป็นต้องมีการคัดลอก. data ..... ถ้ามี. data

ตัวอย่างแรกของฉันและรูปแบบการเข้ารหัสคือไม่ต้องพึ่งพา. data หรือ. bbs ดังเช่นในตัวอย่างนี้ แขนดูแลตัวชี้สแต็กดังนั้นสิ่งเดียวที่เหลือคือเรียกจุดเข้า ฉันชอบที่จะมีมันเพื่อให้จุดเริ่มต้นสามารถกลับมาหลายคนยืนยันว่าคุณไม่ควรทำอย่างนั้น คุณสามารถทำสิ่งนี้ได้แล้ว:

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word centry
.word done
.word done
.word done

และไม่ส่งคืนจาก centry () และไม่มีรหัสตัวจัดการรีเซ็ต

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000

ตัวเชื่อมโยงใส่สิ่งที่เราถาม และโดยรวมแล้วเรามีโปรแกรมที่ทำงานได้อย่างสมบูรณ์

ดังนั้นก่อนอื่นให้ใช้สคริปต์ตัวเชื่อมโยง:

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

เน้นว่าชื่อ rom และ ram ไม่มีความหมายพวกมันเชื่อมต่อจุดสำหรับตัวลิงก์ระหว่างส่วนเท่านั้น

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

.align
.word __data_rom_start__
.word __data_start__
.word __data_end__
.word __data_size__

เพิ่มบางรายการเพื่อให้เราเห็นว่าเครื่องมือทำอะไร

void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

เพิ่มบางรายการที่จะวางในส่วนเหล่านั้น และรับ

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, r11, lsl r0
   c:   0000001b    andeq   r0, r0, r11, lsl r0
  10:   0000001b    andeq   r0, r0, r11, lsl r0

00000014 <reset>:
  14:   f000 f80c   bl  30 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

00000030 <centry>:
  30:   2207        movs    r2, #7
  32:   b510        push    {r4, lr}
  34:   4b04        ldr r3, [pc, #16]   ; (48 <centry+0x18>)
  36:   2007        movs    r0, #7
  38:   601a        str r2, [r3, #0]
  3a:   f7ff ffef   bl  1c <bounce>
  3e:   2000        movs    r0, #0
  40:   bc10        pop {r4}
  42:   bc02        pop {r1}
  44:   4708        bx  r1
  46:   46c0        nop         ; (mov r8, r8)
  48:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

นี่คือสิ่งที่เรากำลังมองหาในการทดสอบนั้น (โปรดทราบว่าไม่มีเหตุผลที่จะโหลดหรือเรียกใช้รหัสใด ๆ ... รู้เครื่องมือของคุณเรียนรู้พวกเขา)

  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

ดังนั้นสิ่งที่เราเรียนรู้ที่นี่คือตำแหน่งของตัวแปรมีความอ่อนไหวมากในสคริปต์ตัวเชื่อมโยง gnu บันทึกตำแหน่งdata_rom_startกับdata_startแต่ทำไมdata_end ถึงทำงาน ไม่ให้คุณคิดออก เข้าใจอยู่แล้วว่าทำไมคนเราอาจไม่ต้องการยุ่งกับ linker script และแค่ไปเขียนโปรแกรมง่ายๆ ...

ดังนั้นสิ่งอื่นที่เราเรียนรู้ที่นี่คือ linker จัดdata_rom_startให้เราเราไม่ต้องการ ALIGN (4) ในนั้น เราควรสมมติว่ามันจะได้ผลเสมอ

นอกจากนี้โปรดทราบว่ามันมีความสะดวกในการออกไปเรามี 5 ไบต์ของ. data แต่มันเพิ่มเป็น 8 โดยไม่มี ALIGN () เราสามารถคัดลอกโดยใช้คำ จากสิ่งที่เราเห็นด้วย toolchain นี้ในคอมพิวเตอร์ของฉันวันนี้มันอาจจะเป็นจริงสำหรับอดีตและอนาคต ใครจะรู้แม้กับ ALIGNs จะต้องตรวจสอบเป็นระยะเพื่อยืนยันบางเวอร์ชั่นใหม่ที่ไม่ทำลายสิ่งต่าง ๆ พวกเขาจะทำเช่นนั้นเป็นครั้งคราว

จากการทดสอบดังกล่าวจะช่วยให้ปลอดภัยได้

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   . = ALIGN(4);
   __data_end__ = .;
   } > ted AT > bob
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   . = ALIGN(4);
   __bss_end__ = .;
   } > ted
   __bss_size__ = __bss_end__ - __bss_start__;

}

ย้ายปลายด้านในให้สอดคล้องกับสิ่งที่คนอื่นทำ และนั่นก็ไม่ได้เปลี่ยน:

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

การทดสอบด่วนอีกหนึ่งข้อ:

.globl bounce
bounce:
    nop
    bx lr

ให้

0000001c <bounce>:
  1c:   46c0        nop         ; (mov r8, r8)
  1e:   4770        bx  lr
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

ไม่จำเป็นต้อง pad ระหว่างการตีกลับและ. align

โอ้จำได้แล้วตอนนี้ทำไมฉันไม่ใส่ _end__ ข้างใน เพราะมันไม่ทำงาน

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

โค้ดที่เรียบง่าย แต่พกพาสะดวกบางอย่างเพื่อแต่งงานกับสคริปต์ตัวเชื่อมโยงนี้

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
bss_zero:
    stmia r1!,{r2}
    sub r0,#4
    bne bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3}
    stmia r2!,{r3}
    sub r0,#4
    bne data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__

ให้

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000003d    andeq   r0, r0, sp, lsr r0
   c:   0000003d    andeq   r0, r0, sp, lsr r0
  10:   0000003d    andeq   r0, r0, sp, lsr r0

00000014 <reset>:
  14:   480c        ldr r0, [pc, #48]   ; (48 <blen>)
  16:   2800        cmp r0, #0
  18:   d004        beq.n   24 <bss_zero_done>
  1a:   490a        ldr r1, [pc, #40]   ; (44 <bstart>)
  1c:   2200        movs    r2, #0

0000001e <bss_zero>:
  1e:   c104        stmia   r1!, {r2}
  20:   3804        subs    r0, #4
  22:   d1fc        bne.n   1e <bss_zero>

00000024 <bss_zero_done>:
  24:   480b        ldr r0, [pc, #44]   ; (54 <dlen>)
  26:   2800        cmp r0, #0
  28:   d005        beq.n   36 <data_copy_done>
  2a:   4908        ldr r1, [pc, #32]   ; (4c <rstart>)
  2c:   4a08        ldr r2, [pc, #32]   ; (50 <dstart>)

0000002e <data_copy>:
  2e:   c908        ldmia   r1!, {r3}
  30:   c208        stmia   r2!, {r3}
  32:   3804        subs    r0, #4
  34:   d1fb        bne.n   2e <data_copy>

00000036 <data_copy_done>:
  36:   f000 f80f   bl  58 <centry>
  3a:   e7ff        b.n 3c <done>

0000003c <done>:
  3c:   e7fe        b.n 3c <done>

0000003e <bounce>:
  3e:   46c0        nop         ; (mov r8, r8)
  40:   4770        bx  lr
  42:   46c0        nop         ; (mov r8, r8)

00000044 <bstart>:
  44:   20000008    andcs   r0, r0, r8

00000048 <blen>:
  48:   00000004    andeq   r0, r0, r4

0000004c <rstart>:
  4c:   00000074    andeq   r0, r0, r4, ror r0

00000050 <dstart>:
  50:   20000000    andcs   r0, r0, r0

00000054 <dlen>:
  54:   00000008    andeq   r0, r0, r8

00000058 <centry>:
  58:   2207        movs    r2, #7
  5a:   b510        push    {r4, lr}
  5c:   4b04        ldr r3, [pc, #16]   ; (70 <centry+0x18>)
  5e:   2007        movs    r0, #7
  60:   601a        str r2, [r3, #0]
  62:   f7ff ffec   bl  3e <bounce>
  66:   2000        movs    r0, #0
  68:   bc10        pop {r4}
  6a:   bc02        pop {r1}
  6c:   4708        bx  r1
  6e:   46c0        nop         ; (mov r8, r8)
  70:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

เราสามารถหยุดตรงนั้นหรือเดินต่อไป หากเราเริ่มต้นในลำดับเดียวกันกับสคริปต์ตัวเชื่อมโยงมันก็โอเคถ้าเราไปสู่สิ่งต่อไปเพราะเรายังไม่ได้ไปที่นั่น และ stm / ldm จำเป็นต้องใช้ / ต้องการใช้ที่อยู่ที่จัดเรียงคำเท่านั้นดังนั้นหากคุณเปลี่ยนเป็น:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

กับ bss เป็นอันดับแรกในตัวเชื่อมโยงสคริปต์และใช่คุณต้องการไม่ bls bls

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3804        subs    r0, #4
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000004    andcs   r0, r0, r4

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

ลูปเหล่านั้นจะไปเร็วขึ้น ตอนนี้ฉันไม่รู้ว่ารถถัง ahb นั้นมีความกว้าง 64 บิตหรือไม่ แต่สำหรับแขนเต็มขนาดคุณต้องการจัดตำแหน่งสิ่งเหล่านี้ในขอบเขต 64 บิต การลงทะเบียนสี่ ldm / stm บนขอบเขต 32 บิต แต่ไม่ใช่ขอบเขต 64 บิตจะกลายเป็นธุรกรรมรถบัสที่แยกกันสามรายการโดยการจัดตำแหน่งบนขอบเขต 64 บิตเป็นธุรกรรมเดียวที่ประหยัดนาฬิกาหลายต่อการเรียนการสอน

เนื่องจากเรากำลังทำ baremetal และเรามีความรับผิดชอบในทุกสิ่งที่เราสามารถพูด bss ก่อนแล้วข้อมูลถ้าเรามีกองทำแล้วกองเติบโตจากด้านบนลงดังนั้นถ้าเราศูนย์ bss และหกกว่าบางตราบเท่าที่เราเริ่มที่ สถานที่ที่เหมาะสมที่เราไม่ได้ใช้หน่วยความจำนั้น จากนั้นเราก็คัดลอก. data ไปแล้วและสามารถกระจายเข้าไปใน heap ได้ดี heap หรือไม่มีที่ว่างมากมายสำหรับ stack ดังนั้นเราจึงไม่เหยียบกับใคร / อะไร (ตราบใดที่เรามั่นใจใน scripter linker ที่เราทำ หากมีข้อกังวลให้ทำให้ ALIGN () ใหญ่ขึ้นเพื่อให้เราอยู่ในพื้นที่ของเราเสมอสำหรับการเติมเหล่านี้

ดังนั้นวิธีง่ายๆของฉันเอาไปหรือทิ้งไป ยินดีต้อนรับสู่แก้ไขข้อบกพร่องใด ๆ ฉันไม่ได้เรียกใช้บนฮาร์ดแวร์หรือตัวจำลองของฉัน ...

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(8);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

   . = ALIGN(8);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

}



.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3,r4,r5,r6}
    stmia r2!,{r3,r4,r5,r6}
    sub r0,#16
    ble data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__


void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-ld -o hello.elf -T flash.ld flash.o centry.o
arm-none-eabi-objdump -D hello.elf > hello.list
arm-none-eabi-objcopy hello.elf hello.bin -O binary

รวบรวมทั้งหมดเข้าด้วยกันและคุณจะได้รับ:

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3810        subs    r0, #16
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000008    andcs   r0, r0, r8

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

โปรดทราบว่าสิ่งนี้ใช้งานได้กับ arm-none-eabi- และ arm-linux-gnueabi และตัวแปรอื่น ๆ เนื่องจากไม่มีการใช้ whee ghee whiz

คุณจะพบว่าเมื่อคุณมองไปรอบ ๆ นั้นผู้คนจะคลั่งไคล้กับ ghee whiz ในสคริปต์ linker ของพวกเขาห้องครัวขนาดใหญ่มหึมาจมสิ่งต่างๆ ดีกว่าที่จะรู้วิธีที่จะทำมัน (หรือดีกว่าวิธีที่เชี่ยวชาญเครื่องมือเพื่อให้คุณสามารถควบคุมสิ่งที่เกิดขึ้น) แทนที่จะพึ่งพาสิ่งที่คนอื่นและไม่ทราบว่ามันจะทำลายเพราะคุณไม่เข้าใจและ / หรือต้องการวิจัย มัน.

ตามกฎทั่วไปห้าม bootstrap ภาษาที่มีภาษาเดียวกัน (bootstrap ในกรณีนี้หมายถึงการเรียกใช้โค้ดที่ไม่คอมไพล์เลอร์ด้วยคอมไพเลอร์เดียวกัน) ที่คุณต้องการใช้ภาษาที่เรียบง่าย แต่มี bootstrap น้อยกว่า นั่นคือสาเหตุที่ C ประกอบเสร็จแล้วมันไม่มีข้อกำหนดบูตสแตรปที่คุณเพิ่งเริ่มจากคำสั่งแรกหลังจากรีเซ็ต JAVA แน่ใจว่าคุณอาจเขียน jvm ใน C และ bootstrap C ที่มี asm จากนั้น bootstrap JAVA หากคุณต้องการด้วย C แต่ยังรัน JAVA ใน C ด้วย

เนื่องจากเราควบคุมสมมติฐานเกี่ยวกับลูปการคัดลอกเหล่านี้พวกเขาจะถูกกำหนดโดยเข้มงวดและสะอาดกว่า memcpy / memset ที่ปรับด้วยมือ

โปรดทราบว่าปัญหาอื่นของคุณคือ:

unsigned int * bss_start_p = &_BSS_START; 
unsigned int * bss_end_p = &_BSS_END;

ถ้าสิ่งเหล่านี้เป็นแบบโลคัลไม่มีปัญหาหากสิ่งเหล่านี้เป็นแบบโกลบอลคุณต้องมี. data เริ่มต้นก่อนเพื่อให้มันทำงานและถ้าคุณลองใช้เคล็ดลับที่จะทำ. data คุณจะล้มเหลว ตัวแปรท้องถิ่นที่ใช้ได้ หากคุณมีเหตุผลบางอย่างตัดสินใจที่จะทำให้คนในท้องถิ่นคงที่ (ฉันต้องการเรียกพวกเขา) คุณจะกลับมามีปัญหาอีกครั้ง ทุกครั้งที่คุณทำงานที่ได้รับมอบหมายในการประกาศถึงแม้ว่าคุณควรคิดเกี่ยวกับมันวิธีการที่มีการใช้งานและมีความปลอดภัย / สติ ทุกครั้งที่คุณถือว่าตัวแปรเป็นศูนย์เมื่อไม่ได้ประกาศใช้ข้อตกลงเดียวกันหากตัวแปรท้องถิ่นไม่ถือว่าเป็นศูนย์ถ้าเป็นสากลก็จะเป็นเช่นนั้น หากคุณไม่เคยคิดว่ามันจะเป็นศูนย์คุณก็ไม่ต้องกังวล


น่ากลัวนี่เป็นครั้งที่สองที่ฉันได้เกินจำนวนอักขระสูงสุดในคำตอบ ....
old_timer

คำถามนี้อยู่ใน stackoverflow ไม่ใช่วิศวกรรมไฟฟ้า
old_timer

การพึ่งพาลิงก์ภายนอกในคำถามของคุณนั้นไม่ได้อยู่ในรูปแบบที่ดีหากลิงก์นั้นหายไปก่อนที่คำถามนั้นคำถามนั้นอาจไม่สมเหตุสมผล
old_timer

ในกรณีนี้ชื่อและเนื้อหาของคุณก็เพียงพอที่จะรู้ว่าคุณกำลังพยายามบูท C ในไมโครคอนโทรลเลอร์ที่เฉพาะเจาะจงและกำลังเข้าสู่การเริ่มต้น. bss และ. data
old_timer

แต่ในกรณีนี้ทำให้เข้าใจผิดโดยเว็บไซต์ที่ให้ข้อมูลอย่างอื่น
old_timer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.