GCC के साथ C / C ++: निष्पादन योग्य / पुस्तकालय में संसाधन फ़ाइलों को सांख्यिकीय रूप से जोड़ें


94

क्या किसी को यह अंदाजा है कि जीसीसी का उपयोग करते हुए निष्पादन योग्य या साझा लाइब्रेरी फ़ाइल में किसी भी संसाधन फ़ाइल को सांख्यिकीय रूप से कैसे संकलित किया जाए?

उदाहरण के लिए, मैं ऐसी छवि फ़ाइलें जोड़ना चाहूंगा, जो कभी न बदलें (और यदि वे करते हैं, तो मुझे फ़ाइल को वैसे भी बदलना होगा) और वे नहीं चाहेंगे कि वे फ़ाइल सिस्टम में झूठ बोलें।

यदि यह संभव है (और मुझे लगता है कि यह इसलिए है क्योंकि विंडोज के लिए विजुअल सी ++ यह भी कर सकता है), मैं उन फाइलों को कैसे लोड कर सकता हूं जो स्वयं बाइनरी में संग्रहीत हैं? क्या निष्पादन योग्य पार्स खुद ही फाइल ढूंढता है और उसमें से डेटा निकालता है?

शायद जीसीसी के लिए एक विकल्प है जिसे मैंने अभी तक नहीं देखा है। खोज इंजन का उपयोग वास्तव में सही सामान बाहर थूक नहीं था।

मुझे साझा पुस्तकालयों और सामान्य ईएलएफ-निष्पादनयोग्य के लिए काम करने की आवश्यकता होगी।

किसी भी मदद की सराहना की है


3
के संभावित डुप्लिकेट stackoverflow.com/questions/1997172/...
blueberryfields

की ओर इशारा किया सवाल blueberryfields में objcopy लिंक भी एक अच्छा है, इस के लिए सामान्य समाधान है
Flexo

@blueberryfields: डुप्लिकेट करने के लिए क्षमा करें। आप सही हे। आम तौर पर मैं डुप्लिकेट के रूप में करीब से मतदान करता हूं। लेकिन क्योंकि वे सभी इतने अच्छे उत्तर पोस्ट करते हैं, मैं सिर्फ एक को स्वीकार करूंगा।
18

क्या मैं यह जोड़ सकता हूं कि जॉन रिप्ले का तरीका शायद एक सबसे बड़ा कारण है - एक बहुत बड़ा कारण। यदि आप एक मानक objcopy या "ld -r -b -b foo.o foo.txt" करते हैं और फिर परिणामी वस्तु को objdump -x के साथ देखते हैं तो ऐसा लगता है कि ब्लॉक के लिए संरेखण 0 पर सेट है। द्विआधारी डेटा चार के अलावा के लिए सही होने के लिए संरेखण, मैं कल्पना नहीं कर सकता यह एक अच्छी बात है।
१६

जवाबों:


49

इमेजमैजिक के साथ :

convert file.png data.h

कुछ इस तरह देता है:

