gcc जीसीसी टूलचैन का उपयोग करके दो-भाग फर्मवेयर छवि बनाना




linker embedded (2)

मेरे पास जीसीसी के साथ निर्मित कुछ फर्मवेयर है जो एआरएम कॉर्टेक्स एम 0 आधारित माइक्रोकंट्रोलर पर चलता है। बिल्ड में एक एकल बाइनरी छवि उत्पन्न होती है जिसे माइक्रोकंट्रोलर की प्रोग्राम मेमोरी में लिखा जा सकता है।

फ़ील्ड अपडेट के साथ करने के कारणों के लिए, मुझे इस इमेज को दो भागों में विभाजित करने की आवश्यकता है जिसे अलग से अपडेट किया जा सकता है। मैं इन कोर और ऐप को फोन करूंगा

  • कोर : इसमें इंटरप्ट वेक्टर टेबल, main() दिनचर्या, और विभिन्न चालकों और पुस्तकालय रूटीन शामिल हैं। यह प्रोग्राम मेमोरी के पहले छमाही में स्थित होगा।

  • ऐप : ऐप्लिकेशन-विशिष्ट कोड शामिल हैं यह प्रोग्राम स्मृति के दूसरे छमाही में स्थित होगा यह एक ज्ञात पते पर एक एकल प्रविष्टि बिंदु होगा, जिसे एप्लिकेशन शुरू करने के लिए कोर द्वारा कहा जाता है। यह ज्ञात पते के माध्यम से कोर में फ़ंक्शंस और डेटा एक्सेस करेगा।

यहां कुछ स्पष्ट सीमाएं हैं, जो मुझे अच्छी तरह जानते हैं:

  • एप का निर्माण करते समय, कोर में प्रतीकों के पते को ज्ञात होने की आवश्यकता होगी। तो कोर पहले बनाया जाना चाहिए, और एप्लिकेशन को लिंक करते समय उपलब्ध होना चाहिए।

  • एक ऐप छवि केवल उस विशिष्ट कोर छवि के साथ संगत होगी, जिसका निर्माण इसके विरुद्ध किया गया था

  • कोर को अपडेट किए बिना ऐप को अपडेट करना संभव होगा, लेकिन इसके विपरीत नहीं।

ये सब ठीक है।

मेरा प्रश्न बस, मैं जीसीसी और जीएनयू binutils का उपयोग कर इन छवियों को कैसे बना सकते हैं?

मूलतः मैं एक सामान्य फर्मवेयर छवि की तरह कोर बनाना चाहता हूं, और फिर ऐप इमेज का निर्माण करना चाहता हूं, जिसमें लाइब्रेरी की तरह मूल के एप का इलाज किया जाता है लेकिन न तो साझा लिंक (जो एक डायनेमिक लिंकिंग तंत्र की आवश्यकता होगी) या स्थैतिक लिंकिंग (जो एप बाइनरी में इस्तेमाल की जाने वाली मूल फ़ंक्शन की नकल होगी) यहां लागू होते हैं। मैं जो करने की कोशिश कर रहा हूं वह वास्तव में बहुत आसान है: एक मौजूदा बाइनरी के खिलाफ लिंक जिसका ज्ञात, निर्धारित पते यह सिर्फ मुझे स्पष्ट नहीं है कि उपकरण के साथ ऐसा कैसे करना है


सबसे पहले ... यदि यह केवल क्षेत्र अपडेट करने के लिए है, तो आपको ऐप के लिए मुख्य स्थान में वेक्टर तालिका के बीच में भरोसा करने की आवश्यकता नहीं है। मुझे लगता है कि एआरएम एम 0 भागों में हमेशा इसे स्थानांतरित करने की क्षमता होती है। मैं जानता हूं कि यह कुछ (सभी?) STM32Fx सामान पर किया जा सकता है, लेकिन मेरा मानना ​​है कि यह एक एआरएम एमएक्स बात है, एसटी नहीं है। अपना आवेदन आईएसआर बनाने के फैसले पर खुद को सम्मिलित करने से पहले इसे देखें, सभी को कोर से बुलाए गए हुक बनें।

यदि आप अपने कोर (बीटीडब्लू, मैं हमेशा उस टुकड को कॉल करता है जो एमसीयू पर "बूटलोडर" को स्वयं अपडेट करता है) के साथ बहुत सारी बातचीत करने की योजना बना रही है, तो यह एक वैकल्पिक सुझाव है:

क्या कोर एक संरचना / फ़ंक्शन के सूचक को पास करता है जो ऐप एंट्री बिंदु में इसकी क्षमताओं का वर्णन करता है?

यह साझा शेर के लिए ऐप बनाम कोर के कोड के पूर्ण पृथक्करण की अनुमति देगा (आपके एबीआई को संभालने की कोई बात नहीं है) और नाम टक्कर को रोकने के लिए।

यह जीसीसी को किसी भी कार्य को ऑप्टिमाइज़ करने से रोकने के लिए एक उचित तरीका भी प्रदान करता है, जिसे आप केवल ऐप से ही अपनी ऑप्टिमाइज़ेशन सेटिंग को अपरिवर्तित कर सकते हैं या प्रगामा के साथ पंगा ले सकते हैं।

