C / C ++. How to use embedded application resources when working in GCC on Linux

I wanted to somehow use Linux embedded resources, moreover, automatically. In general, the task is this:







  1. There is an Eclipse project program in C ++.
  2. OS: Linux Ubuntu. Compiler: G ++
  3. The project uses data from external files: localization strings, SQL queries, pictures, sounds, etc.
  4. All resources must be embedded in the executable file, because the program is planned to be distributed as portable.
  5. In addition, I want the process to be as automated as possible, because laziness.


To begin with, a search on the forums provided several possible ways to solve the problem. Among those found to be the most universal, the idea seemed to me to use the “ --format=binary



” parameter of the linker “ ld



”. Forum posts promised a team of the form:







 g++ -Wl,--format=binary -Wl,my.res -Wl,--format=default
      
      





will link the file “my.res” to the application and create two characters - _binary_my_res_start



and _binary_my_res_end



, indicating, respectively, the beginning and end of the same data that was in the linked file. Therefore, accessing data from C ++ could be done something like this:

 extern const uint8_t my_res_start[] asm("_binary_my_res_start"); extern const uint8_t my_res_end[] asm("_binary_my_res_end");
      
      





But it was not there. We write everything as it should, and the compiler is not happy. The symbol «_binary_my_res_start»



, you see, cannot be found. Well, nothing, nm



to help us. We write the following command:







 nm MyProgramm |grep -w -o -P -e '_binary_[\w\d_]+'
      
      





And we get:



 _binary__home_unknown_workspace_MyProgramm_res_my_res_sql_end _binary__home_unknown_workspace_MyProgramm_res_my_res_sql_start
      
      





It turns out that the name of the symbol includes the entire path to it, which, in the future, may lead to the need for constant rewriting of the header file containing links to resources. The problem is solved if the following script is added to the PostBuild event in the Eclipse project settings:







 #!/bin/bash OUTPUT=$1/resources.h printf '#ifndef __RESOURCES_H__\n' > "$OUTPUT" printf '#define __RESOURCES_H__\n\n' >> "$OUTPUT" printf '#include <inttypes.h>\n\n' >> "$OUTPUT" SYMBOLS=$(nm NewsParser |grep -w -o -P -e '_binary_[\w\d_]+') >> "$OUTPUT" VAR_SIZES_LIST='' for SYMBOL in $SYMBOLS do VAR_NAME=$(echo $SYMBOL | grep -o -P -e 'res_[\w\d_]+'|cut -c 5-) if [[ -z $(echo $SYMBOL|grep _size) ]] then printf '\textern const uint8_t '$VAR_NAME'[]\tasm("'$SYMBOL'");\n\n' >> "$OUTPUT" else START_VAR=$(echo $VAR_NAME|rev|cut -c 5-|rev)'start' END_VAR=$(echo $VAR_NAME|rev|cut -c 5-|rev)'end' VAR_SIZES_LIST=$VAR_SIZES_LIST$(printf '\\tconst uint64_t '$VAR_NAME'\\t=\\t'$END_VAR' - '$START_VAR';\\n\\n') fi done printf "$VAR_SIZES_LIST" >> "$OUTPUT" printf '#endif\n' >> "$OUTPUT" printf 'File '$OUTPUT' is generated.\n'
      
      





How to add the script “update_resource.sh”, which is in the root of the project, to the PostBuild event in the Eclipse project settings.




Good. Now the header file will be like new each time, and you can access the data by the names of variables that will not change unless you rename the resource file itself. In addition, this script calculates the size for each resource. Not only taking the pointer to the beginning from the end pointer was a big problem, but, nevertheless, it’s more convenient.



But this, for now, is not all. After all, adding each new resource to a project will turn into a shaped AD. And this problem can also be solved using a script, only at the linking stage:







 FLAGS=$1 OUTPUT_FLAG=$2 OUTPUT_PREFIX=$3 OUTPUT=$4 INPUTS=$5 RESOURCE_PATH=$6 RESOURCES='' for res_file in $(ls $RESOURCE_PATH/*) do RESOURCES=$RESOURCES' '-Wl,$res_file echo ' '$res_file'   ' done g++ $FLAGS $OUTPUT_FLAG $OUTPUT_PREFIX$OUTPUT $INPUTS -Wl,--format=binary $RESOURCES -Wl,--format=default
      
      





How to replace the call of the standard linker with a custom script in the Eclipse project settings.




  • The place in red is marked in the picture, where instead of the standard command to call the linker, the path to the script “link.sh” is located at the root of the project.
  • Green in the picture is a place in which another linker is added to the usual linker parameters, which tells the script the location of the directory with resources.
  • In addition , it is important not to forget to wrap the remaining parameters with double quotes so that they do not accidentally break with spaces in the wrong order in which the script is waiting for them.




Fine. Now all the files that are in the "res" subdirectory will themselves fall into the resources during each assembly.




All Articles