/*
  data.h (PNM).
*/
static unsigned char
  MagickImage[] =
  {
    0x50, 0x36, 0x0A, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 
    0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4D, 0x50, 0x0A, 0x32, 0x37, 
    0x37, 0x20, 0x31, 0x36, 0x32, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0xFF, 0xFF, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 

....

अन्य कोड के साथ संगतता के लिए आप या तो fmemopen"नियमित" FILE *ऑब्जेक्ट प्राप्त करने के लिए उपयोग कर सकते हैं , या वैकल्पिक रूप std::stringstreamसे एक बनाने के लिए iostreamstd::stringstreamहालांकि इसके लिए महान नहीं है और आप निश्चित रूप से एक सूचक का उपयोग कर सकते हैं कहीं भी आप एक पुनरावृत्ति का उपयोग कर सकते हैं।

यदि आप इसे स्वचालित के साथ उपयोग कर रहे हैं तो उचित रूप से BUILT_SOURCES सेट करना न भूलें ।

इस तरह से करने के बारे में अच्छी बात यह है:

  1. आप पाठ को बाहर निकालते हैं, इसलिए यह संस्करण नियंत्रण और समझदारी से पैच में हो सकता है
  2. यह हर प्लेटफॉर्म पर पोर्टेबल और अच्छी तरह से परिभाषित है

2
Bleahg! यही उपाय मैंने भी सोचा। कोई भी ऐसा क्यों करना चाहेगा यह मुझसे परे है। एक अच्छी तरह से परिभाषित नाम स्थान में डेटा के भंडारण के लिए फाइलसिस्टम क्या हैं।
सर्वार्थसिद्ध

35
कभी-कभी, आपके पास एक निष्पादन योग्य होता है जो चलता है जहां कोई फाइल सिस्टम नहीं है, या यहां तक ​​कि कोई ऑपरेटिंग सिस्टम भी नहीं है। या आपके एल्गोरिथ्म को लुकअप के लिए कुछ पूर्व-निर्धारित तालिका की आवश्यकता है। और मुझे यकीन है वहाँ अधिक मामलों बहुत हैं, जब कार्यक्रम में भंडारण के डेटा एक बनाता हूँ बहुत भावना की।
ndim

15
कन्वर्ट का यह उपयोग ठीक उसी तरह है जैसेxxd -i infile.bin outfile.h
16

5
इस दृष्टिकोण के लिए एक नकारात्मक पक्ष यह है कि कुछ संकलक ऐसे विशाल स्थिर सरणियों को संभाल नहीं सकते हैं, यदि आपकी छवियां विशेष रूप से बड़ी हैं; जिस तरह से चारों ओर पाने का तरीका है, जैसा कि ndim का सुझाव है, objcopyबाइनरी डेटा को सीधे ऑब्जेक्ट फ़ाइल में बदलने के लिए उपयोग करना; हालांकि यह शायद ही कभी एक चिंता का विषय है।
एडम रोसेनफील्ड

3
ध्यान रखें कि इसे हेडर में इस तरह परिभाषित करने का अर्थ है कि प्रत्येक फ़ाइल जिसमें यह शामिल है, उसकी अपनी प्रति प्राप्त होगी। हेडर में इसे बाहरी घोषित करना बेहतर है और फिर इसे एक सीपीपी में परिभाषित करें। यहाँ उदाहरण
निकोलस स्मिथ

90

अपडेट मैं जॉन रिप्ले के असेंबली .incbinबेस्ड सॉल्यूशन ऑफर्स को पसंद करने के लिए बढ़ा हूं और अब उस पर एक वेरिएंट का उपयोग करूंगा ।

मैंने एक फ़ाइल foo-data.bin से बाइनरी डेटा को निष्पादन योग्य के डेटा अनुभाग में लिंक करने के लिए objcopy (GNU binutils) का उपयोग किया है:

objcopy -B i386 -I binary -O elf32-i386 foo-data.bin foo-data.o

यह आपको एक foo-data.oऑब्जेक्ट फ़ाइल देता है जिसे आप अपने निष्पादन योग्य में लिंक कर सकते हैं। C इंटरफ़ेस कुछ ऐसा दिखता है

/** created from binary via objcopy */
extern uint8_t foo_data[]      asm("_binary_foo_data_bin_start");
extern uint8_t foo_data_size[] asm("_binary_foo_data_bin_size");
extern uint8_t foo_data_end[]  asm("_binary_foo_data_bin_end");

तो आप सामान की तरह कर सकते हैं

for (uint8_t *byte=foo_data; byte<foo_data_end; ++byte) {
    transmit_single_byte(*byte);
}

या

size_t foo_size = (size_t)((void *)foo_data_size);
void  *foo_copy = malloc(foo_size);
assert(foo_copy);
memcpy(foo_copy, foo_data, foo_size);

यदि आपके लक्ष्य आर्किटेक्चर में विशेष अड़चनें हैं जहां निरंतर और परिवर्तनीय डेटा संग्रहीत किया जाता है, या आप उस डेटा .textको अपने प्रोग्राम कोड के समान मेमोरी प्रकार में फिट करने के लिए खंड में संग्रहीत करना चाहते हैं , तो आप objcopyकुछ और मापदंडों के साथ खेल सकते हैं ।


अच्छा विचार! मेरे मामले में यह बहुत उपयोगी नहीं है। लेकिन यह कुछ ऐसा है जिसे मैं वास्तव में अपने स्निपेट-संग्रह में डालने जा रहा हूं। इसे साझा करने के लिए धन्यवाद!
18

2
इसका उपयोग करना थोड़ा आसान है ldक्योंकि आउटपुट स्वरूप वहां निहित है, stackoverflow.com/a/4158997/201725 देखें ।
Jan Hudec

52

आप ldलिंकर का उपयोग करके निष्पादन योग्य में बाइनरी फ़ाइलों को एम्बेड कर सकते हैं । उदाहरण के लिए, यदि आपके पास फ़ाइल है, foo.barतो आप इसे निम्नलिखित आदेशों को जोड़कर निष्पादन योग्य में एम्बेड कर सकते हैंld

--format=binary foo.bar --format=default

यदि आप ldथ्रू इनवॉइस कर रहे हैं, gccतो आपको जोड़ना होगा-Wl

-Wl,--format=binary -Wl,foo.bar -Wl,--format=default

यहां --format=binaryलिंकर को बताता है कि निम्न फ़ाइल द्विआधारी है और --format=defaultडिफ़ॉल्ट इनपुट प्रारूप पर वापस आ जाती है (यदि आप बाद में अन्य इनपुट फ़ाइलों को निर्दिष्ट करेंगे तो यह उपयोगी है foo.bar)।

फिर आप अपनी फ़ाइल की सामग्री को कोड से एक्सेस कर सकते हैं:

extern uint8_t data[]     asm("_binary_foo_bar_start");
extern uint8_t data_end[] asm("_binary_foo_bar_end");

नाम का प्रतीक भी है "_binary_foo_bar_size"। मुझे लगता है कि यह प्रकार का है, uintptr_tलेकिन मैंने इसकी जांच नहीं की।


बहुत ही रोचक टिप्पणी। इसे साझा करने के लिए धन्यवाद!
Atmocreations

1
अच्छा है! सिर्फ एक सवाल: data_endएक सरणी, एक सूचक क्यों नहीं है? (या यह
मुहावरा

2
@xtofl, अगर data_endकोई पॉइंटर होगा तो कंपाइलर सोचेगा कि फाइल कंटेंट के बाद पॉइंटर स्टोर किया गया है। Similary, यदि आप dataएक पॉइंटर के प्रकार को बदल देंगे तो आपको पॉइंटर के बजाय इसकी फाइल के पहले बाइट्स से पॉइंटर मिल जाएगा। मुझे ऐसा लगता है।
साइमन

1
+1: आपका जवाब मुझे एक जावा क्लास लोडर और एक जार में एक कस्टम जावा लॉन्चर बनाने की अनुमति देता है
ऑबिन

2
@xtofl - यदि आप इसे एक पॉइंटर बनाने जा रहे हैं, तो इसे बनाएं const pointer। कंपाइलर आपको नॉन-कास्ट पॉइंटर्स का मान बदलने देता है, यह आपको एरे होने पर वैल्यू को बदलने नहीं देता है। तो यह सरणी सिंटैक्स का उपयोग करने के लिए शायद कम टाइपिंग है।
जेसी चिशोल्म

40

आप अपने सभी संसाधनों को एक ज़िप फ़ाइल में डाल सकते हैं और निष्पादन योग्य फ़ाइल के अंत में जोड़ सकते हैं :

g++ foo.c -o foo0
zip -r resources.zip resources/
cat foo0 resources.zip >foo

यह काम करता है, क्योंकि क) ज्यादातर निष्पादन योग्य छवि प्रारूप परवाह नहीं करते हैं अगर छवि के पीछे अतिरिक्त डेटा है और ख) ज़िप फ़ाइल के अंत में फ़ाइल हस्ताक्षर संग्रहीत करता है । इसका मतलब है, आपकी निष्पादन योग्य इसके बाद एक नियमित ज़िप फ़ाइल है (आपके अपफ्रंट एक्ज़ीक्यूटेबल को छोड़कर, जो ज़िप संभाल सकता है), जिसे लिबज़िप के साथ खोला और पढ़ा जा सकता है।