core.h:

struct core_functions
{
    int (*pcore_func1)(int a, int b);
    void (*pcore_func2)(void);
};

core.c:

int core_func1(int a, int b){ return a + b; }
void core_func2(void){ // do something here }

static const struct core_functions cfuncs= 
{
    core_func1,
    core_func2
};

void core_main()
{
   // do setup here
   void (app_entry*)(const struct core_functions *) = ENTRY_POINT;
   app_entry( &cfuncs );
}

app.c

void app_main(const struct core_functions * core)
{
   int res;
   res = core->pcore_func1(20, 30);
}

डाउनसाइड / कॉस्ट थोड़ी रनटाइम और मेमोरी ओवरहेड और अधिक कोड है।


अब हम यह काम कर रहे हैं इसलिए मैं अपने प्रश्न का उत्तर देने जा रहा हूं। ऐसा करने के लिए क्या आवश्यक था, सामान्य एकल छवि निर्माण से शुरू, "कोर" में बदलकर और फिर "ऐप" के लिए बिल्ड सेट करना।

  1. तय करें कि कैसे फ्लैश और रैम दोनों को अलग-अलग क्षेत्रों में कोर और ऐप के लिए अलग करना है प्रत्येक क्षेत्र के प्रारंभिक पता और आकार को परिभाषित करें

  2. मूल के लिए एक लिंकर स्क्रिप्ट बनाएं। यह मंच के लिए मानक लिंकर स्क्रिप्ट के समान होगा, सिवाय इसके कि यह केवल कोर के लिए आरक्षित क्षेत्रों का उपयोग करना चाहिए। यह लिंकर स्क्रिप्ट के MEMORY अनुभाग में फ़्लैश और रैम प्रविष्टियों के ORIGIN और LENGTH को बदलकर किया जा सकता है।

  3. एप के लिए एंट्री पॉइंट को घोषित करने वाला हेडर फाइल बनाएं यह सिर्फ एक प्रोटोटाइप उदाहरण की आवश्यकता है:

void app_init(void);

  1. इस हेडर को मुख्य सी कोड से शामिल करें और ऐप को शुरू करने के लिए मुख्य कॉल app_init() करें

  2. एंट्री प्वाइंट के पते की घोषणा करते हुए एक प्रतीक फ़ाइल बनाएं, जो एप के लिए फ्लैश एरिया का प्रारंभ पता होगा। मैं इस app.sym । यह केवल निम्न स्वरूप में एक पंक्ति हो सकती है:

app_init = 0x00010000;

  1. मुख्य लिंकर स्क्रिप्ट का उपयोग करके कोर बनाएं, और --just-symbols=app.sym को linker पैरामीटर में app_init का पता प्रदान app_init । बिल्ड से ELF फ़ाइल को बनाए रखें, जिसे मैं core.elf कॉल कर core.elf

  2. ऐप के लिए एक लिंकर स्क्रिप्ट बनाएं यह फिर से मंच के लिए मानक लिंकर स्क्रिप्ट पर आधारित होगा, लेकिन फ्लैश और रैम मेमोरी रेंज के साथ ऐप के लिए आरक्षित लोगों को बदल दिया गया है। साथ ही, यह सुनिश्चित करने के लिए एक विशेष अनुभाग की आवश्यकता होगी कि app_init क्षेत्र की शुरुआत में app_init रखा गया है, शेष app_init कोड में से पहले।

SECTIONS
{
    .text :
    {
        KEEP(*(.app_init))
        *(.text*)
  1. app_init फ़ंक्शन लिखें यह विधानसभा में होना जरूरी है, क्योंकि ऐप में किसी भी सी कोड से पहले इसे कम स्तर पर काम करना चाहिए। इसे .section .app_init के साथ चिह्नित करने की आवश्यकता होगी ताकि लिंकर इसे एप फ्लैश क्षेत्र की शुरुआत में सही स्थान पर .section .app_initapp_init फ़ंक्शन की आवश्यकता है:

    1. एप के .data अनुभाग में फ्लैश से प्रारंभिक मानों के साथ चर को पॉप्युलेट करें।
    2. ऐप के। .bss अनुभाग में चर को शून्य पर सेट करें।
    3. ऐप के लिए सी एंट्री पॉइंट को कॉल करें, जिसे मैं app_start() कॉल करता हूँ
  2. app_start() फ़ंक्शन लिखें जो ऐप को प्रारंभ करता है

  3. ऐप लिंकर स्क्रिप्ट का उपयोग करके ऐप बनाएं। इस लिंक के चरण में app_init , app_start और ऑब्जेक्ट फाइलों को app_init द्वारा बुलाए जाने वाले कोड को पारित किया जाना चाहिए जो पहले से कोर में नहीं है। लिंकर पैरामीटर --just-symbols=core.elf को अपने पतों के द्वारा कोर में फ़ंक्शन को लिंक करने के लिए पारित किया जाना चाहिए। साथ ही, सामान्य सी रनटाइम स्टार्टअप कोड को छोड़ने के लिए -nostartfiles को पारित किया जाना चाहिए।

यह सब कुछ निकालने के लिए कुछ समय लगा, लेकिन यह अब अच्छी तरह से काम कर रहा है







binutils