Building applications for an OpenWrt Target
There are several ways you can use an OpenWrt Software Development Kit (SDK) or Toolchain to compile your own applications outside of the OpenWrt buildroot :
- use prebuilt OpenWrt toolchain to build applications (ie from http://dev.gateworks.com/openwrt/latest/)
- use prebuilt OpenWrt SDK to build an OpenWrt package (ie from http://dev.gateworks.com/openwrt/latest/)
- use toolchain in your OpenWrt buildroot staging_dir tree (what you may need to do if your running your own firmware with a different lib/kernel config than Gateworks pre-built images)
The point of using the OpenWrt toolchain or SDK is that it can be pre-built and installed on a host that does not have the OpenWrt buildroot.
Note: Custom programming can be achieved through shell scripts on the Gateworks boards (most examples on the Gateworks wiki are shell commands). C code can be used when desired.
OpenWrt Prebuilt Toolchain
If you are using pre-built firmware from http://dev.gateworks.com/openwrt then you can use the pre-built toolchain there to build your own code.
The steps involved:
- Download prebuilt toolchain/SDK, the toolchain contains the compiler and standard libraries only.
- Uncompress it.
- Add it to your path (optional)
- Use the toolchain cross compiler to cross compile your code.
Examples:
- Example, if you wanted to build a hello-world for OpenWrt 14.08 for the laguna (cns3xxx) platform:
$ wget http://dev.gateworks.com/openwrt/14.08/cns3xxx/OpenWrt-Toolchain-cns3xxx-for-arm_v6k-gcc-4.6-linaro_uClibc-0.9.33.2_eabi.tar.bz2 $ tar xvf OpenWrt-Toolchain-cns3xxx-for-arm_v6k-gcc-4.6-linaro_uClibc-0.9.33.2_eabi.tar.bz2 $ PATH=$PWD/OpenWrt-Toolchain-cns3xxx-for-arm_v6k-gcc-linaro_uClibc-0.9.32_eabi/toolchain-arm_v6k_gcc-linaro_uClibc-0.9.32_eabi/bin:$PATH $ cat << EOF > hello-world.c #include <stdio.h> int main (int argc, char** argv) { printf("Hello World\n"); return 0; } EOF $ arm-openwrt-linux-uclibcgnueabi-gcc hello-world.c -o hello-world $ file hello-world hello-world: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
- Example OpenWrt 16.02 for the Ventana (imx6) platform:
# download toolchain wget http://dev.gateworks.com/openwrt/16.02/imx6/Gateworks-Toolchain-imx6_gcc-5.2.0_musl-1.1.12_eabi.Linux-x86_64.tar.bz2 # extract toolchain tar xvf Gateworks-Toolchain-imx6_gcc-5.2.0_musl-1.1.12_eabi.Linux-x86_64.tar.bz2 # add toolchain to path PATH=$PWD/Gateworks-Toolchain-imx6_gcc-5.2.0_musl-1.1.12_eabi.Linux-x86_64/toolchain-arm_cortex-a9+neon_gcc-5.2.0_musl-1.1.12_eabi/bin:$PATH # create a simple program cat << EOF > hello-world.c #include <stdio.h> int main (int argc, char** argv) { printf("Hello World\n"); return 0; } EOF # compile your program arm-openwrt-linux-gcc hello-world.c -o hello-world
Note: The prebuilt toolchain may have some issue if the script arm-openwrt-linux-uclibcgnueabi-wrapper.sh is used (with regard to paths having whitespace).
Venice Notes
Gateworks has not published a Toolchain / SDK and thus it should be built with the OpenWrt instructions on the OpenWrt/building page.
This results in a toolchain in ./staging_dir and for 24.01 imx8m the toolchain will be toolchain-aarch64_cortex-a53_gcc-12.3.0_musl
Once the toolchain is there, use the following commands to compile a Hello World:
# configure toolchain export TOOLCHAIN=toolchain-aarch64_cortex-a53_gcc-12.3.0_musl export STAGING_DIR=$PWD/staging_dir export ARCH=arm64 export CROSS_COMPILE=aarch64-openwrt-linux- export PATH=$PATH:$STAGING_DIR/$TOOLCHAIN/bin cat << EOF > helloworld.c #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { printf("hello world!\n"); return 0; } EOF ${CROSS_COMPILE}gcc helloworld.c -o helloworld
This builds a dynamically linked helloworld which you can inspect as:
$ file helloworld helloworld: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped ${CROSS_COMPILE}objdump -x helloworld | grep NEEDED NEEDED libgcc_s.so.1 NEEDED libc.so
You can then copy this executable and run this on a board with OpenWrt 24.01 installed
Ventana Notes
If desired, download the Ventana OpenWrt Toolchain/SDK and skip to step 4: https://www.gateworks.com/download/gateworks-openwrt-16-02_20160520-sdk-imx6-tar-bz2/
- Gateworks-OpenWrt-16.02_20160520-SDK-imx6.tar.bz2 (sha256sum:cb5868daeeafddd5032f0f0380654e5a736530aa1033ae9a8df3b54e77da2ea3)
- Gateworks-OpenWrt-16.02_20160520-Toolchain-imx6.tar.bz2 (sha256sum:7098d70fa5ab4d9be91d95261cd2d9552259043520711aa1f893bd8e4b4c9334)
- Turn on Toolchain in make menuconfig -> Build the OpenWrt based Toolchain
- Compile using make -j8 v=99 in trunk directory
- Find compiled toolchain in trunk/bin/imx6/OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi.tar.bz2
- Download the toolchain to a different directory on your PC where you will want to do some compiling, such as ~/compiling
- Extract the toolchain
ryan@Ryan:~/Documents/toolchain$ tar xvf OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi.tar.bz2 OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/ OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/LICENSE OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/toolchain-arm_cortex-a9+neon_gcc-4.6-linaro_uClibc-0.9.33.2_eabi/ OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/toolchain-arm_cortex-a9+neon_gcc-4.6-linaro_uClibc-0.9.33.2_eabi/lib64 OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/toolchain-arm_cortex-a9+neon_gcc-4.6-linaro_uClibc-0.9.33.2_eabi/share/ ...... .....
- Set PATH
ryan@Ryan:~/Documents/toolchain$ PATH=$PWD/OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/toolchain-arm_cortex-a9+neon_gcc-4.6-linaro_uClibc-0.9.33.2_eabi/bin:$PATH
- Create hello-world.c file as shown above,
- Compile hello-world.c. Note, when compiling, Gateworks uses hard float, so you may have to specify -mfloat-abi=hard as shown below
ryan@Ryan:~/Documents/toolchain$ arm-openwrt-linux-uclibcgnueabi-gcc -mfloat-abi=hard hello-world.c -o hello-world arm-openwrt-linux-uclibcgnueabi-gcc.bin: warning: environment variable 'STAGING_DIR' not defined arm-openwrt-linux-uclibcgnueabi-gcc.bin: warning: environment variable 'STAGING_DIR' not defined arm-openwrt-linux-uclibcgnueabi-gcc.bin: warning: environment variable 'STAGING_DIR' not defined /home/ryan/Documents/toolchain/OpenWrt-Toolchain-imx6-for-arm_cortex-a9+neon-gcc-4.6-linaro_uClibc-0.9.33.2_eabi/toolchain-arm_cortex-a9+neon_gcc-4.6-linaro_uClibc-0.9.33.2_eabi/bin/../lib/gcc/arm-openwrt-linux-uclibcgnueabi/4.6.4/../../../../arm-openwrt-linux-uclibcgnueabi/bin/ld: warning: .init_array section has zero size ryan@Ryan:~/Documents/toolchain$
- Verify hello-world has been compiled in current directory
ryan@Ryan:~/Documents/toolchain$ file hello-world hello-world: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
OpenWrt Prebuilt SDK
If you are using pre-built firmware from http://dev.gateworks.com/openwrt then you can use the pre-built SDK there to build OpenWrt packages. The SDK contains a cross toolchain, libraries, and the Makefile structure to be able to build OpenWrt packages without having an entire buildroot tree. Note that you must also have the OpenWrt Toolchain (see below) in your path. For example to build the OpenWrt nuttcp package for laguna (cns3xxx):
$ wget http://dev.gateworks.com/openwrt/latest/cns3xxx/OpenWrt-SDK-cns3xxx-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2.tar.bz2 $ tar xvf OpenWrt-SDK-cns3xxx-for-Linux-i686-gcc-linaro_uClibc-0.9.32.tar.bz2 $ PATH=$PWD/OpenWrt-SDK-imx61-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2/staging_dir/toolchain-arm_v7-a_gcc-4.6-linaro_uClibc-0.9.33.2_eabi/bin:$PATH $ cd OpenWrt-SDK-imx61-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2 $ svn export svn://svn.openwrt.org/openwrt/packages/net/nuttcp package/nuttcp $ make package/nuttcp/compile $ ls bin/cns3xxx/packages/ nuttcp_6.1.2-2_cns3xxx.ipk nuttcp-xinetd_6.1.2-2_cns3xxx.ipk
GDB - GNU Debugger
GDB can be used on Gateworks SBC's.
Gateworks has tested GDB in the following way on OpenWrt:
- Compile GDB as a package using the make menuconfig option. Be sure it is GDB and not GDB server (example shows GDB server): OpenWrt/Configuration
- Install the ipk file on the target board: ipkupload
- Compile your program using the toolchain described above but with GDB support. Example is hello-world.
arm-openwrt-linux-uclibcgnueabi-gcc -g -mfloat-abi=hard hello-world.c -o hello-world
- Copy program to target board and be sure it is executable (chmod)
- Start gdb on target board (Ventana)
- Run the gdb command from the Ventana command prompt:
(gdb) file hello-world Load new symbol table from "/hello-world"? (y or n) y Reading symbols from /hello-world...done.
- Then type the run command:
(gdb) run Starting program: /hello-world warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. warning: no loadable sections found in added symbol-file /lib/libgcc_s.so.1 warning: no loadable sections found in added symbol-file /lib/libc.so.0 Hello World [Inferior 1 (process 17906) exited normally]
- For more GDB reference please see this page: GDB Documentation and other examples on Google search.
Remote Debugging
A brief example:
- From the target start the server using the command to use over TCP on port 2345 the application hello-world
root@OpenWrt:/# gdbserver host:2345 hello-world
Example Continues to step 2 below, shown below is the rest of the output:
root@OpenWrt:/# gdbserver host:2345 hello-world Process hello-world created; pid = 21696 Listening on port 2345 Remote debugging from host 192.168.1.22 Hello World Child exited with status 0 GDBserver exiting root@OpenWrt:/#
- From the host start the GDB from the SDK (Ventana example shown)
ryan@Ryan:~/sdk_ventana/OpenWrt-SDK-imx6-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2$ ./staging_dir/toolchain-arm_cortex-a9+neon_gcc-4.8-linaro_uClibc-0.9.33.2_eabi/bin/arm-openwrt-linux-uclibcgnueabi-gdb
- Attach to target:
(gdb) target remote 192.168.1.1:2345 Remote debugging using 192.168.1.1:2345 0x76ff1e38 in ?? () (gdb)
- Use the continue command and notice the program run on the target as shown in step 1
(gdb) continue Continuing. Cannot access memory at address 0x0 [Inferior 1 (process 23862) exited normally] (gdb)
Using Toolchain from staging_dir
If you are running firmware from your own OpenWrt build directory you can use the toolchain provided there. The OpenWrt toolchain is placed in staging_dir under a directory name appropriate for your target configuration (toolchain-<arch-info>) and you can use it there to build applications for your target directly without using the buildroot:
- laguna - staging_dir/toolchain-arm_v6k_gcc-linaro_uClibc-0.9.32_eabi
Examples:
- Cross-compile hello-world.c for Ventana (imx6) using the OpenWrt 16.02, built in the present working directory:
export STAGING_DIR=$PWD/staging_dir export PATH=$STAGING_DIR/toolchain-arm_cortex-a9+neon_gcc-5.2.0_musl-1.1.12_eabi/bin:$PATH arm-openwrt-linux-gcc hello-world.c -o hello-world
- Cross-compile hello-world.c for Laguna (cns3xxx) using the OpenWrt 14.08, built in the present working directory:
export STAGING_DIR=$PWD/staging_dir export PATH=$STAGING_DIR/toolchain-arm_v6k_gcc-linaro_uClibc-0.9.32_eabi/bin:$PATH arm-openwrt-linux-uclibcgnueabi-gcc hello-world.c -o hello-world
Other Notes
- uclibc is missing certain things that glibc on your desktop has - There really isn't a clear list of what those items are.
- uclibc isn't your only libc option - OpenWrt has support for other popular alternatives to glibc as well (make menuconfig, Advanced configuration options -> Toolchain Options -> C Library implementation).
- Current OpenWrt (ie using our trunk BSP) supports uClibc (default) as well as eglibc and musl.
- Note that eglibc is extremely popular and I believe Ubuntu has even switched to using it.
- If you are missing features in the c++ lib then you can instead/also choose an alternative to uClibc++ via menuconfig 'Global build settings -> Preferred standard C++ library.
- In the toolchain/bin directory, there is a gcc-uc script which sets everything up for uclibc++.
- c++ exceptions have special configuration as noted here http://lists.uclibc.org/pipermail/uclibc-cvs/2012-May/030326.html
- Also, if you want to use compiler tools such as 'nm' you need to use the ones from the cross toolchain (ie arm-openwrt-linux-nm)
- The STAGING_DIR may need to be set as noted here: http://wiki.openwrt.org/doc/devel/crosscompile
Additional Resources
OpenWrt
OpenWrt is a open source software. There are many OpenWrt resources around the web.
Open WRT Wiki for Cross Compile: http://wiki.openwrt.org/doc/devel/crosscompile
OpenWrt Buildroot : http://wiki.openwrt.org/doc/howto/buildroot.exigence
OpenWrt Creating Packages : http://wiki.openwrt.org/doc/devel/packages
Other C code examples:
A userspace daemon has been written by Gateworks, source code is available
http://trac.gateworks.com/wiki/gsc#GSCuserspaceDaemon
Another small sample C program to read through input registers such as temp and voltage on the Gateworks GSC http://trac.gateworks.com/wiki/gsc#SystemTemperatureandVoltageMonitor
Steps:
- Write the C code as a .c file such as input.c
- Compile the C code with the instructions above
- Transfer the resulting executable over to the Gateworks board. One way to do this is through a wget command on the Gateworks board if the executable is hosted on a web server.
- Run the executable by chmod 777 if needed, and the type at the command line ./input
#include <stdio.h> #include <string.h> /******************************* * * get value of inputs on Gateworks Boards * **********************************/ int getValue(char *file) { FILE *fp; int val=0; fp = fopen(file,"r"); if ( fp == NULL) { printf("Bad open\n"); exit(0); } while(!feof(fp)) { if(fscanf(fp,"%d", &val)!=1) { break; } else { printf("\nVal=%d\n\n",val); } } fclose(fp); return val; } /******************************* * * get name of inputs on Gateworks Boards * **********************************/ const char * getName(char *file) { FILE *fp; char label[100]; fp = fopen(file,"r"); if ( fp == NULL) { printf("Bad open\n"); exit(0); } while(!feof(fp)) { if(fscanf(fp,"%s", &label)!=1) { break; } else { printf("\n\nLabel=%s",label); } } fclose(fp); } /******************************* * * dispatch name and value request Gateworks Boards * **********************************/ void getNameValuePair(char *file) { char filelabel[360]; strcpy(filelabel,file); strcat (filelabel,"_label"); getName(filelabel); strcat (file,"_input"); getValue(file); } /***************************** * * MAIN read inputs such as voltage * *****************************/ int main (int argc, char** argv) { int i=0; char str[360]; char ival[2]; printf("About to read values...\n"); for(i=0;i<13;i++) { strcpy (str," "); strcpy (str,"/sys/class/hwmon/hwmon0/device/in"); sprintf(ival,"%d",i); strcat (str,ival); getNameValuePair(str); } return 0; }
The output once ran on the target Gateworks board looks like:
root@OpenWrt:/# ./input About to read values... Label=vin Val=23852 Label=3p3 Val=3295 Label=bat Val=3037 Label=5p0 Val=4983 Label=core Val=1257 Label=cpu1 Val=16777215 Label=cpu2 Val=16777215 Label=dram Val=1837 Label=ext_bat Val=16777215 Label=io1 Val=2505 Label=io2 Val=16777215 Label=pci2 Val=1517 Label=current Val=775 root@OpenWrt:/#