7
अगर मैं foo0 और resource.zip को foo में शामिल करना चाहता हूं, तो मुझे जरूरत है> अगर मैं बिल्ली के कमांड लाइन पर दोनों इनपुट देता हूं। (क्योंकि मैं पहले से ही foo में क्या चाहते हैं के लिए अपील नहीं करना चाहता)
नॉर्डिक मेनफ्रेम

1
आह हाँ, मेरी गलती है। मैं 0 वहाँ नाम में ठीक से मेरी पहली पढ़ने पर के माध्यम से स्थान नहीं था
Flexo

यह बहुत चालाक है। +1।
लाइनक्सियोस

1
+1 अद्भुत, विशेषकर जब miniz
mvp

यह एक अमान्य बाइनरी (कम से कम मैक और लिनक्स पर) का उत्पादन करेगा, जिसे जैसे उपकरण द्वारा संसाधित नहीं किया जा सकता है install_name_tool। इसके अलावा, बाइनरी अभी भी निष्पादन योग्य के रूप में काम करता है।
एंडी ली

36

से http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 :

मुझे हाल ही में एक निष्पादन योग्य में एक फ़ाइल एम्बेड करने की आवश्यकता थी। चूंकि मैं कमांड लाइन पर gcc, et al के साथ काम कर रहा हूं, न कि एक फैंसी RAD टूल के साथ जो यह सब जादुई तरीके से होता है, इसलिए यह तुरंत मेरे लिए स्पष्ट नहीं था कि यह कैसे किया जाए। नेट पर खोज करने वाले एक बिट ने एक हैक पाया जो अनिवार्य रूप से इसे निष्पादन योग्य के अंत में बिल्ली बना देता था और फिर यह समझ में नहीं आता कि यह जानकारी के एक समूह पर आधारित था जिसके बारे में मैं जानना नहीं चाहता था। लगता है जैसे वहाँ एक बेहतर तरीका होना चाहिए ...

