...
To keep things manageable, let's use a concrete example Suppose the ELF program that we wish to add to the release code is the since source file hello.c
:
Code Block |
---|
#include <stdio.h>
|
Code Block |
int main(int argc, char **argv) { printf("Hello from Add-On Program!\n"); return 0; } |
Let's say that we have a a directory called addon
and contains the hello.c
source file, a Makefile
that will create the the ELF program, and a Bash script called mkdefines.sh
that will create the a linker script.
...
This is the Makefile that I used to create ELF program:
Code Block |
---|
include nuttx-export-7.25/build/Make.defs
|
Code Block |
# Long calls are need to call from RAM into FLASH
|
Code Block |
ARCHCFLAGS += -mlong-calls ARCHWARNINGS = -Wall -Wstrict-prototypes -Wshadow -Wundef ARCHOPTIMIZATION = -Os -fno-strict-aliasing -fno-strength-reduce -fomit-frame-pointer ARCHINCLUDES = -I. -isystem nuttx-export-7.25/include |
Code Block |
CFLAGS = $(ARCHCFLAGS) $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHINCLUDES) -pipe
|
Code Block |
CROSSDEV = arm-none-eabi- CC = $(CROSSDEV)gcc LD = $(CROSSDEV)ld STRIP = $(CROSSDEV)strip --strip-unneeded |
Code Block |
# Setup up linker command line options
|
Code Block |
LDRELFLAGS = -r
|
Code Block |
LDELFFLAGS = -r -e main LDELFFLAGS += -T defines.ld -T gnu-elf.ld |
Code Block |
# This might change in a different environment |
Code Block |
OBJEXT ?= .o
|
Code Block |
# This is the generated ELF program
|
Code Block |
BIN = hello REL = hello.r |
Code Block |
# These are the sources files that we use
|
Code Block |
SRCS = hello.c OBJS = $(SRCS:.c=$(OBJEXT)) |
Code Block |
# Build targets
|
Code Block |
all: $(BIN) .PHONY: clean |
Code Block |
$(OBJS): %$(OBJEXT): %.c $(CC) -c $(CFLAGS) -o $@ $< |
Code Block |
System.map: nuttx-export-7.25/System.map cat nuttx-export-7.25/System.map | sed -e "s/\r//g" >System.map |
Code Block |
$(REL): $(OBJS) $(LD) $(LDRELFLAGS) -o $@ $< |
Code Block |
defines.ld: System.map $(REL) ./mkdefines.sh System.map "$(REL)" >defines.ld |
Code Block |
$(BIN): defines.ld $(REL) $(LD) $(LDELFFLAGS) -o $@ $(REL) $(STRIP) $(REL) |
Code Block |
clean: rm -f $(BIN) rm -f $(REL) rm -f defines.ld rm -f System.map rm -f *.o |
The Linker Script
...
The main linker script that I am using in this example, gnu-elf.ld
, contains the following:
Code Block |
---|
SECTIONS { .text 0x00000000 : { _stext = . ; *(.text) |
...
*(.text.*) |
...
*(.gnu.warning) |
...
*(.stub) |
...
*(.glue_7) |
...
*(.glue_7t) |
...
*(.jcr) |
...
Code Block |
---|
_etext = . ; } |
Code Block |
.rodata : { _srodata = . ; *(.rodata) |
...
*(.rodata1) |
...
*(.rodata.*) |
...
*(.gnu.linkonce.r*) |
...
Code Block |
---|
_erodata = . ; } |
Code Block |
.data : { _sdata = . ; |
...
*(.data) *(.data1) |
...
*(.data.*) |
...
*(.gnu.linkonce.d*) |
...
Code Block |
---|
_edata = . ; } |
Code Block |
.bss : { _sbss = . ; *(.bss) |
...
*(.bss |
...
.*) *(.sbss) |
...
*(.sbss.*) |
...
*(.gnu.linkonce.b*) |
...
*(COMMON) |
...
Code Block |
---|
_ebss = . ; } |
Code Block |
/* Stabs debugging sections. */ |
Code Block |
.stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_info 0 : { *(.debug_info) } .debug_line 0 : { *(.debug_line) } .debug_pubnames 0 : { *(.debug_pubnames) } .debug_aranges 0 : { *(.debug_aranges) } } |
Creating the defined.ld Linker Script
...
Here is the version of mkdefines.sh
that I used in this demo:
Code Block |
---|
#!/bin/bash
|
Code Block |
usage="Usage: $0 <system-map> <relprog>"
|
Code Block |
# Check for the required path to the System.map file
|
Code Block |
sysmap=$1 if [ -z "$sysmap" ]; then echo "ERROR: Missing <system-map>" echo "" echo $usage exit 1 fi |
Code Block |
# Check for the required partially linked file
|
Code Block |
relprog=$2 if [ -z "$relprog" ]; then echo "ERROR: Missing <program-list>" echo "" echo $usage exit 1 fi |
Code Block |
# Verify the System.map and the partially linked file
|
Code Block |
if [ ! -r "$sysmap" ]; then echo "ERROR: $sysmap does not exist" echo "" echo $usage exit 1 fi |
Code Block |
if [ ! -r "$relprog" ]; then echo "ERROR: $relprog does not exist" echo "" echo $usage exit 1 fi |
Code Block |
# Extract all of the undefined symbols from the partially linked file and create a # list of sorted, unique undefined variable names. |
Code Block |
varlist=`nm $relprog | fgrep ' U ' | sed -e "s/^[ ]*//g" | cut -d' ' -f2 | sort - | uniq`
|
Code Block |
# Now output the linker script that provides a value for all of the undefined symbols
|
Code Block |
for var in $varlist; do map=`grep " ${var}$" ${sysmap}` if [ -z "$map" ]; then echo "ERROR: Variable $var not found in $sysmap" echo "" echo $usage exit 1 fi |
Code Block |
varaddr=`echo ${map} | cut -d' ' -f1` echo "${var} = 0x${varaddr} | 0x00000001;" done |
This script basically just uses the nm
utility to find all of the undefined symbols in the ELF object. Then it searches for the address of each undefined symbol in the System.map
that was created when the released firmware was created. Finally, it uses the symbol name and the symbol address to create each symbol table entry.
...