और वहाँ है, यह बचाव के लिए objcopy है। objcopy एक प्रारूप से दूसरे में ऑब्जेक्ट फ़ाइलों या निष्पादन योग्य को रूपांतरित करता है। एक प्रारूप जो इसे समझता है वह "बाइनरी" है, जो मूल रूप से किसी भी फ़ाइल है जो कि उस अन्य स्वरूपों में से एक में नहीं है जिसे वह समझता है। तो आपने शायद इस विचार की कल्पना की है: उस फ़ाइल को रूपांतरित करें जिसे हम ऑब्जेक्ट फ़ाइल में एम्बेड करना चाहते हैं, फिर इसे बस हमारे बाकी कोड के साथ जोड़ा जा सकता है।

मान लें कि हमारे पास एक फ़ाइल नाम data.txt है जिसे हम अपने निष्पादन योग्य में एम्बेड करना चाहते हैं:

# cat data.txt
Hello world

इसे एक ऑब्जेक्ट फ़ाइल में परिवर्तित करने के लिए जिसे हम अपने प्रोग्राम से जोड़ सकते हैं, हम सिर्फ ".o" फ़ाइल का उत्पादन करने के लिए objcopy का उपयोग करते हैं।

# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o

यह objcopy बताता है कि हमारी इनपुट फ़ाइल "बाइनरी" प्रारूप में है, कि हमारी आउटपुट फाइल "elf32-i386" प्रारूप में होनी चाहिए (x86 पर ऑब्जेक्ट फाइलें)। --Binary- आर्किटेक्चर विकल्प objcopy को बताता है कि आउटपुट फ़ाइल x86 पर "रन" करने के लिए है। यह आवश्यक है ताकि ld फ़ाइल को x86 के लिए अन्य फ़ाइलों के साथ लिंक करने के लिए स्वीकार कर ले। किसी को लगता है कि आउटपुट प्रारूप को "elf32-i386" के रूप में निर्दिष्ट करने से इसका अर्थ होगा, लेकिन ऐसा नहीं है।

अब जब हमारे पास एक ऑब्जेक्ट फाइल है तो हमें केवल इसे शामिल करना होगा जब हम लिंकर को चलाते हैं:

# gcc main.c data.o

जब हम परिणाम चलाते हैं तो हमें आउटपुट के लिए प्रार्थना की जाती है:

# ./a.out
Hello world

बेशक, मैंने पूरी कहानी अभी तक नहीं बताई है, और न ही आपको मुख्य। जब objcopy उपरोक्त रूपांतरण करता है तो यह परिवर्तित ऑब्जेक्ट फ़ाइल में कुछ "लिंकर" प्रतीकों को जोड़ता है:

_binary_data_txt_start
_binary_data_txt_end

लिंक करने के बाद, ये प्रतीक एम्बेडेड फ़ाइल की शुरुआत और अंत निर्दिष्ट करते हैं। प्रतीक नाम द्विआधारी prepending और फ़ाइल नाम के लिए _start या _end द्वारा बनाए गए हैं । यदि फ़ाइल नाम में कोई भी वर्ण है जो प्रतीक नाम में अमान्य होगा तो वे अंडरस्कोर में बदल जाते हैं (जैसे data.txt data_txt हो जाता है)। यदि आप इन प्रतीकों का उपयोग करते समय लिंक किए गए नाम प्राप्त करते हैं, तो ऑब्जेक्ट फ़ाइल पर एक हेक्सडंप-सी करें और उन नामों के लिए डंप के अंत को देखें जो ऑबजॉपी ने चुने थे।

कोड वास्तव में एम्बेडेड फ़ाइल का उपयोग करने के लिए अब यथोचित स्पष्ट होना चाहिए:

#include <stdio.h>

extern char _binary_data_txt_start;
extern char _binary_data_txt_end;

main()
{
    char*  p = &_binary_data_txt_start;

    while ( p != &_binary_data_txt_end ) putchar(*p++);
}

एक महत्वपूर्ण और सूक्ष्म बात यह है कि ऑब्जेक्ट फ़ाइल में जोड़े गए प्रतीक "चर" नहीं हैं। उनके पास कोई डेटा नहीं है, बल्कि, उनका पता उनका मूल्य है। मैं उन्हें टाइप चार के रूप में घोषित करता हूं क्योंकि यह इस उदाहरण के लिए सुविधाजनक है: एम्बेडेड डेटा चरित्र डेटा है। हालाँकि, आप उन्हें कुछ भी घोषित कर सकते हैं, जैसे कि डेटा पूर्णांक का एक सरणी है, या यदि डेटा foo बार के किसी भी सरणी के रूप में संरचनात्मक foo_bar_t है। यदि एम्बेडेड डेटा एक समान नहीं है, तो संभवत: चार सबसे सुविधाजनक है: अपना पता लें और सूचक को उचित प्रकार से डालें जैसे कि आप डेटा को पार करते हैं।


36

यदि आप संसाधनों के सटीक प्रतीक के नाम और प्लेसमेंट पर नियंत्रण चाहते हैं, तो आप पूरी बाइनरी फ़ाइलों को आयात करने के लिए GNU असेंबलर (gcc का वास्तव में हिस्सा नहीं) का उपयोग (या स्क्रिप्ट) कर सकते हैं। इसे इस्तेमाल करे:

विधानसभा (x86 / बांह):

    .section .rodata

    .global thing
    .type   thing, @object
    .balign 4
thing:
    .incbin "meh.bin"
thing_end:

    .global thing_size
    .type   thing_size, @object
    .balign 4
thing_size:
    .int    thing_end - thing

सी:

#include <stdio.h>

extern const char thing[];
extern const unsigned thing_size;

int main() {
  printf("%p %u\n", thing, thing_size);
  return 0;
}

आप जो भी उपयोग करते हैं, वह संभवतः सभी संसाधनों को उत्पन्न करने के लिए एक स्क्रिप्ट बनाने के लिए सबसे अच्छा है, और सब कुछ के लिए अच्छा / एक समान प्रतीक नाम हैं।

आपके डेटा और सिस्टम की बारीकियों के आधार पर, आपको अलग-अलग संरेखण मूल्यों (अधिमानतः .balignपोर्टेबिलिटी के साथ ), या पूर्णांक प्रकार के विभिन्न प्रकारों के लिए thing_sizeया thing[]सरणी के लिए एक अलग तत्व प्रकार का उपयोग करने की आवश्यकता हो सकती है।


साझा करने के लिए धन्यवाद! निश्चित रूप से दिलचस्प लग रहा है, लेकिन इस बार ऐसा नहीं है जो मैं देख रहा हूँ =) का संबंध है
Atmocreations

1
ठीक वही जो मेरे द्वारा खोजा जा रहा था। हो सकता है कि आप यह सत्यापित कर सकें कि यह 4. के आकार की फाइलों के लिए ठीक नहीं है, जो अदृश्य नहीं है। ऐसा लगता है कि thing_size में अतिरिक्त पैडिंग बाइट शामिल होंगी।
पावेल पी

क्या होगा यदि मैं एक स्थानीय प्रतीक बनना चाहता हूं? मैं शायद अपनी खुद की विधानसभा के साथ संकलक उत्पादन को पूरा कर सकता हूं लेकिन क्या कोई बेहतर तरीका है?
user877329

रिकॉर्ड के लिए: मेरा संपादन अतिरिक्त पैडिंग बाइट्स के मुद्दे को स्वीकार करता है @ पावेल ने कहा।
ndim

4

यहाँ और इंटरनेट में सभी पोस्ट पढ़ने से मैंने एक निष्कर्ष निकाला है कि संसाधनों के लिए कोई उपकरण नहीं है, जो है:

1) कोड में उपयोग करने के लिए आसान है।

2) स्वचालित (cmake / मेक में आसान होना शामिल है)।

3) क्रॉस-प्लेटफॉर्म।

मैंने टूल लिखने का फैसला खुद से किया है। यहां कोड उपलब्ध है। https://github.com/orex/cpp_rsc

सेमीके साथ इसका उपयोग करना बहुत आसान है।

आपको अपने CMakeLists.txt फ़ाइल को इस तरह के कोड में जोड़ना चाहिए।

file(DOWNLOAD https://raw.github.com/orex/cpp_rsc/master/cmake/modules/cpp_resource.cmake ${CMAKE_BINARY_DIR}/cmake/modules/cpp_resource.cmake) 

set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake/modules)

include(cpp_resource)

find_resource_compiler()
add_resource(pt_rsc) #Add target pt_rsc
link_resource_file(pt_rsc FILE <file_name1> VARIABLE <variable_name1> [TEXT]) #Adds resource files
link_resource_file(pt_rsc FILE <file_name2> VARIABLE <variable_name2> [TEXT])

...

#Get file to link and "resource.h" folder
#Unfortunately it is not possible with CMake add custom target in add_executable files list.
get_property(RSC_CPP_FILE TARGET pt_rsc PROPERTY _AR_SRC_FILE)
get_property(RSC_H_DIR TARGET pt_rsc PROPERTY _AR_H_DIR)

add_executable(<your_executable> <your_source_files> ${RSC_CPP_FILE})

दृष्टिकोण का उपयोग करते हुए वास्तविक उदाहरण, यहां से डाउनलोड किया जा सकता है, https://bitbucket.org/orex/periodic_table


मुझे लगता है कि आपके उत्तर को अधिक लोगों के लिए उपयोगी बनने के लिए बेहतर स्पष्टीकरण की आवश्यकता है।
कीब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.